00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "server.h"
00025 #include "..\rankings\1.0\gr\grapi.h"
00026 #include "..\rankings\1.0\gr\grlog.h"
00027
00028 typedef struct
00029 {
00030 GR_CONTEXT context;
00031 uint64_t game_id;
00032 uint64_t match;
00033 uint64_t player_id;
00034 GR_PLAYER_TOKEN token;
00035 grank_status_t grank_status;
00036 grank_status_t final_status;
00037 uint32_t grank;
00038 char name[32];
00039 } ranked_player_t;
00040
00041 static int s_rankings_contexts = 0;
00042 static qboolean s_rankings_active = qfalse;
00043 static GR_CONTEXT s_server_context = 0;
00044 static uint64_t s_server_match = 0;
00045 static char* s_rankings_game_key = NULL;
00046 static uint64_t s_rankings_game_id = 0;
00047 static ranked_player_t* s_ranked_players = NULL;
00048 static qboolean s_server_quitting = qfalse;
00049 static const char s_ascii_encoding[] =
00050 "0123456789abcdef"
00051 "ghijklmnopqrstuv"
00052 "wxyzABCDEFGHIJKL"
00053 "MNOPQRSTUVWXYZ[]";
00054
00055
00056 static void SV_RankNewGameCBF( GR_NEWGAME* gr_newgame, void* cbf_arg );
00057 static void SV_RankUserCBF( GR_LOGIN* gr_login, void* cbf_arg );
00058 static void SV_RankJoinGameCBF( GR_JOINGAME* gr_joingame, void* cbf_arg );
00059 static void SV_RankSendReportsCBF( GR_STATUS* gr_status, void* cbf_arg );
00060 static void SV_RankCleanupCBF( GR_STATUS* gr_status, void* cbf_arg );
00061 static void SV_RankCloseContext( ranked_player_t* ranked_player );
00062 static int SV_RankAsciiEncode( char* dest, const unsigned char* src,
00063 int src_len );
00064 static int SV_RankAsciiDecode( unsigned char* dest, const char* src,
00065 int src_len );
00066 static void SV_RankEncodeGameID( uint64_t game_id, char* result,
00067 int len );
00068 static uint64_t SV_RankDecodePlayerID( const char* string );
00069 static void SV_RankDecodePlayerKey( const char* string, GR_PLAYER_TOKEN key );
00070 static char* SV_RankStatusString( GR_STATUS status );
00071 static void SV_RankError( const char* fmt, ... );
00072 static char SV_RankGameKey[64];
00073
00074
00075
00076
00077
00078
00079 void SV_RankBegin( char *gamekey )
00080 {
00081 GR_INIT init;
00082 GR_STATUS status;
00083
00084 assert( s_rankings_contexts == 0 );
00085 assert( !s_rankings_active );
00086 assert( s_ranked_players == NULL );
00087
00088 if( sv_enableRankings->integer == 0 || Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER )
00089 {
00090 s_rankings_active = qfalse;
00091 if( sv_rankingsActive->integer == 1 )
00092 {
00093 Cvar_Set( "sv_rankingsActive", "0" );
00094 }
00095 return;
00096 }
00097
00098
00099 if( strcmp(gamekey, GR_GAMEKEY) == 0 )
00100 {
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110 switch( (int)Cvar_VariableValue("g_gametype") )
00111 {
00112 case GT_FFA:
00113 gamekey = "Q3 Free For All";
00114 break;
00115 case GT_TOURNAMENT:
00116 gamekey = "Q3 Tournament";
00117 break;
00118 case GT_TEAM:
00119 gamekey = "Q3 Team Deathmatch";
00120 break;
00121 case GT_CTF:
00122 gamekey = "Q3 Capture the Flag";
00123 break;
00124 case GT_1FCTF:
00125 gamekey = "Q3 One Flag CTF";
00126 break;
00127 case GT_OBELISK:
00128 gamekey = "Q3 Overload";
00129 break;
00130 case GT_HARVESTER:
00131 gamekey = "Q3 Harvester";
00132 break;
00133 default:
00134 break;
00135 }
00136 }
00137 s_rankings_game_key = gamekey;
00138
00139
00140 GRankLogLevel( GRLOG_OFF );
00141 memset(SV_RankGameKey,0,sizeof(SV_RankGameKey));
00142 strncpy(SV_RankGameKey,gamekey,sizeof(SV_RankGameKey)-1);
00143 init = GRankInit( 1, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END );
00144 s_server_context = init.context;
00145 s_rankings_contexts++;
00146 Com_DPrintf( "SV_RankBegin(); GR_GAMEKEY is %s\n", gamekey );
00147 Com_DPrintf( "SV_RankBegin(); s_rankings_contexts=%d\n",s_rankings_contexts );
00148 Com_DPrintf( "SV_RankBegin(); s_server_context=%d\n",init.context );
00149
00150
00151 if(!strlen(Cvar_VariableString( "sv_leagueName" )))
00152 {
00153 status = GRankNewGameAsync
00154 (
00155 s_server_context,
00156 SV_RankNewGameCBF,
00157 NULL,
00158 GR_OPT_LEAGUENAME,
00159 (void*)(Cvar_VariableString( "sv_leagueName" )),
00160 GR_OPT_END
00161 );
00162 }
00163 else
00164 {
00165 status = GRankNewGameAsync
00166 (
00167 s_server_context,
00168 SV_RankNewGameCBF,
00169 NULL,
00170 GR_OPT_END
00171 );
00172 }
00173
00174 if( status != GR_STATUS_PENDING )
00175 {
00176 SV_RankError( "SV_RankBegin: Expected GR_STATUS_PENDING, got %s",
00177 SV_RankStatusString( status ) );
00178 return;
00179 }
00180
00181
00182 if( com_developer->value )
00183 {
00184 GRankLogLevel( GRLOG_TRACE );
00185 }
00186
00187
00188 s_ranked_players = Z_Malloc( sv_maxclients->value *
00189 sizeof(ranked_player_t) );
00190 memset( (void*)s_ranked_players, 0 ,sv_maxclients->value
00191 * sizeof(ranked_player_t));
00192 }
00193
00194
00195
00196
00197
00198
00199 void SV_RankEnd( void )
00200 {
00201 GR_STATUS status;
00202 int i;
00203
00204 Com_DPrintf( "SV_RankEnd();\n" );
00205
00206 if( !s_rankings_active )
00207 {
00208
00209 if( s_ranked_players != NULL )
00210 {
00211 for( i = 0; i < sv_maxclients->value; i++ )
00212 {
00213 if( s_ranked_players[i].context != 0 )
00214 {
00215 SV_RankCloseContext( &(s_ranked_players[i]) );
00216 }
00217 }
00218 }
00219 if( s_server_context != 0 )
00220 {
00221 SV_RankCloseContext( NULL );
00222 }
00223
00224 return;
00225 }
00226
00227 for( i = 0; i < sv_maxclients->value; i++ )
00228 {
00229 if( s_ranked_players[i].grank_status == QGR_STATUS_ACTIVE )
00230 {
00231 SV_RankUserLogout( i );
00232 Com_DPrintf( "SV_RankEnd: SV_RankUserLogout %d\n",i );
00233 }
00234 }
00235
00236 assert( s_server_context != 0 );
00237
00238
00239 status = GRankSendReportsAsync
00240 (
00241 s_server_context,
00242 0,
00243 SV_RankSendReportsCBF,
00244 NULL,
00245 GR_OPT_END
00246 );
00247
00248 if( status != GR_STATUS_PENDING )
00249 {
00250 SV_RankError( "SV_RankEnd: Expected GR_STATUS_PENDING, got %s",
00251 SV_RankStatusString( status ) );
00252 }
00253
00254 s_rankings_active = qfalse;
00255 Cvar_Set( "sv_rankingsActive", "0" );
00256 }
00257
00258
00259
00260
00261
00262
00263 void SV_RankPoll( void )
00264 {
00265 GRankPoll();
00266 }
00267
00268
00269
00270
00271
00272
00273 qboolean SV_RankCheckInit( void )
00274 {
00275 return (s_rankings_contexts > 0);
00276 }
00277
00278
00279
00280
00281
00282
00283 qboolean SV_RankActive( void )
00284 {
00285 return s_rankings_active;
00286 }
00287
00288
00289
00290
00291
00292
00293 grank_status_t SV_RankUserStatus( int index )
00294 {
00295 if( !s_rankings_active )
00296 {
00297 return GR_STATUS_ERROR;
00298 }
00299
00300 assert( s_ranked_players != NULL );
00301 assert( index >= 0 );
00302 assert( index < sv_maxclients->value );
00303
00304 return s_ranked_players[index].grank_status;
00305 }
00306
00307
00308
00309
00310
00311
00312 int SV_RankUserGrank( int index )
00313 {
00314 if( !s_rankings_active )
00315 {
00316 return 0;
00317 }
00318
00319 assert( s_ranked_players != NULL );
00320 assert( index >= 0 );
00321 assert( index < sv_maxclients->value );
00322
00323 return s_ranked_players[index].grank;
00324 }
00325
00326
00327
00328
00329
00330
00331 void SV_RankUserReset( int index )
00332 {
00333 if( !s_rankings_active )
00334 {
00335 return;
00336 }
00337
00338 assert( s_ranked_players != NULL );
00339 assert( index >= 0 );
00340 assert( index < sv_maxclients->value );
00341
00342 switch( s_ranked_players[index].grank_status )
00343 {
00344 case QGR_STATUS_SPECTATOR:
00345 case QGR_STATUS_NO_USER:
00346 case QGR_STATUS_BAD_PASSWORD:
00347 case QGR_STATUS_USER_EXISTS:
00348 case QGR_STATUS_NO_MEMBERSHIP:
00349 case QGR_STATUS_TIMEOUT:
00350 case QGR_STATUS_ERROR:
00351 s_ranked_players[index].grank_status = QGR_STATUS_NEW;
00352 break;
00353 default:
00354 break;
00355 }
00356 }
00357
00358
00359
00360
00361
00362
00363 void SV_RankUserSpectate( int index )
00364 {
00365 if( !s_rankings_active )
00366 {
00367 return;
00368 }
00369
00370 assert( s_ranked_players != NULL );
00371 assert( index >= 0 );
00372 assert( index < sv_maxclients->value );
00373
00374
00375 s_ranked_players[index].grank_status = QGR_STATUS_SPECTATOR;
00376 }
00377
00378
00379
00380
00381
00382
00383 void SV_RankUserCreate( int index, char* username, char* password,
00384 char* email )
00385 {
00386 GR_INIT init;
00387 GR_STATUS status;
00388
00389 assert( index >= 0 );
00390 assert( index < sv_maxclients->value );
00391 assert( username != NULL );
00392 assert( password != NULL );
00393 assert( email != NULL );
00394 assert( s_ranked_players );
00395 assert( s_ranked_players[index].grank_status != QGR_STATUS_ACTIVE );
00396
00397 Com_DPrintf( "SV_RankUserCreate( %d, %s, \"****\", %s );\n", index,
00398 username, email );
00399
00400 if( !s_rankings_active )
00401 {
00402 Com_DPrintf( "SV_RankUserCreate: Not ready to create\n" );
00403 s_ranked_players[index].grank_status = QGR_STATUS_ERROR;
00404 return;
00405 }
00406
00407 if( s_ranked_players[index].grank_status == QGR_STATUS_ACTIVE )
00408 {
00409 Com_DPrintf( "SV_RankUserCreate: Got Create from active player\n" );
00410 return;
00411 }
00412
00413
00414 init = GRankInit( 0, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END );
00415 s_ranked_players[index].context = init.context;
00416 s_rankings_contexts++;
00417 Com_DPrintf( "SV_RankUserCreate(); s_rankings_contexts=%d\n",s_rankings_contexts );
00418 Com_DPrintf( "SV_RankUserCreate(); s_ranked_players[%d].context=%d\n",index,init.context );
00419
00420
00421 status = GRankUserCreateAsync
00422 (
00423 s_ranked_players[index].context,
00424 username,
00425 password,
00426 email,
00427 SV_RankUserCBF,
00428 (void*)&s_ranked_players[index],
00429 GR_OPT_END
00430 );
00431
00432 if( status == GR_STATUS_PENDING )
00433 {
00434 s_ranked_players[index].grank_status = QGR_STATUS_PENDING;
00435 s_ranked_players[index].final_status = QGR_STATUS_NEW;
00436 }
00437 else
00438 {
00439 SV_RankError( "SV_RankUserCreate: Expected GR_STATUS_PENDING, got %s",
00440 SV_RankStatusString( status ) );
00441 }
00442 }
00443
00444
00445
00446
00447
00448
00449 void SV_RankUserLogin( int index, char* username, char* password )
00450 {
00451 GR_INIT init;
00452 GR_STATUS status;
00453
00454 assert( index >= 0 );
00455 assert( index < sv_maxclients->value );
00456 assert( username != NULL );
00457 assert( password != NULL );
00458 assert( s_ranked_players );
00459 assert( s_ranked_players[index].grank_status != QGR_STATUS_ACTIVE );
00460
00461 Com_DPrintf( "SV_RankUserLogin( %d, %s, \"****\" );\n", index, username );
00462
00463 if( !s_rankings_active )
00464 {
00465 Com_DPrintf( "SV_RankUserLogin: Not ready for login\n" );
00466 s_ranked_players[index].grank_status = QGR_STATUS_ERROR;
00467 return;
00468 }
00469
00470 if( s_ranked_players[index].grank_status == QGR_STATUS_ACTIVE )
00471 {
00472 Com_DPrintf( "SV_RankUserLogin: Got Login from active player\n" );
00473 return;
00474 }
00475
00476
00477 init = GRankInit( 0, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END );
00478 s_ranked_players[index].context = init.context;
00479 s_rankings_contexts++;
00480 Com_DPrintf( "SV_RankUserLogin(); s_rankings_contexts=%d\n",s_rankings_contexts );
00481 Com_DPrintf( "SV_RankUserLogin(); s_ranked_players[%d].context=%d\n",index,init.context );
00482
00483
00484 status = GRankUserLoginAsync
00485 (
00486 s_ranked_players[index].context,
00487 username,
00488 password,
00489 SV_RankUserCBF,
00490 (void*)&s_ranked_players[index],
00491 GR_OPT_END
00492 );
00493
00494 if( status == GR_STATUS_PENDING )
00495 {
00496 s_ranked_players[index].grank_status = QGR_STATUS_PENDING;
00497 s_ranked_players[index].final_status = QGR_STATUS_NEW;
00498 }
00499 else
00500 {
00501 SV_RankError( "SV_RankUserLogin: Expected GR_STATUS_PENDING, got %s",
00502 SV_RankStatusString( status ) );
00503 }
00504 }
00505
00506
00507
00508
00509
00510
00511 qboolean SV_RankUserValidate( int index, const char* player_id, const char* key, int token_len, int rank, char* name )
00512 {
00513 GR_INIT init;
00514 GR_STATUS status;
00515 qboolean rVal;
00516 ranked_player_t* ranked_player;
00517 int i;
00518
00519 assert( s_ranked_players );
00520 assert( s_ranked_players[index].grank_status != QGR_STATUS_ACTIVE );
00521
00522 rVal = qfalse;
00523
00524 if( !s_rankings_active )
00525 {
00526 Com_DPrintf( "SV_RankUserValidate: Not ready to validate\n" );
00527 s_ranked_players[index].grank_status = QGR_STATUS_ERROR;
00528 return rVal;
00529 }
00530
00531 ranked_player = &(s_ranked_players[index]);
00532
00533 if ( (player_id != NULL) && (key != NULL))
00534 {
00535
00536
00537
00538
00539
00540
00541 init = GRankInit( 0, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END );
00542 ranked_player->context = init.context;
00543 s_rankings_contexts++;
00544 Com_DPrintf( "SV_RankUserValidate(); s_rankings_contexts=%d\n",s_rankings_contexts );
00545 Com_DPrintf( "SV_RankUserValidate(); s_ranked_players[%d].context=%d\n",index,init.context );
00546
00547
00548 ranked_player->player_id = SV_RankDecodePlayerID(player_id);
00549 Com_DPrintf( "SV_RankUserValidate(); ranked_player->player_id =%u\n", (uint32_t)ranked_player->player_id );
00550 SV_RankDecodePlayerKey(key, ranked_player->token);
00551
00552
00553 Q_strncpyz( ranked_player->name, name, sizeof(ranked_player->name) );
00554 for( i = 0; i < sv_maxclients->value; i++ )
00555 {
00556 if( (i != index) && (s_ranked_players[i].grank_status == QGR_STATUS_ACTIVE) &&
00557 (strcmp( s_ranked_players[i].name, name ) == 0) )
00558 {
00559 Com_DPrintf( "SV_RankUserValidate: Duplicate login\n" );
00560 ranked_player->grank_status = QGR_STATUS_NO_USER;
00561 ranked_player->final_status = QGR_STATUS_NEW;
00562 ranked_player->grank = 0;
00563 return qfalse;
00564 }
00565 }
00566
00567
00568 status = GRankPlayerValidate(
00569 s_server_context,
00570 ranked_player->player_id,
00571 ranked_player->token,
00572 token_len,
00573 GR_OPT_PLAYERCONTEXT,
00574 ranked_player->context,
00575 GR_OPT_END);
00576 }
00577 else
00578 {
00579
00580 status = GR_STATUS_OK;
00581 }
00582
00583 if (status == GR_STATUS_OK)
00584 {
00585 ranked_player->grank_status = QGR_STATUS_ACTIVE;
00586 ranked_player->final_status = QGR_STATUS_NEW;
00587 ranked_player->grank = rank;
00588 rVal = qtrue;
00589 }
00590 else if (status == GR_STATUS_INVALIDUSER)
00591 {
00592 ranked_player->grank_status = QGR_STATUS_INVALIDUSER;
00593 ranked_player->final_status = QGR_STATUS_NEW;
00594 ranked_player->grank = 0;
00595 rVal = qfalse;
00596 }
00597 else
00598 {
00599 SV_RankError( "SV_RankUserValidate: Unexpected status %s",
00600 SV_RankStatusString( status ) );
00601 s_ranked_players[index].grank_status = QGR_STATUS_ERROR;
00602 ranked_player->grank = 0;
00603 }
00604
00605 return rVal;
00606 }
00607
00608
00609
00610
00611
00612
00613 void SV_RankUserLogout( int index )
00614 {
00615 GR_STATUS status;
00616 GR_STATUS cleanup_status;
00617
00618 if( !s_rankings_active )
00619 {
00620 return;
00621 }
00622
00623 assert( index >= 0 );
00624 assert( index < sv_maxclients->value );
00625 assert( s_ranked_players );
00626
00627 if( s_ranked_players[index].context == 0 ) {
00628 return;
00629 }
00630
00631 Com_DPrintf( "SV_RankUserLogout( %d );\n", index );
00632
00633
00634
00635
00636
00637
00638 status = GRankSendReportsAsync
00639 (
00640 s_ranked_players[index].context,
00641 0,
00642 SV_RankSendReportsCBF,
00643 (void*)&s_ranked_players[index],
00644 GR_OPT_END
00645 );
00646
00647 if( status == GR_STATUS_PENDING )
00648 {
00649 s_ranked_players[index].grank_status = QGR_STATUS_PENDING;
00650 s_ranked_players[index].final_status = QGR_STATUS_NEW;
00651 }
00652 else
00653 {
00654 SV_RankError( "SV_RankUserLogout: Expected GR_STATUS_PENDING, got %s",
00655 SV_RankStatusString( status ) );
00656
00657 cleanup_status = GRankCleanupAsync
00658 (
00659 s_ranked_players[index].context,
00660 0,
00661 SV_RankCleanupCBF,
00662 (void*)&s_ranked_players[index],
00663 GR_OPT_END
00664 );
00665
00666 if( cleanup_status != GR_STATUS_PENDING )
00667 {
00668 SV_RankError( "SV_RankUserLogout: Expected "
00669 "GR_STATUS_PENDING from GRankCleanupAsync, got %s",
00670 SV_RankStatusString( cleanup_status ) );
00671 SV_RankCloseContext( &(s_ranked_players[index]) );
00672 }
00673 }
00674 }
00675
00676
00677
00678
00679
00680
00681 void SV_RankReportInt( int index1, int index2, int key, int value,
00682 qboolean accum )
00683 {
00684 GR_STATUS status;
00685 GR_CONTEXT context;
00686 uint64_t match;
00687 uint64_t user1;
00688 uint64_t user2;
00689 int opt_accum;
00690
00691 if( !s_rankings_active )
00692 {
00693 return;
00694 }
00695
00696 assert( index1 >= -1 );
00697 assert( index1 < sv_maxclients->value );
00698 assert( index2 >= -1 );
00699 assert( index2 < sv_maxclients->value );
00700 assert( s_ranked_players );
00701
00702
00703
00704
00705
00706 if( index1 == -1 )
00707 {
00708 context = s_server_context;
00709 match = s_server_match;
00710 user1 = 0;
00711 }
00712 else
00713 {
00714 if( s_ranked_players[index1].grank_status != QGR_STATUS_ACTIVE )
00715 {
00716 Com_DPrintf( "SV_RankReportInt: Expecting QGR_STATUS_ACTIVE"
00717 " Got Unexpected status %d for player %d\n",
00718 s_ranked_players[index1].grank_status, index1 );
00719 return;
00720 }
00721
00722 context = s_ranked_players[index1].context;
00723 match = s_ranked_players[index1].match;
00724 user1 = s_ranked_players[index1].player_id;
00725 }
00726
00727
00728 if( index2 == -1 )
00729 {
00730 user2 = 0;
00731 }
00732 else
00733 {
00734 if( s_ranked_players[index2].grank_status != QGR_STATUS_ACTIVE )
00735 {
00736 Com_DPrintf( "SV_RankReportInt: Expecting QGR_STATUS_ACTIVE"
00737 " Got Unexpected status %d for player %d\n",
00738 s_ranked_players[index2].grank_status, index2 );
00739 return;
00740 }
00741
00742 user2 = s_ranked_players[index2].player_id;
00743 }
00744
00745 opt_accum = accum ? GR_OPT_ACCUM : GR_OPT_END;
00746
00747 status = GRankReportInt
00748 (
00749 context,
00750 match,
00751 user1,
00752 user2,
00753 key,
00754 value,
00755 opt_accum,
00756 GR_OPT_END
00757 );
00758
00759 if( status != GR_STATUS_OK )
00760 {
00761 SV_RankError( "SV_RankReportInt: Unexpected status %s",
00762 SV_RankStatusString( status ) );
00763 }
00764
00765 if( user2 != 0 )
00766 {
00767 context = s_ranked_players[index2].context;
00768 match = s_ranked_players[index2].match;
00769
00770 status = GRankReportInt
00771 (
00772 context,
00773 match,
00774 user1,
00775 user2,
00776 key,
00777 value,
00778 opt_accum,
00779 GR_OPT_END
00780 );
00781
00782 if( status != GR_STATUS_OK )
00783 {
00784 SV_RankError( "SV_RankReportInt: Unexpected status %s",
00785 SV_RankStatusString( status ) );
00786 }
00787 }
00788 }
00789
00790
00791
00792
00793
00794
00795 void SV_RankReportStr( int index1, int index2, int key, char* value )
00796 {
00797 GR_STATUS status;
00798 GR_CONTEXT context;
00799 uint64_t match;
00800 uint64_t user1;
00801 uint64_t user2;
00802
00803 if( !s_rankings_active )
00804 {
00805 return;
00806 }
00807
00808 assert( index1 >= -1 );
00809 assert( index1 < sv_maxclients->value );
00810 assert( index2 >= -1 );
00811 assert( index2 < sv_maxclients->value );
00812 assert( s_ranked_players );
00813
00814
00815
00816
00817
00818 if( index1 == -1 )
00819 {
00820 context = s_server_context;
00821 match = s_server_match;
00822 user1 = 0;
00823 }
00824 else
00825 {
00826 if( s_ranked_players[index1].grank_status != QGR_STATUS_ACTIVE )
00827 {
00828 Com_DPrintf( "SV_RankReportStr: Unexpected status %d\n",
00829 s_ranked_players[index1].grank_status );
00830 return;
00831 }
00832
00833 context = s_ranked_players[index1].context;
00834 match = s_ranked_players[index1].match;
00835 user1 = s_ranked_players[index1].player_id;
00836 }
00837
00838
00839 if( index2 == -1 )
00840 {
00841 user2 = 0;
00842 }
00843 else
00844 {
00845 if( s_ranked_players[index2].grank_status != QGR_STATUS_ACTIVE )
00846 {
00847 Com_DPrintf( "SV_RankReportStr: Unexpected status %d\n",
00848 s_ranked_players[index2].grank_status );
00849 return;
00850 }
00851
00852 user2 = s_ranked_players[index2].player_id;
00853 }
00854
00855 status = GRankReportStr
00856 (
00857 context,
00858 match,
00859 user1,
00860 user2,
00861 key,
00862 value,
00863 GR_OPT_END
00864 );
00865
00866 if( status != GR_STATUS_OK )
00867 {
00868 SV_RankError( "SV_RankReportStr: Unexpected status %s",
00869 SV_RankStatusString( status ) );
00870 }
00871
00872 if( user2 != 0 )
00873 {
00874 context = s_ranked_players[index2].context;
00875 match = s_ranked_players[index2].match;
00876
00877 status = GRankReportStr
00878 (
00879 context,
00880 match,
00881 user1,
00882 user2,
00883 key,
00884 value,
00885 GR_OPT_END
00886 );
00887
00888 if( status != GR_STATUS_OK )
00889 {
00890 SV_RankError( "SV_RankReportInt: Unexpected status %s",
00891 SV_RankStatusString( status ) );
00892 }
00893 }
00894 }
00895
00896
00897
00898
00899
00900
00901 void SV_RankQuit( void )
00902 {
00903 int i;
00904 int j = 0;
00905
00906
00907 while( s_rankings_contexts > 1 )
00908 {
00909 assert(s_ranked_players);
00910 if( s_ranked_players != NULL )
00911 {
00912 for( i = 0; i < sv_maxclients->value; i++ )
00913 {
00914
00915 if( s_ranked_players[i].grank_status == QGR_STATUS_ACTIVE )
00916 {
00917 SV_RankUserLogout( i );
00918 Com_DPrintf( "SV_RankQuit: SV_RankUserLogout %d\n",i );
00919 }
00920 else
00921 {
00922 if( s_ranked_players[i].context )
00923 {
00924 GR_STATUS cleanup_status;
00925 cleanup_status = GRankCleanupAsync
00926 (
00927 s_ranked_players[i].context,
00928 0,
00929 SV_RankCleanupCBF,
00930 (void*)&(s_ranked_players[i]),
00931 GR_OPT_END
00932 );
00933
00934 if( cleanup_status != GR_STATUS_PENDING )
00935 {
00936 SV_RankError( "SV_RankQuit: Expected "
00937 "GR_STATUS_PENDING from GRankCleanupAsync, got %s",
00938 SV_RankStatusString( cleanup_status ) );
00939 }
00940 }
00941 }
00942 }
00943 }
00944 SV_RankPoll();
00945
00946
00947 assert( (j++) < 68 );
00948 }
00949 }
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961
00962
00963
00964 static void SV_RankNewGameCBF( GR_NEWGAME* gr_newgame, void* cbf_arg )
00965 {
00966 GR_MATCH match;
00967 int i;
00968
00969 assert( gr_newgame != NULL );
00970 assert( cbf_arg == NULL );
00971
00972 Com_DPrintf( "SV_RankNewGameCBF( %08X, %08X );\n", gr_newgame, cbf_arg );
00973
00974 if( gr_newgame->status == GR_STATUS_OK )
00975 {
00976 char info[MAX_INFO_STRING];
00977 char gameid[sizeof(s_ranked_players[i].game_id) * 4 / 3 + 2];
00978
00979
00980 s_rankings_game_id = gr_newgame->game_id;
00981
00982
00983 memset(gameid,0,sizeof(gameid));
00984 SV_RankEncodeGameID(s_rankings_game_id,gameid,sizeof(gameid));
00985
00986
00987 memset(info,0,sizeof(info));
00988 Info_SetValueForKey( info, "rankingsGameKey", s_rankings_game_key );
00989 Info_SetValueForKey( info, "rankingsGameID", gameid );
00990 SV_SetConfigstring( CS_GRANK, info );
00991
00992
00993 for( i = 0; i < sv_maxclients->value; i++ )
00994 s_ranked_players[i].grank_status = QGR_STATUS_NEW;
00995
00996
00997 match = GRankStartMatch( s_server_context );
00998 s_server_match = match.match;
00999
01000
01001 s_rankings_active = qtrue;
01002 Cvar_Set( "sv_rankingsActive", "1" );
01003
01004 }
01005 else if( gr_newgame->status == GR_STATUS_BADLEAGUE )
01006 {
01007 SV_RankError( "SV_RankNewGameCBF: Invalid League name\n" );
01008 }
01009 else
01010 {
01011
01012
01013
01014 SV_RankError( "SV_RankNewGameCBF: Unexpected status %s",
01015 SV_RankStatusString( gr_newgame->status ) );
01016 }
01017 }
01018
01019
01020
01021
01022
01023
01024 static void SV_RankUserCBF( GR_LOGIN* gr_login, void* cbf_arg )
01025 {
01026 ranked_player_t* ranked_player;
01027 GR_STATUS join_status;
01028 GR_STATUS cleanup_status;
01029
01030 assert( gr_login != NULL );
01031 assert( cbf_arg != NULL );
01032
01033 Com_DPrintf( "SV_RankUserCBF( %08X, %08X );\n", gr_login, cbf_arg );
01034
01035 ranked_player = (ranked_player_t*)cbf_arg;
01036 assert(ranked_player);
01037 assert( ranked_player->context );
01038
01039 switch( gr_login->status )
01040 {
01041 case GR_STATUS_OK:
01042
01043 join_status = GRankJoinGameAsync
01044 (
01045 ranked_player->context,
01046 s_rankings_game_id,
01047 SV_RankJoinGameCBF,
01048 cbf_arg,
01049 GR_OPT_END
01050 );
01051
01052 if( join_status != GR_STATUS_PENDING )
01053 {
01054 SV_RankError( "SV_RankUserCBF: Expected GR_STATUS_PENDING "
01055 "from GRankJoinGameAsync, got %s",
01056 SV_RankStatusString( join_status ) );
01057 }
01058 break;
01059 case GR_STATUS_NOUSER:
01060 Com_DPrintf( "SV_RankUserCBF: Got status %s\n",
01061 SV_RankStatusString( gr_login->status ) );
01062 ranked_player->final_status = QGR_STATUS_NO_USER;
01063 break;
01064 case GR_STATUS_BADPASSWORD:
01065 Com_DPrintf( "SV_RankUserCBF: Got status %s\n",
01066 SV_RankStatusString( gr_login->status ) );
01067 ranked_player->final_status = QGR_STATUS_BAD_PASSWORD;
01068 break;
01069 case GR_STATUS_TIMEOUT:
01070 Com_DPrintf( "SV_RankUserCBF: Got status %s\n",
01071 SV_RankStatusString( gr_login->status ) );
01072 ranked_player->final_status = QGR_STATUS_TIMEOUT;
01073 break;
01074 default:
01075 Com_DPrintf( "SV_RankUserCBF: Unexpected status %s\n",
01076 SV_RankStatusString( gr_login->status ) );
01077 ranked_player->final_status = QGR_STATUS_ERROR;
01078 break;
01079 }
01080
01081 if( ranked_player->final_status != QGR_STATUS_NEW )
01082 {
01083
01084 cleanup_status = GRankCleanupAsync
01085 (
01086 ranked_player->context,
01087 0,
01088 SV_RankCleanupCBF,
01089 (void*)ranked_player,
01090 GR_OPT_END
01091 );
01092
01093 if( cleanup_status != GR_STATUS_PENDING )
01094 {
01095 SV_RankError( "SV_RankUserCBF: Expected GR_STATUS_PENDING "
01096 "from GRankCleanupAsync, got %s",
01097 SV_RankStatusString( cleanup_status ) );
01098 SV_RankCloseContext( ranked_player );
01099 }
01100 }
01101 }
01102
01103
01104
01105
01106
01107
01108 static void SV_RankJoinGameCBF( GR_JOINGAME* gr_joingame, void* cbf_arg )
01109 {
01110 ranked_player_t* ranked_player;
01111 GR_MATCH match;
01112 GR_STATUS cleanup_status;
01113
01114 assert( gr_joingame != NULL );
01115 assert( cbf_arg != NULL );
01116
01117 Com_DPrintf( "SV_RankJoinGameCBF( %08X, %08X );\n", gr_joingame, cbf_arg );
01118
01119 ranked_player = (ranked_player_t*)cbf_arg;
01120
01121 assert( ranked_player );
01122 assert( ranked_player->context != 0 );
01123
01124 if( gr_joingame->status == GR_STATUS_OK )
01125 {
01126 int i;
01127
01128 ranked_player->player_id = gr_joingame->player_id;
01129 memcpy(ranked_player->token,gr_joingame->token,
01130 sizeof(GR_PLAYER_TOKEN)) ;
01131 match = GRankStartMatch( ranked_player->context );
01132 ranked_player->match = match.match;
01133 ranked_player->grank = gr_joingame->rank;
01134
01135
01136 for (i=0;i<sv_maxclients->value;i++)
01137 if ( ranked_player == &s_ranked_players[i] )
01138 SV_RankUserValidate(i,NULL,NULL,0, gr_joingame->rank,ranked_player->name);
01139 }
01140 else
01141 {
01142
01143 SV_RankError( "SV_RankJoinGameCBF: Unexpected status %s",
01144 SV_RankStatusString( gr_joingame->status ) );
01145
01146 cleanup_status = GRankCleanupAsync
01147 (
01148 ranked_player->context,
01149 0,
01150 SV_RankCleanupCBF,
01151 cbf_arg,
01152 GR_OPT_END
01153 );
01154
01155 if( cleanup_status != GR_STATUS_PENDING )
01156 {
01157 SV_RankError( "SV_RankJoinGameCBF: Expected "
01158 "GR_STATUS_PENDING from GRankCleanupAsync, got %s",
01159 SV_RankStatusString( cleanup_status ) );
01160 SV_RankCloseContext( ranked_player );
01161 }
01162 }
01163 }
01164
01165
01166
01167
01168
01169
01170 static void SV_RankSendReportsCBF( GR_STATUS* status, void* cbf_arg )
01171 {
01172 ranked_player_t* ranked_player;
01173 GR_CONTEXT context;
01174 GR_STATUS cleanup_status;
01175
01176 assert( status != NULL );
01177
01178
01179 Com_DPrintf( "SV_RankSendReportsCBF( %08X, %08X );\n", status, cbf_arg );
01180
01181 ranked_player = (ranked_player_t*)cbf_arg;
01182 if( ranked_player == NULL )
01183 {
01184 Com_DPrintf( "SV_RankSendReportsCBF: server\n" );
01185 context = s_server_context;
01186 }
01187 else
01188 {
01189 Com_DPrintf( "SV_RankSendReportsCBF: player\n" );
01190 context = ranked_player->context;
01191 }
01192
01193
01194 if( *status != GR_STATUS_OK )
01195 {
01196 SV_RankError( "SV_RankSendReportsCBF: Unexpected status %s",
01197 SV_RankStatusString( *status ) );
01198 }
01199
01200 if( context == 0 )
01201 {
01202 Com_DPrintf( "SV_RankSendReportsCBF: WARNING: context == 0" );
01203 SV_RankCloseContext( ranked_player );
01204 }
01205 else
01206 {
01207 cleanup_status = GRankCleanupAsync
01208 (
01209 context,
01210 0,
01211 SV_RankCleanupCBF,
01212 cbf_arg,
01213 GR_OPT_END
01214 );
01215
01216 if( cleanup_status != GR_STATUS_PENDING )
01217 {
01218 SV_RankError( "SV_RankSendReportsCBF: Expected "
01219 "GR_STATUS_PENDING from GRankCleanupAsync, got %s",
01220 SV_RankStatusString( cleanup_status ) );
01221 SV_RankCloseContext( ranked_player );
01222 }
01223 }
01224 }
01225
01226
01227
01228
01229
01230
01231 static void SV_RankCleanupCBF( GR_STATUS* status, void* cbf_arg )
01232 {
01233 ranked_player_t* ranked_player;
01234 ranked_player = (ranked_player_t*)cbf_arg;
01235
01236 assert( status != NULL );
01237
01238
01239 Com_DPrintf( "SV_RankCleanupCBF( %08X, %08X );\n", status, cbf_arg );
01240
01241 if( *status != GR_STATUS_OK )
01242 {
01243 SV_RankError( "SV_RankCleanupCBF: Unexpected status %s",
01244 SV_RankStatusString( *status ) );
01245 }
01246
01247 SV_RankCloseContext( ranked_player );
01248 }
01249
01250
01251
01252
01253
01254
01255 static void SV_RankCloseContext( ranked_player_t* ranked_player )
01256 {
01257 if( ranked_player == NULL )
01258 {
01259
01260 if( s_server_context == 0 )
01261 {
01262 return;
01263 }
01264 s_server_context = 0;
01265 s_server_match = 0;
01266 }
01267 else
01268 {
01269
01270 if( s_ranked_players == NULL )
01271 {
01272 return;
01273 }
01274 if( ranked_player->context == 0 )
01275 {
01276 return;
01277 }
01278 ranked_player->context = 0;
01279 ranked_player->match = 0;
01280 ranked_player->player_id = 0;
01281 memset( ranked_player->token, 0, sizeof(GR_PLAYER_TOKEN) );
01282 ranked_player->grank_status = ranked_player->final_status;
01283 ranked_player->final_status = QGR_STATUS_NEW;
01284 ranked_player->name[0] = '\0';
01285 }
01286
01287 assert( s_rankings_contexts > 0 );
01288 s_rankings_contexts--;
01289 Com_DPrintf( "SV_RankCloseContext: s_rankings_contexts = %d\n",
01290 s_rankings_contexts );
01291
01292 if( s_rankings_contexts == 0 )
01293 {
01294 GRankLogLevel( GRLOG_OFF );
01295
01296 if( s_ranked_players != NULL )
01297 {
01298 Z_Free( s_ranked_players );
01299 s_ranked_players = NULL;
01300 }
01301
01302 s_rankings_active = qfalse;
01303 Cvar_Set( "sv_rankingsActive", "0" );
01304 }
01305 }
01306
01307
01308
01309
01310
01311
01312
01313
01314
01315
01316
01317
01318
01319
01320 static int SV_RankAsciiEncode( char* dest, const unsigned char* src,
01321 int src_len )
01322 {
01323 unsigned char bin[3];
01324 unsigned char txt[4];
01325 int dest_len = 0;
01326 int i;
01327 int j;
01328 int num_chars;
01329
01330 assert( dest != NULL );
01331 assert( src != NULL );
01332
01333 for( i = 0; i < src_len; i += 3 )
01334 {
01335
01336 for( j = 0; j < 3; j++ )
01337 {
01338 bin[j] = (i + j < src_len) ? src[i + j] : 0;
01339 }
01340
01341
01342 txt[0] = bin[0] >> 2;
01343 txt[1] = ((bin[0] << 4) | (bin[1] >> 4)) & 63;
01344 txt[2] = ((bin[1] << 2) | (bin[2] >> 6)) & 63;
01345 txt[3] = bin[2] & 63;
01346
01347
01348 num_chars = (i + 2 < src_len) ? 4 : ((src_len - i) * 4) / 3 + 1;
01349 for( j = 0; j < num_chars; j++ )
01350 {
01351 dest[dest_len++] = s_ascii_encoding[txt[j]];
01352 }
01353 }
01354
01355 dest[dest_len] = '\0';
01356
01357 return dest_len;
01358 }
01359
01360
01361
01362
01363
01364
01365
01366
01367
01368
01369
01370
01371
01372 static int SV_RankAsciiDecode( unsigned char* dest, const char* src,
01373 int src_len )
01374 {
01375 static unsigned char s_inverse_encoding[256];
01376 static char s_init = 0;
01377
01378 unsigned char bin[3];
01379 unsigned char txt[4];
01380 int dest_len = 0;
01381 int i;
01382 int j;
01383 int num_bytes;
01384
01385 assert( dest != NULL );
01386 assert( src != NULL );
01387
01388 if( !s_init )
01389 {
01390
01391 memset( s_inverse_encoding, 255, sizeof(s_inverse_encoding) );
01392 for( i = 0; i < 64; i++ )
01393 {
01394 s_inverse_encoding[s_ascii_encoding[i]] = i;
01395 }
01396 s_init = 1;
01397 }
01398
01399 for( i = 0; i < src_len; i += 4 )
01400 {
01401
01402 for( j = 0; j < 4; j++ )
01403 {
01404 txt[j] = (i + j < src_len) ? s_inverse_encoding[src[i + j]] : 0;
01405 if (txt[j] == 255)
01406 {
01407 return 0;
01408 }
01409 }
01410
01411
01412 bin[0] = (txt[0] << 2) | (txt[1] >> 4);
01413 bin[1] = (txt[1] << 4) | (txt[2] >> 2);
01414 bin[2] = (txt[2] << 6) | txt[3];
01415
01416
01417 num_bytes = (i + 3 < src_len) ? 3 : ((src_len - i) * 3) / 4;
01418 for( j = 0; j < num_bytes; j++ )
01419 {
01420 dest[dest_len++] = bin[j];
01421 }
01422 }
01423
01424 return dest_len;
01425 }
01426
01427
01428
01429
01430
01431
01432 static void SV_RankEncodeGameID( uint64_t game_id, char* result,
01433 int len )
01434 {
01435 assert( result != NULL );
01436
01437 if( len < ( ( sizeof(game_id) * 4) / 3 + 2) )
01438 {
01439 Com_DPrintf( "SV_RankEncodeGameID: result buffer too small\n" );
01440 result[0] = '\0';
01441 }
01442 else
01443 {
01444 qint64 gameid = LittleLong64(*(qint64*)&game_id);
01445 SV_RankAsciiEncode( result, (unsigned char*)&gameid,
01446 sizeof(qint64) );
01447 }
01448 }
01449
01450
01451
01452
01453
01454
01455 static uint64_t SV_RankDecodePlayerID( const char* string )
01456 {
01457 unsigned char buffer[9];
01458 int len;
01459 qint64 player_id;
01460
01461 assert( string != NULL );
01462
01463 len = strlen (string) ;
01464 Com_DPrintf( "SV_RankDecodePlayerID: string length %d\n",len );
01465 SV_RankAsciiDecode( buffer, string, len );
01466 player_id = LittleLong64(*(qint64*)buffer);
01467 return *(uint64_t*)&player_id;
01468 }
01469
01470
01471
01472
01473
01474
01475 static void SV_RankDecodePlayerKey( const char* string, GR_PLAYER_TOKEN key )
01476 {
01477 unsigned char buffer[1400];
01478 int len;
01479 assert( string != NULL );
01480
01481 len = strlen (string) ;
01482 Com_DPrintf( "SV_RankDecodePlayerKey: string length %d\n",len );
01483
01484 memset(key,0,sizeof(GR_PLAYER_TOKEN));
01485 memset(buffer,0,sizeof(buffer));
01486 memcpy( key, buffer, SV_RankAsciiDecode( buffer, string, len ) );
01487 }
01488
01489
01490
01491
01492
01493
01494 static char* SV_RankStatusString( GR_STATUS status )
01495 {
01496 switch( status )
01497 {
01498 case GR_STATUS_OK: return "GR_STATUS_OK";
01499 case GR_STATUS_ERROR: return "GR_STATUS_ERROR";
01500 case GR_STATUS_BADPARAMS: return "GR_STATUS_BADPARAMS";
01501 case GR_STATUS_NETWORK: return "GR_STATUS_NETWORK";
01502 case GR_STATUS_NOUSER: return "GR_STATUS_NOUSER";
01503 case GR_STATUS_BADPASSWORD: return "GR_STATUS_BADPASSWORD";
01504 case GR_STATUS_BADGAME: return "GR_STATUS_BADGAME";
01505 case GR_STATUS_PENDING: return "GR_STATUS_PENDING";
01506 case GR_STATUS_BADDOMAIN: return "GR_STATUS_BADDOMAIN";
01507 case GR_STATUS_DOMAINLOCK: return "GR_STATUS_DOMAINLOCK";
01508 case GR_STATUS_TIMEOUT: return "GR_STATUS_TIMEOUT";
01509 case GR_STATUS_INVALIDUSER: return "GR_STATUS_INVALIDUSER";
01510 case GR_STATUS_INVALIDCONTEXT: return "GR_STATUS_INVALIDCONTEXT";
01511 default: return "(UNKNOWN)";
01512 }
01513 }
01514
01515
01516
01517
01518
01519
01520 static void SV_RankError( const char* fmt, ... )
01521 {
01522 va_list arg_ptr;
01523 char text[1024];
01524
01525 va_start( arg_ptr, fmt );
01526 vsprintf( text, fmt, arg_ptr );
01527 va_end( arg_ptr );
01528
01529 Com_DPrintf( "****************************************\n" );
01530 Com_DPrintf( "SV_RankError: %s\n", text );
01531 Com_DPrintf( "****************************************\n" );
01532
01533 s_rankings_active = qfalse;
01534 Cvar_Set( "sv_rankingsActive", "0" );
01535
01536 }
01537