]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/playerstats.qc
Begin working on cleaning up playerstats code
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / playerstats.qc
1 #ifdef SVQC
2 void PlayerStats_Prematch
3
4 float playerstats_db;
5 string teamstats_last;
6 string playerstats_last;
7 string events_last;
8 .float playerstats_addedglobalinfo;
9 .string playerstats_id;
10
11 void PlayerStats_GameReport_AddPlayer(entity e)
12 {
13         string s;
14
15         if(playerstats_db < 0) { return; }
16         if(e.playerstats_id) { return; }
17
18         s = string_null;
19         if(e.crypto_idfp != "" && e.cvar_cl_allow_uidtracking == 1)
20                 { s = e.crypto_idfp; }
21         else if(IS_BOT_CLIENT(e))
22                 { s = sprintf("bot#%g#%s", skill, e.cleanname); }
23
24         if((s == "") || find(world, playerstats_id, s)) // already have one of the ID - next one can't be tracked then!
25         {
26                 if(IS_BOT_CLIENT(e))
27                         { s = sprintf("bot#%d", e.playerid); }
28                 else
29                         { s = sprintf("player#%d", e.playerid); }
30         }
31
32         e.playerstats_id = strzone(s);
33
34         string key = sprintf("%s:*", e.playerstats_id);
35         string p = db_get(playerstats_db, key);
36         
37         if(p == "")
38         {
39                 if(playerstats_last)
40                 {
41                         db_put(playerstats_db, key, playerstats_last);
42                         strunzone(playerstats_last);
43                 }
44                 else { db_put(playerstats_db, key, "#"); }
45                 playerstats_last = strzone(e.playerstats_id);
46         }
47 }
48
49 void PlayerStats_GameReport_AddTeam(float t)
50 {
51         if(playerstats_db < 0) { return; }
52
53         string key = sprintf("%d", t);
54         string p = db_get(playerstats_db, key);
55         
56         if(p == "")
57         {
58                 if(teamstats_last)
59                 {
60                         db_put(playerstats_db, key, teamstats_last);
61                         strunzone(teamstats_last);
62                 }
63                 else { db_put(playerstats_db, key, "#"); }
64                 teamstats_last = strzone(key);
65         }
66 }
67
68 void PlayerStats_GameReport_AddEvent(string event_id)
69 {
70         if(playerstats_db < 0) { return; }
71
72         string key = sprintf("*:%s", event_id);
73         string p = db_get(playerstats_db, key);
74         
75         if(p == "")
76         {
77                 if(events_last)
78                 {
79                         db_put(playerstats_db, key, events_last);
80                         strunzone(events_last);
81                 }
82                 else { db_put(playerstats_db, key, "#"); }
83                 events_last = strzone(event_id);
84         }
85 }
86
87 float PlayerStats_GameReport_Event(entity e, string event_id, float value)
88 {
89         if((e.playerstats_id == "") || playerstats_db < 0) { return 0; }
90
91         string key = sprintf("%s:%s", e.playerstats_id, event_id);
92         float val = stof(db_get(playerstats_db, key));
93         val += value;
94         db_put(playerstats_db, key, ftos(val));
95         return val;
96 }
97
98 float PlayerStats_GameReport_TeamScore(float t, string event_id, float value)
99 {
100         if(playerstats_db < 0) { return 0; }
101
102         string key = sprintf("team#%d:%s", t, event_id);
103         float val = stof(db_get(playerstats_db, key));
104         val += value;
105         db_put(playerstats_db, key, ftos(val));
106         return val;
107 }
108
109 void PlayerStats_GameReport_Accuracy(entity p)
110 {
111     entity w;
112     float i;
113
114         #define PAC p.accuracy
115     for(i = WEP_FIRST; i <= WEP_LAST; ++i)
116     {
117         w = get_weaponinfo(i);
118         PlayerStats_Event(p, strcat("acc-", w.netname, "-hit"), PAC.(accuracy_hit[i-1]));
119         PlayerStats_Event(p, strcat("acc-", w.netname, "-fired"), PAC.(accuracy_fired[i-1]));
120         PlayerStats_Event(p, strcat("acc-", w.netname, "-cnt-hit"), PAC.(accuracy_cnt_hit[i-1]));
121         PlayerStats_Event(p, strcat("acc-", w.netname, "-cnt-fired"), PAC.(accuracy_cnt_fired[i-1]));
122         PlayerStats_Event(p, strcat("acc-", w.netname, "-frags"), PAC.(accuracy_frags[i-1]));
123     }
124     #undef PAC
125 }
126
127 void PlayerStats_GameReport_AddGlobalInfo(entity p)
128 {
129         if((p.playerstats_id == "") || playerstats_db < 0) { return; }
130         p.playerstats_addedglobalinfo = TRUE; // todo: move to end?
131
132         // add global info!
133         if(p.alivetime)
134         {
135                 PlayerStats_Event(p, PLAYERSTATS_ALIVETIME, time - p.alivetime);
136                 p.alivetime = 0;
137         }
138
139         db_put(playerstats_db, sprintf("%s:_playerid", p.playerstats_id), ftos(p.playerid));
140
141         if(p.cvar_cl_allow_uid2name == 1 || IS_BOT_CLIENT(p))
142                 db_put(playerstats_db, sprintf("%s:_netname", p.playerstats_id), p.netname);
143
144         if(teamplay)
145                 db_put(playerstats_db, sprintf("%s:_team", p.playerstats_id), ftos(p.team));
146
147         if(stof(db_get(playerstats_db, sprintf("%d:%s", p.playerstats_id, PLAYERSTATS_ALIVETIME))) > 0)
148                 PlayerStats_Event(p, PLAYERSTATS_JOINS, 1);
149
150         PlayerStats_Accuracy(p);
151
152         if(IS_REAL_CLIENT(p))
153         {
154                 if(p.latency_cnt)
155                 {
156                         float latency = (p.latency_sum / p.latency_cnt);
157                         if(latency) { PlayerStats_Event(p, PLAYERSTATS_AVGLATENCY, latency); }
158                 }
159         }
160
161         strunzone(p.playerstats_id);
162         p.playerstats_id = string_null;
163 }
164
165 .float scoreboard_pos;
166 void PlayerStats_GameReport_EndMatch(float finished)
167 {
168         entity p;
169         PlayerScore_Sort(score_dummyfield, 0, 0, 0);
170         PlayerScore_Sort(scoreboard_pos, 1, 1, 1);
171         if(teamplay) { PlayerScore_TeamStats(); }
172         FOR_EACH_CLIENT(p)
173         {
174                 // add personal score rank
175                 PlayerStats_Event(p, PLAYERSTATS_RANK, p.score_dummyfield);
176
177                 if(!p.scoreboard_pos) { continue; }
178
179                 // scoreboard is valid!
180                 PlayerStats_Event(p, PLAYERSTATS_SCOREBOARD_VALID, 1);
181
182                 // add scoreboard position
183                 PlayerStats_Event(p, PLAYERSTATS_SCOREBOARD_POS, p.scoreboard_pos);
184
185                 // add scoreboard data
186                 PlayerScore_PlayerStats(p);
187
188                 // if the match ended normally, add winning info
189                 if(finished)
190                 {
191                         PlayerStats_Event(p, PLAYERSTATS_WINS, p.winning);
192                         PlayerStats_Event(p, PLAYERSTATS_MATCHES, 1);
193                 }
194         }
195 }
196
197 void PlayerStats_GameReport_Init() // initiated before InitGameplayMode so that scores are added properly
198 {
199         string uri;
200         playerstats_db = -1;
201         playerstats_waitforme = TRUE;
202         uri = autocvar_g_playerstats_uri;
203         if(uri == "")
204                 return;
205         playerstats_db = db_create();
206         if(playerstats_db >= 0)
207                 playerstats_waitforme = FALSE; // must wait for it at match end
208
209         serverflags |= SERVERFLAG_PLAYERSTATS;
210
211         PlayerStats_AddEvent(PLAYERSTATS_ALIVETIME);
212         PlayerStats_AddEvent(PLAYERSTATS_AVGLATENCY);
213         PlayerStats_AddEvent(PLAYERSTATS_WINS);
214         PlayerStats_AddEvent(PLAYERSTATS_MATCHES);
215         PlayerStats_AddEvent(PLAYERSTATS_JOINS);
216         PlayerStats_AddEvent(PLAYERSTATS_SCOREBOARD_VALID);
217         PlayerStats_AddEvent(PLAYERSTATS_SCOREBOARD_POS);
218         PlayerStats_AddEvent(PLAYERSTATS_RANK);
219
220     // accuracy stats
221     entity w;
222     float i;
223     for(i = WEP_FIRST; i <= WEP_LAST; ++i)
224     {
225         w = get_weaponinfo(i);
226         PlayerStats_AddEvent(strcat("acc-", w.netname, "-hit"));
227         PlayerStats_AddEvent(strcat("acc-", w.netname, "-fired"));
228         PlayerStats_AddEvent(strcat("acc-", w.netname, "-cnt-hit"));
229         PlayerStats_AddEvent(strcat("acc-", w.netname, "-cnt-fired"));
230         PlayerStats_AddEvent(strcat("acc-", w.netname, "-frags"));
231     }
232
233         PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_3);
234         PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_5);
235         PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_10);
236         PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_15);
237         PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_20);
238         PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_25);
239         PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_30);
240         PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_BOTLIKE);
241         PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD);
242         PlayerStats_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM);
243 }
244
245 void PlayerStats_GameReport_Shutdown()
246 {
247         string uri;
248
249         if(playerstats_db < 0) { return; }
250
251         uri = autocvar_g_playerstats_uri;
252         if(uri != "")
253         {
254                 playerstats_waitforme = FALSE;
255                 url_multi_fopen(uri, FILE_APPEND, PlayerStats_GameReport_Handler, world);
256         }
257         else
258         {
259                 playerstats_waitforme = TRUE;
260                 db_close(playerstats_db);
261                 playerstats_db = -1;
262         }
263 }
264
265 void PlayerStats_GameReport_Handler(entity fh, entity pass, float status)
266 {
267         string t, tn;
268         string p, pn;
269         string e, en;
270         string nn, tt;
271         string s;
272
273         switch(status)
274         {
275                 // ======================================
276                 // -- OUTGOING GAME REPORT INFORMATION --
277                 // ======================================
278                 /* SPECIFICATIONS:
279                  * V: format version (always a fixed number) - this MUST be the first line!
280                  * #: comment (MUST be ignored by any parser)
281                  * R: release information on the server
282                  * G: game type
283                  * O: mod name (icon request) as in server browser
284                  * M: map name
285                  * I: match ID (see "matchid" in g_world.qc
286                  * S: "hostname" of the server
287                  * C: number of "unpure" cvar changes
288                  * U: UDP port number of the server
289                  * D: duration of the match
290                  * P: player ID of an existing player; this also sets the owner for all following "n", "e" and "t" lines (lower case!)
291                  * Q: team number of an existing team (format: team#NN); this also sets the owner for all following "e" lines (lower case!)
292                  * n: nickname of the player (optional)
293                  * t: team ID
294                  * i: player index
295                  * e: followed by an event name, a space, and the event count/score
296                  *  event names can be:
297                  *   alivetime: total playing time of the player
298                  *   avglatency: average network latency compounded throughout the match
299                  *   wins: number of games won (can only be set if matches is set)
300                  *   matches: number of matches played to the end (not aborted by map switch)
301                  *   joins: number of matches joined (always 1 unless player never played during the match)
302                  *   scoreboardvalid: set to 1 if the player was there at the end of the match
303                  *   total-<scoreboardname>: total score of that scoreboard item
304                  *   scoreboard-<scoreboardname>: end-of-game score of that scoreboard item (can differ in non-team games)
305                  *   achievement-<achievementname>: achievement counters (their "count" is usually 1 if nonzero at all)
306                  *   kills-<index>: number of kills against the indexed player
307                  *   rank <number>: rank of player
308                  *   acc-<weapon netname>-hit: total damage dealt
309                  *   acc-<weapon netname>-fired: total damage that all fired projectiles *could* have dealt
310                  *   acc-<weapon netname>-cnt-hit: amount of shots that actually hit
311                  *   acc-<weapon netname>-cnt-fired: amount of fired shots
312                  *   acc-<weapon netname>-frags: amount of frags dealt by weapon
313                  */
314                 case URL_READY_CANWRITE:
315                 {
316                         url_fputs(fh, "V 8\n");
317                         #ifdef WATERMARK
318                         url_fputs(fh, sprintf("R %s\n", WATERMARK));
319                         #endif
320                         url_fputs(fh, sprintf("G %s\n", GetGametype()));
321                         url_fputs(fh, sprintf("O %s\n", modname));
322                         url_fputs(fh, sprintf("M %s\n", GetMapname()));
323                         url_fputs(fh, sprintf("I %s\n", matchid));
324                         url_fputs(fh, sprintf("S %s\n", cvar_string("hostname")));
325                         url_fputs(fh, sprintf("C %d\n", cvar_purechanges_count));
326                         url_fputs(fh, sprintf("U %d\n", cvar("port")));
327                         url_fputs(fh, sprintf("D %f\n", max(0, time - game_starttime)));
328                         if(teamplay)
329                         {
330                                 for(t = teamstats_last; (tn = db_get(playerstats_db, sprintf("%d", stof(t)))) != ""; t = tn)
331                                 {
332                                         url_fputs(fh, sprintf("Q team#%s\n", t));
333                                         for(e = events_last; (en = db_get(playerstats_db, sprintf("*:%s", e))) != ""; e = en)
334                                         {
335                                                 float v;
336                                                 v = stof(db_get(playerstats_db, sprintf("team#%d:%s", stof(t), e)));
337                                                 if(v != 0)
338                                                         url_fputs(fh, sprintf("e %s %g\n", e, v));
339                                         }
340                                 }
341                         }
342                         for(p = playerstats_last; (pn = db_get(playerstats_db, sprintf("%s:*", p))) != ""; p = pn)
343                         {
344                                 url_fputs(fh, sprintf("P %s\n", p));
345                                 nn = db_get(playerstats_db, sprintf("%s:_playerid", p));
346                                 if(nn != "")
347                                         url_fputs(fh, sprintf("i %s\n", nn));
348                                 nn = db_get(playerstats_db, sprintf("%s:_netname", p));
349                                 if(nn != "")
350                                         url_fputs(fh, sprintf("n %s\n", nn));
351                                 if(teamplay)
352                                 {
353                                         tt = db_get(playerstats_db, sprintf("%s:_team", p));
354                                         url_fputs(fh, sprintf("t %s\n", tt));
355                                 }
356                                 for(e = events_last; (en = db_get(playerstats_db, sprintf("*:%s", e))) != ""; e = en)
357                                 {
358                                         float v;
359                                         v = stof(db_get(playerstats_db, sprintf("%s:%s", p, e)));
360                                         if(v != 0)
361                                                 url_fputs(fh, sprintf("e %s %g\n", e, v));
362                                 }
363                         }
364                         url_fputs(fh, "\n");
365                         url_fclose(fh);
366                         break;
367                 }
368
369                 // ======================================
370                 // -- INCOMING GAME REPORT INFORMATION --
371                 // ======================================
372                 /* SPECIFICATIONS:
373                  * stuff
374                  */
375                 case URL_READY_CANREAD:
376                 {
377                         // url_fclose is processing, we got a response for writing the data
378                         // this must come from HTTP
379                         print("Got response from player stats server:\n");
380                         while((s = url_fgets(fh))) { print("  ", s, "\n"); }
381                         print("End of response.\n");
382                         url_fclose(fh);
383                         break;
384                 }
385                 
386                 case URL_READY_CLOSED:
387                 {
388                         // url_fclose has finished
389                         print("Player stats written\n");
390                         playerstats_waitforme = TRUE;
391                         db_close(playerstats_db);
392                         playerstats_db = -1;
393                         break;
394                 }
395                 
396                 case URL_READY_ERROR:
397                 default:
398                 {
399                         print("Player stats writing failed: ", ftos(status), "\n");
400                         playerstats_waitforme = TRUE;
401                         if(playerstats_db >= 0)
402                         {
403                                 db_close(playerstats_db);
404                                 playerstats_db = -1;
405                         }
406                         break;
407                 }
408         }
409 }
410 #endif // SVQC
411
412 float playerinfo_db;
413 string playerinfo_last;
414 string playerinfo_events_last;
415 .float playerid;
416
417 void PlayerInfo_AddPlayer(entity e)
418 {
419         if(playerinfo_db < 0)
420                 return;
421
422         string key;
423         key = sprintf("#%d:*", e.playerid); // TODO: use hashkey instead?
424
425         string p;
426         p = db_get(playerinfo_db, key);
427         if(p == "")
428         {
429                 if(playerinfo_last)
430                 {
431                         db_put(playerinfo_db, key, playerinfo_last);
432                         strunzone(playerinfo_last);
433                 }
434                 else
435                         db_put(playerinfo_db, key, "#");
436                 playerinfo_last = strzone(ftos(e.playerid));
437                 print("  Added player ", ftos(e.playerid), " to playerinfo_db\n");//DEBUG//
438         }
439 }
440
441 void PlayerInfo_AddItem(entity e, string item_id, string val)
442 {
443         if(playerinfo_db < 0)
444                 return;
445
446         string key;
447         key = sprintf("*:%s", item_id);
448
449         string p;
450         p = db_get(playerinfo_db, key);
451         if(p == "")
452         {
453                 if(playerinfo_events_last)
454                 {
455                         db_put(playerinfo_db, key, playerinfo_events_last);
456                         strunzone(playerinfo_events_last);
457                 }
458                 else
459                         db_put(playerinfo_db, key, "#");
460                 playerinfo_events_last = strzone(item_id);
461         }
462
463         key = sprintf("#%d:%s", e.playerid, item_id);
464         db_put(playerinfo_db, key, val);
465         print("  Added item ", key, "=", val, " to playerinfo_db\n");//DEBUG//
466 }
467
468 string PlayerInfo_GetItem(entity e, string item_id)
469 {
470         if(playerinfo_db < 0)
471                 return "";
472
473         string key;
474         key = sprintf("#%d:%s",  e.playerid, item_id);
475         return db_get(playerinfo_db, key);
476 }
477
478 string PlayerInfo_GetItemLocal(string item_id)
479 {
480         entity p = spawn();
481         p.playerid = 0;
482         return PlayerInfo_GetItem(p, item_id);
483 }
484
485 void PlayerInfo_ready(entity fh, entity p, float status)
486 {
487         float n;
488         string s;
489
490         PlayerInfo_AddPlayer(p);
491
492         switch(status)
493         {
494                 case URL_READY_CANWRITE:
495                         print("-- Sending data to player stats server\n");
496                         url_fputs(fh, "V 1\n");
497 #ifdef WATERMARK
498                         url_fputs(fh, sprintf("R %s\n", WATERMARK));
499 #endif
500 #ifdef MENUQC
501                         url_fputs(fh, sprintf("l %s\n", cvar_string("_menu_prvm_language"))); // language
502                         url_fputs(fh, sprintf("c %s\n", cvar_string("_menu_prvm_country"))); // country
503                         url_fputs(fh, sprintf("g %s\n", cvar_string("_menu_prvm_gender"))); // gender
504                         url_fputs(fh, sprintf("n %s\n", cvar_string("_cl_name"))); // name
505                         url_fputs(fh, sprintf("m %s %s\n", cvar_string("_cl_playermodel"), cvar_string("_cl_playerskin"))); // model/skin
506 #endif
507                         url_fputs(fh, "\n");
508                         url_fclose(fh);
509                         break;
510                 case URL_READY_CANREAD:
511                         print("-- Got response from player stats server:\n");
512                         string gametype = string_null;
513                         while((s = url_fgets(fh)))
514                         {
515                                 print("  ", s, "\n");
516
517                                 string key = string_null, value = string_null, data = string_null;
518
519                                 n = tokenizebyseparator(s, " "); // key (value) data
520                                 if (n == 1)
521                                         continue;
522                                 else if (n == 2)
523                                 {
524                                         key = argv(0);
525                                         data = argv(1);
526                                 }
527                                 else if (n == 3)
528                                 {
529                                         key = argv(0);
530                                         value = argv(1);
531                                         data = argv(2);
532                                 }
533                                 else if (n == 4)
534                                 {
535                                         key = argv(0);
536                                         value = argv(1);
537                                         data = argv(2);
538                                 }
539                                 else
540                                         continue;
541
542                                 if (data == "")
543                                         continue;
544
545                                 if (key == "#")
546                                         // comment
547                                         continue;
548                                 else if (key == "V")
549                                         // version
550                                         PlayerInfo_AddItem(p, "_version", data);
551                                 else if (key == "R")
552                                         // watermark
553                                         PlayerInfo_AddItem(p, "_release", data);
554                                 else if (key == "T")
555                                         // timestamp
556                                         PlayerInfo_AddItem(p, "_time", data);
557                                 else if (key == "S")
558                                         // stats page URL
559                                         PlayerInfo_AddItem(p, "_statsurl", data);
560                                 else if (key == "P")
561                                         // player hashkey
562                                         PlayerInfo_AddItem(p, "_hashkey", data);
563                                 else if (key == "n")
564                                         // player nick
565                                         PlayerInfo_AddItem(p, "_playernick", data);
566                                 else if (key == "i")
567                                         // xonstats id
568                                         PlayerInfo_AddItem(p, "_playerid", data);
569                                 else if (key == "G")
570                                         gametype = data;
571                                 else if (key == "e" && value != "")
572                                 {
573                                         if (gametype == "")
574                                                 PlayerInfo_AddItem(p, value, data);
575                                         else
576                                                 PlayerInfo_AddItem(p, sprintf("%s/%s", gametype, value), data);
577                                 }
578                                 else
579                                         continue;
580                         }
581                         print("-- End of response.\n");
582                         url_fclose(fh);
583                         break;
584                 case URL_READY_CLOSED:
585                         // url_fclose has finished
586                         print("Player stats synchronized with server\n");
587                         break;
588                 case URL_READY_ERROR:
589                 default:
590                         print("Receiving player stats failed: ", ftos(status), "\n");
591                         break;
592         }
593 }
594
595 void PlayerInfo_Init()
596 {
597         playerinfo_db = -1;
598         playerinfo_db = db_create();
599 }
600
601 #ifdef SVQC
602 void PlayerInfo_Basic(entity p)
603 {
604         print("-- Getting basic PlayerInfo for player ",ftos(p.playerid)," (SVQC)\n");
605
606         if(playerinfo_db < 0)
607                 return;
608
609         string uri;
610         uri = autocvar_g_playerinfo_uri;
611         if(uri != "" && p.crypto_idfp != "")
612         {
613                 uri = strcat(uri, "/elo/", uri_escape(p.crypto_idfp));
614                 print("Retrieving playerstats from URL: ", uri, "\n");
615                 url_single_fopen(uri, FILE_READ, PlayerInfo_ready, p);
616         }
617 }
618 #endif
619
620 #ifdef MENUQC
621 void PlayerInfo_Details()
622 {
623         print("-- Getting detailed PlayerInfo for local player (MENUQC)\n");
624
625         if(playerinfo_db < 0)
626                 return;
627
628         string uri;
629         uri = autocvar_g_playerinfo_uri; // FIXME
630         if(uri != "" && crypto_getmyidstatus(0) > 0)
631         {
632                 uri = strcat(uri, "/player/", uri_escape(crypto_getmyidfp(0)));
633                 print("Retrieving playerstats from URL: ", uri, "\n");
634                 url_single_fopen(uri, FILE_APPEND, PlayerInfo_ready, world);
635         }
636 }
637 #endif
638
639 #ifdef CSQC
640 /*
641  * FIXME - crypto_* builtin functions missing in CSQC (csprogsdefs.qc:885)
642 void PlayerInfo_Details()
643 {
644         print("-- Getting detailed PlayerInfo for local player (CSQC)\n");
645
646         if(playerinfo_db < 0)
647                 return;
648
649         string uri;
650         uri = autocvar_g_playerinfo_uri; // FIXME
651         if(uri != "" && crypto_getmyidstatus(0) > 0)
652         {
653                 entity p = spawn();
654                 p.playerid = 0; // TODO: okay to use -1 for local player? or does local player already has an entity in MENUQC?
655                 uri = strcat(uri, "/player/", uri_escape(crypto_getmyidfp(0)));
656                 print("Retrieving playerstats from URL: ", uri, "\n");
657                 url_single_fopen(uri, FILE_READ, PlayerInfo_ready, p);
658         }
659 }
660 */
661 #endif