aboutsummaryrefslogtreecommitdiffstats
path: root/code/server/sv_rankings.c
diff options
context:
space:
mode:
authorzakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea>2005-08-26 17:39:27 +0000
committerzakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea>2005-08-26 17:39:27 +0000
commit6bf20c78f5b69d40bcc4931df93d29198435ab67 (patch)
treee3eda937a05d7db42de725b7013bd0344b987f34 /code/server/sv_rankings.c
parent872d4d7f55af706737ffb361bb76ad13e7496770 (diff)
downloadioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.tar.gz
ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.zip
newlines fixed
git-svn-id: svn://svn.icculus.org/quake3/trunk@6 edf5b092-35ff-0310-97b2-ce42778d08ea
Diffstat (limited to 'code/server/sv_rankings.c')
-rwxr-xr-xcode/server/sv_rankings.c3074
1 files changed, 1537 insertions, 1537 deletions
diff --git a/code/server/sv_rankings.c b/code/server/sv_rankings.c
index 5d8a01f..d21a799 100755
--- a/code/server/sv_rankings.c
+++ b/code/server/sv_rankings.c
@@ -1,1537 +1,1537 @@
-/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-
-This file is part of Quake III Arena source code.
-
-Quake III Arena source code is free software; you can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
-or (at your option) any later version.
-
-Quake III Arena source code is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Foobar; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-===========================================================================
-*/
-// sv_rankings.c -- global rankings interface
-
-#include "server.h"
-#include "..\rankings\1.0\gr\grapi.h"
-#include "..\rankings\1.0\gr\grlog.h"
-
-typedef struct
-{
- GR_CONTEXT context;
- uint64_t game_id;
- uint64_t match;
- uint64_t player_id;
- GR_PLAYER_TOKEN token;
- grank_status_t grank_status;
- grank_status_t final_status; // status to set after cleanup
- uint32_t grank; // global rank
- char name[32];
-} ranked_player_t;
-
-static int s_rankings_contexts = 0;
-static qboolean s_rankings_active = qfalse;
-static GR_CONTEXT s_server_context = 0;
-static uint64_t s_server_match = 0;
-static char* s_rankings_game_key = NULL;
-static uint64_t s_rankings_game_id = 0;
-static ranked_player_t* s_ranked_players = NULL;
-static qboolean s_server_quitting = qfalse;
-static const char s_ascii_encoding[] =
- "0123456789abcdef"
- "ghijklmnopqrstuv"
- "wxyzABCDEFGHIJKL"
- "MNOPQRSTUVWXYZ[]";
-
-// private functions
-static void SV_RankNewGameCBF( GR_NEWGAME* gr_newgame, void* cbf_arg );
-static void SV_RankUserCBF( GR_LOGIN* gr_login, void* cbf_arg );
-static void SV_RankJoinGameCBF( GR_JOINGAME* gr_joingame, void* cbf_arg );
-static void SV_RankSendReportsCBF( GR_STATUS* gr_status, void* cbf_arg );
-static void SV_RankCleanupCBF( GR_STATUS* gr_status, void* cbf_arg );
-static void SV_RankCloseContext( ranked_player_t* ranked_player );
-static int SV_RankAsciiEncode( char* dest, const unsigned char* src,
- int src_len );
-static int SV_RankAsciiDecode( unsigned char* dest, const char* src,
- int src_len );
-static void SV_RankEncodeGameID( uint64_t game_id, char* result,
- int len );
-static uint64_t SV_RankDecodePlayerID( const char* string );
-static void SV_RankDecodePlayerKey( const char* string, GR_PLAYER_TOKEN key );
-static char* SV_RankStatusString( GR_STATUS status );
-static void SV_RankError( const char* fmt, ... );
-static char SV_RankGameKey[64];
-
-/*
-================
-SV_RankBegin
-================
-*/
-void SV_RankBegin( char *gamekey )
-{
- GR_INIT init;
- GR_STATUS status;
-
- assert( s_rankings_contexts == 0 );
- assert( !s_rankings_active );
- assert( s_ranked_players == NULL );
-
- if( sv_enableRankings->integer == 0 || Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER )
- {
- s_rankings_active = qfalse;
- if( sv_rankingsActive->integer == 1 )
- {
- Cvar_Set( "sv_rankingsActive", "0" );
- }
- return;
- }
-
- // only allow official game key on pure servers
- if( strcmp(gamekey, GR_GAMEKEY) == 0 )
- {
-/*
- if( Cvar_VariableValue("sv_pure") != 1 )
- {
- Cvar_Set( "sv_enableRankings", "0" );
- return;
- }
-*/
-
- // substitute game-specific game key
- switch( (int)Cvar_VariableValue("g_gametype") )
- {
- case GT_FFA:
- gamekey = "Q3 Free For All";
- break;
- case GT_TOURNAMENT:
- gamekey = "Q3 Tournament";
- break;
- case GT_TEAM:
- gamekey = "Q3 Team Deathmatch";
- break;
- case GT_CTF:
- gamekey = "Q3 Capture the Flag";
- break;
- case GT_1FCTF:
- gamekey = "Q3 One Flag CTF";
- break;
- case GT_OBELISK:
- gamekey = "Q3 Overload";
- break;
- case GT_HARVESTER:
- gamekey = "Q3 Harvester";
- break;
- default:
- break;
- }
- }
- s_rankings_game_key = gamekey;
-
- // initialize rankings
- GRankLogLevel( GRLOG_OFF );
- memset(SV_RankGameKey,0,sizeof(SV_RankGameKey));
- strncpy(SV_RankGameKey,gamekey,sizeof(SV_RankGameKey)-1);
- init = GRankInit( 1, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END );
- s_server_context = init.context;
- s_rankings_contexts++;
- Com_DPrintf( "SV_RankBegin(); GR_GAMEKEY is %s\n", gamekey );
- Com_DPrintf( "SV_RankBegin(); s_rankings_contexts=%d\n",s_rankings_contexts );
- Com_DPrintf( "SV_RankBegin(); s_server_context=%d\n",init.context );
-
- // new game
- if(!strlen(Cvar_VariableString( "sv_leagueName" )))
- {
- status = GRankNewGameAsync
- (
- s_server_context,
- SV_RankNewGameCBF,
- NULL,
- GR_OPT_LEAGUENAME,
- (void*)(Cvar_VariableString( "sv_leagueName" )),
- GR_OPT_END
- );
- }
- else
- {
- status = GRankNewGameAsync
- (
- s_server_context,
- SV_RankNewGameCBF,
- NULL,
- GR_OPT_END
- );
- }
-
- if( status != GR_STATUS_PENDING )
- {
- SV_RankError( "SV_RankBegin: Expected GR_STATUS_PENDING, got %s",
- SV_RankStatusString( status ) );
- return;
- }
-
- // logging
- if( com_developer->value )
- {
- GRankLogLevel( GRLOG_TRACE );
- }
-
- // allocate rankings info for each player
- s_ranked_players = Z_Malloc( sv_maxclients->value *
- sizeof(ranked_player_t) );
- memset( (void*)s_ranked_players, 0 ,sv_maxclients->value
- * sizeof(ranked_player_t));
-}
-
-/*
-================
-SV_RankEnd
-================
-*/
-void SV_RankEnd( void )
-{
- GR_STATUS status;
- int i;
-
- Com_DPrintf( "SV_RankEnd();\n" );
-
- if( !s_rankings_active )
- {
- // cleanup after error during game
- if( s_ranked_players != NULL )
- {
- for( i = 0; i < sv_maxclients->value; i++ )
- {
- if( s_ranked_players[i].context != 0 )
- {
- SV_RankCloseContext( &(s_ranked_players[i]) );
- }
- }
- }
- if( s_server_context != 0 )
- {
- SV_RankCloseContext( NULL );
- }
-
- return;
- }
-
- for( i = 0; i < sv_maxclients->value; i++ )
- {
- if( s_ranked_players[i].grank_status == QGR_STATUS_ACTIVE )
- {
- SV_RankUserLogout( i );
- Com_DPrintf( "SV_RankEnd: SV_RankUserLogout %d\n",i );
- }
- }
-
- assert( s_server_context != 0 );
-
- // send match reports, proceed to SV_RankSendReportsCBF
- status = GRankSendReportsAsync
- (
- s_server_context,
- 0,
- SV_RankSendReportsCBF,
- NULL,
- GR_OPT_END
- );
-
- if( status != GR_STATUS_PENDING )
- {
- SV_RankError( "SV_RankEnd: Expected GR_STATUS_PENDING, got %s",
- SV_RankStatusString( status ) );
- }
-
- s_rankings_active = qfalse;
- Cvar_Set( "sv_rankingsActive", "0" );
-}
-
-/*
-================
-SV_RankPoll
-================
-*/
-void SV_RankPoll( void )
-{
- GRankPoll();
-}
-
-/*
-================
-SV_RankCheckInit
-================
-*/
-qboolean SV_RankCheckInit( void )
-{
- return (s_rankings_contexts > 0);
-}
-
-/*
-================
-SV_RankActive
-================
-*/
-qboolean SV_RankActive( void )
-{
- return s_rankings_active;
-}
-
-/*
-=================
-SV_RankUserStatus
-=================
-*/
-grank_status_t SV_RankUserStatus( int index )
-{
- if( !s_rankings_active )
- {
- return GR_STATUS_ERROR;
- }
-
- assert( s_ranked_players != NULL );
- assert( index >= 0 );
- assert( index < sv_maxclients->value );
-
- return s_ranked_players[index].grank_status;
-}
-
-/*
-================
-SV_RankUserGRank
-================
-*/
-int SV_RankUserGrank( int index )
-{
- if( !s_rankings_active )
- {
- return 0;
- }
-
- assert( s_ranked_players != NULL );
- assert( index >= 0 );
- assert( index < sv_maxclients->value );
-
- return s_ranked_players[index].grank;
-}
-
-/*
-================
-SV_RankUserReset
-================
-*/
-void SV_RankUserReset( int index )
-{
- if( !s_rankings_active )
- {
- return;
- }
-
- assert( s_ranked_players != NULL );
- assert( index >= 0 );
- assert( index < sv_maxclients->value );
-
- switch( s_ranked_players[index].grank_status )
- {
- case QGR_STATUS_SPECTATOR:
- case QGR_STATUS_NO_USER:
- case QGR_STATUS_BAD_PASSWORD:
- case QGR_STATUS_USER_EXISTS:
- case QGR_STATUS_NO_MEMBERSHIP:
- case QGR_STATUS_TIMEOUT:
- case QGR_STATUS_ERROR:
- s_ranked_players[index].grank_status = QGR_STATUS_NEW;
- break;
- default:
- break;
- }
-}
-
-/*
-================
-SV_RankUserSpectate
-================
-*/
-void SV_RankUserSpectate( int index )
-{
- if( !s_rankings_active )
- {
- return;
- }
-
- assert( s_ranked_players != NULL );
- assert( index >= 0 );
- assert( index < sv_maxclients->value );
-
- // GRANK_FIXME - check current status?
- s_ranked_players[index].grank_status = QGR_STATUS_SPECTATOR;
-}
-
-/*
-================
-SV_RankUserCreate
-================
-*/
-void SV_RankUserCreate( int index, char* username, char* password,
- char* email )
-{
- GR_INIT init;
- GR_STATUS status;
-
- assert( index >= 0 );
- assert( index < sv_maxclients->value );
- assert( username != NULL );
- assert( password != NULL );
- assert( email != NULL );
- assert( s_ranked_players );
- assert( s_ranked_players[index].grank_status != QGR_STATUS_ACTIVE );
-
- Com_DPrintf( "SV_RankUserCreate( %d, %s, \"****\", %s );\n", index,
- username, email );
-
- if( !s_rankings_active )
- {
- Com_DPrintf( "SV_RankUserCreate: Not ready to create\n" );
- s_ranked_players[index].grank_status = QGR_STATUS_ERROR;
- return;
- }
-
- if( s_ranked_players[index].grank_status == QGR_STATUS_ACTIVE )
- {
- Com_DPrintf( "SV_RankUserCreate: Got Create from active player\n" );
- return;
- }
-
- // get a separate context for the new user
- init = GRankInit( 0, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END );
- s_ranked_players[index].context = init.context;
- s_rankings_contexts++;
- Com_DPrintf( "SV_RankUserCreate(); s_rankings_contexts=%d\n",s_rankings_contexts );
- Com_DPrintf( "SV_RankUserCreate(); s_ranked_players[%d].context=%d\n",index,init.context );
-
- // attempt to create a new account, proceed to SV_RankUserCBF
- status = GRankUserCreateAsync
- (
- s_ranked_players[index].context,
- username,
- password,
- email,
- SV_RankUserCBF,
- (void*)&s_ranked_players[index],
- GR_OPT_END
- );
-
- if( status == GR_STATUS_PENDING )
- {
- s_ranked_players[index].grank_status = QGR_STATUS_PENDING;
- s_ranked_players[index].final_status = QGR_STATUS_NEW;
- }
- else
- {
- SV_RankError( "SV_RankUserCreate: Expected GR_STATUS_PENDING, got %s",
- SV_RankStatusString( status ) );
- }
-}
-
-/*
-================
-SV_RankUserLogin
-================
-*/
-void SV_RankUserLogin( int index, char* username, char* password )
-{
- GR_INIT init;
- GR_STATUS status;
-
- assert( index >= 0 );
- assert( index < sv_maxclients->value );
- assert( username != NULL );
- assert( password != NULL );
- assert( s_ranked_players );
- assert( s_ranked_players[index].grank_status != QGR_STATUS_ACTIVE );
-
- Com_DPrintf( "SV_RankUserLogin( %d, %s, \"****\" );\n", index, username );
-
- if( !s_rankings_active )
- {
- Com_DPrintf( "SV_RankUserLogin: Not ready for login\n" );
- s_ranked_players[index].grank_status = QGR_STATUS_ERROR;
- return;
- }
-
- if( s_ranked_players[index].grank_status == QGR_STATUS_ACTIVE )
- {
- Com_DPrintf( "SV_RankUserLogin: Got Login from active player\n" );
- return;
- }
-
- // get a separate context for the new user
- init = GRankInit( 0, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END );
- s_ranked_players[index].context = init.context;
- s_rankings_contexts++;
- Com_DPrintf( "SV_RankUserLogin(); s_rankings_contexts=%d\n",s_rankings_contexts );
- Com_DPrintf( "SV_RankUserLogin(); s_ranked_players[%d].context=%d\n",index,init.context );
-
- // login user, proceed to SV_RankUserCBF
- status = GRankUserLoginAsync
- (
- s_ranked_players[index].context,
- username,
- password,
- SV_RankUserCBF,
- (void*)&s_ranked_players[index],
- GR_OPT_END
- );
-
- if( status == GR_STATUS_PENDING )
- {
- s_ranked_players[index].grank_status = QGR_STATUS_PENDING;
- s_ranked_players[index].final_status = QGR_STATUS_NEW;
- }
- else
- {
- SV_RankError( "SV_RankUserLogin: Expected GR_STATUS_PENDING, got %s",
- SV_RankStatusString( status ) );
- }
-}
-
-/*
-===================
-SV_RankUserValidate
-===================
-*/
-qboolean SV_RankUserValidate( int index, const char* player_id, const char* key, int token_len, int rank, char* name )
-{
- GR_INIT init;
- GR_STATUS status;
- qboolean rVal;
- ranked_player_t* ranked_player;
- int i;
-
- assert( s_ranked_players );
- assert( s_ranked_players[index].grank_status != QGR_STATUS_ACTIVE );
-
- rVal = qfalse;
-
- if( !s_rankings_active )
- {
- Com_DPrintf( "SV_RankUserValidate: Not ready to validate\n" );
- s_ranked_players[index].grank_status = QGR_STATUS_ERROR;
- return rVal;
- }
-
- ranked_player = &(s_ranked_players[index]);
-
- if ( (player_id != NULL) && (key != NULL))
- {
- // the real player_id and key is set when SV_RankJoinGameCBF
- // is called we do this so that SV_RankUserValidate
- // can be shared by both server side login and client side login
-
- // for client side logined in players
- // server is creating GR_OPT_PLAYERCONTEXT
- init = GRankInit( 0, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END );
- ranked_player->context = init.context;
- s_rankings_contexts++;
- Com_DPrintf( "SV_RankUserValidate(); s_rankings_contexts=%d\n",s_rankings_contexts );
- Com_DPrintf( "SV_RankUserValidate(); s_ranked_players[%d].context=%d\n",index,init.context );
-
- // uudecode player id and player token
- ranked_player->player_id = SV_RankDecodePlayerID(player_id);
- Com_DPrintf( "SV_RankUserValidate(); ranked_player->player_id =%u\n", (uint32_t)ranked_player->player_id );
- SV_RankDecodePlayerKey(key, ranked_player->token);
-
- // save name and check for duplicates
- Q_strncpyz( ranked_player->name, name, sizeof(ranked_player->name) );
- for( i = 0; i < sv_maxclients->value; i++ )
- {
- if( (i != index) && (s_ranked_players[i].grank_status == QGR_STATUS_ACTIVE) &&
- (strcmp( s_ranked_players[i].name, name ) == 0) )
- {
- Com_DPrintf( "SV_RankUserValidate: Duplicate login\n" );
- ranked_player->grank_status = QGR_STATUS_NO_USER;
- ranked_player->final_status = QGR_STATUS_NEW;
- ranked_player->grank = 0;
- return qfalse;
- }
- }
-
- // then validate
- status = GRankPlayerValidate(
- s_server_context,
- ranked_player->player_id,
- ranked_player->token,
- token_len,
- GR_OPT_PLAYERCONTEXT,
- ranked_player->context,
- GR_OPT_END);
- }
- else
- {
- // make server side login (bots) happy
- status = GR_STATUS_OK;
- }
-
- if (status == GR_STATUS_OK)
- {
- ranked_player->grank_status = QGR_STATUS_ACTIVE;
- ranked_player->final_status = QGR_STATUS_NEW;
- ranked_player->grank = rank;
- rVal = qtrue;
- }
- else if (status == GR_STATUS_INVALIDUSER)
- {
- ranked_player->grank_status = QGR_STATUS_INVALIDUSER;
- ranked_player->final_status = QGR_STATUS_NEW;
- ranked_player->grank = 0;
- rVal = qfalse;
- }
- else
- {
- SV_RankError( "SV_RankUserValidate: Unexpected status %s",
- SV_RankStatusString( status ) );
- s_ranked_players[index].grank_status = QGR_STATUS_ERROR;
- ranked_player->grank = 0;
- }
-
- return rVal;
-}
-
-/*
-================
-SV_RankUserLogout
-================
-*/
-void SV_RankUserLogout( int index )
-{
- GR_STATUS status;
- GR_STATUS cleanup_status;
-
- if( !s_rankings_active )
- {
- return;
- }
-
- assert( index >= 0 );
- assert( index < sv_maxclients->value );
- assert( s_ranked_players );
-
- if( s_ranked_players[index].context == 0 ) {
- return;
- }
-
- Com_DPrintf( "SV_RankUserLogout( %d );\n", index );
-
- // masqueraded player may not be active yet, if they fail validation,
- // but still they have a context needs to be cleaned
- // what matters is the s_ranked_players[index].context
-
- // send reports, proceed to SV_RankSendReportsCBF
- status = GRankSendReportsAsync
- (
- s_ranked_players[index].context,
- 0,
- SV_RankSendReportsCBF,
- (void*)&s_ranked_players[index],
- GR_OPT_END
- );
-
- if( status == GR_STATUS_PENDING )
- {
- s_ranked_players[index].grank_status = QGR_STATUS_PENDING;
- s_ranked_players[index].final_status = QGR_STATUS_NEW;
- }
- else
- {
- SV_RankError( "SV_RankUserLogout: Expected GR_STATUS_PENDING, got %s",
- SV_RankStatusString( status ) );
-
- cleanup_status = GRankCleanupAsync
- (
- s_ranked_players[index].context,
- 0,
- SV_RankCleanupCBF,
- (void*)&s_ranked_players[index],
- GR_OPT_END
- );
-
- if( cleanup_status != GR_STATUS_PENDING )
- {
- SV_RankError( "SV_RankUserLogout: Expected "
- "GR_STATUS_PENDING from GRankCleanupAsync, got %s",
- SV_RankStatusString( cleanup_status ) );
- SV_RankCloseContext( &(s_ranked_players[index]) );
- }
- }
-}
-
-/*
-================
-SV_RankReportInt
-================
-*/
-void SV_RankReportInt( int index1, int index2, int key, int value,
- qboolean accum )
-{
- GR_STATUS status;
- GR_CONTEXT context;
- uint64_t match;
- uint64_t user1;
- uint64_t user2;
- int opt_accum;
-
- if( !s_rankings_active )
- {
- return;
- }
-
- assert( index1 >= -1 );
- assert( index1 < sv_maxclients->value );
- assert( index2 >= -1 );
- assert( index2 < sv_maxclients->value );
- assert( s_ranked_players );
-
-// Com_DPrintf( "SV_RankReportInt( %d, %d, %d, %d, %d );\n", index1, index2,
-// key, value, accum );
-
- // get context, match, and player_id for player index1
- if( index1 == -1 )
- {
- context = s_server_context;
- match = s_server_match;
- user1 = 0;
- }
- else
- {
- if( s_ranked_players[index1].grank_status != QGR_STATUS_ACTIVE )
- {
- Com_DPrintf( "SV_RankReportInt: Expecting QGR_STATUS_ACTIVE"
- " Got Unexpected status %d for player %d\n",
- s_ranked_players[index1].grank_status, index1 );
- return;
- }
-
- context = s_ranked_players[index1].context;
- match = s_ranked_players[index1].match;
- user1 = s_ranked_players[index1].player_id;
- }
-
- // get player_id for player index2
- if( index2 == -1 )
- {
- user2 = 0;
- }
- else
- {
- if( s_ranked_players[index2].grank_status != QGR_STATUS_ACTIVE )
- {
- Com_DPrintf( "SV_RankReportInt: Expecting QGR_STATUS_ACTIVE"
- " Got Unexpected status %d for player %d\n",
- s_ranked_players[index2].grank_status, index2 );
- return;
- }
-
- user2 = s_ranked_players[index2].player_id;
- }
-
- opt_accum = accum ? GR_OPT_ACCUM : GR_OPT_END;
-
- status = GRankReportInt
- (
- context,
- match,
- user1,
- user2,
- key,
- value,
- opt_accum,
- GR_OPT_END
- );
-
- if( status != GR_STATUS_OK )
- {
- SV_RankError( "SV_RankReportInt: Unexpected status %s",
- SV_RankStatusString( status ) );
- }
-
- if( user2 != 0 )
- {
- context = s_ranked_players[index2].context;
- match = s_ranked_players[index2].match;
-
- status = GRankReportInt
- (
- context,
- match,
- user1,
- user2,
- key,
- value,
- opt_accum,
- GR_OPT_END
- );
-
- if( status != GR_STATUS_OK )
- {
- SV_RankError( "SV_RankReportInt: Unexpected status %s",
- SV_RankStatusString( status ) );
- }
- }
-}
-
-/*
-================
-SV_RankReportStr
-================
-*/
-void SV_RankReportStr( int index1, int index2, int key, char* value )
-{
- GR_STATUS status;
- GR_CONTEXT context;
- uint64_t match;
- uint64_t user1;
- uint64_t user2;
-
- if( !s_rankings_active )
- {
- return;
- }
-
- assert( index1 >= -1 );
- assert( index1 < sv_maxclients->value );
- assert( index2 >= -1 );
- assert( index2 < sv_maxclients->value );
- assert( s_ranked_players );
-
-// Com_DPrintf( "SV_RankReportStr( %d, %d, %d, \"%s\" );\n", index1, index2,
-// key, value );
-
- // get context, match, and player_id for player index1
- if( index1 == -1 )
- {
- context = s_server_context;
- match = s_server_match;
- user1 = 0;
- }
- else
- {
- if( s_ranked_players[index1].grank_status != QGR_STATUS_ACTIVE )
- {
- Com_DPrintf( "SV_RankReportStr: Unexpected status %d\n",
- s_ranked_players[index1].grank_status );
- return;
- }
-
- context = s_ranked_players[index1].context;
- match = s_ranked_players[index1].match;
- user1 = s_ranked_players[index1].player_id;
- }
-
- // get player_id for player index2
- if( index2 == -1 )
- {
- user2 = 0;
- }
- else
- {
- if( s_ranked_players[index2].grank_status != QGR_STATUS_ACTIVE )
- {
- Com_DPrintf( "SV_RankReportStr: Unexpected status %d\n",
- s_ranked_players[index2].grank_status );
- return;
- }
-
- user2 = s_ranked_players[index2].player_id;
- }
-
- status = GRankReportStr
- (
- context,
- match,
- user1,
- user2,
- key,
- value,
- GR_OPT_END
- );
-
- if( status != GR_STATUS_OK )
- {
- SV_RankError( "SV_RankReportStr: Unexpected status %s",
- SV_RankStatusString( status ) );
- }
-
- if( user2 != 0 )
- {
- context = s_ranked_players[index2].context;
- match = s_ranked_players[index2].match;
-
- status = GRankReportStr
- (
- context,
- match,
- user1,
- user2,
- key,
- value,
- GR_OPT_END
- );
-
- if( status != GR_STATUS_OK )
- {
- SV_RankError( "SV_RankReportInt: Unexpected status %s",
- SV_RankStatusString( status ) );
- }
- }
-}
-
-/*
-================
-SV_RankQuit
-================
-*/
-void SV_RankQuit( void )
-{
- int i;
- int j = 0;
- // yuck
-
- while( s_rankings_contexts > 1 )
- {
- assert(s_ranked_players);
- if( s_ranked_players != NULL )
- {
- for( i = 0; i < sv_maxclients->value; i++ )
- {
- // check for players that weren't yet active in SV_RankEnd
- if( s_ranked_players[i].grank_status == QGR_STATUS_ACTIVE )
- {
- SV_RankUserLogout( i );
- Com_DPrintf( "SV_RankQuit: SV_RankUserLogout %d\n",i );
- }
- else
- {
- if( s_ranked_players[i].context )
- {
- GR_STATUS cleanup_status;
- cleanup_status = GRankCleanupAsync
- (
- s_ranked_players[i].context,
- 0,
- SV_RankCleanupCBF,
- (void*)&(s_ranked_players[i]),
- GR_OPT_END
- );
-
- if( cleanup_status != GR_STATUS_PENDING )
- {
- SV_RankError( "SV_RankQuit: Expected "
- "GR_STATUS_PENDING from GRankCleanupAsync, got %s",
- SV_RankStatusString( cleanup_status ) );
- }
- }
- }
- }
- }
- SV_RankPoll();
-
- // should've finished by now
- assert( (j++) < 68 );
- }
-}
-
-/*
-==============================================================================
-
-Private Functions
-
-==============================================================================
-*/
-
-/*
-=================
-SV_RankNewGameCBF
-=================
-*/
-static void SV_RankNewGameCBF( GR_NEWGAME* gr_newgame, void* cbf_arg )
-{
- GR_MATCH match;
- int i;
-
- assert( gr_newgame != NULL );
- assert( cbf_arg == NULL );
-
- Com_DPrintf( "SV_RankNewGameCBF( %08X, %08X );\n", gr_newgame, cbf_arg );
-
- if( gr_newgame->status == GR_STATUS_OK )
- {
- char info[MAX_INFO_STRING];
- char gameid[sizeof(s_ranked_players[i].game_id) * 4 / 3 + 2];
-
- // save game id
- s_rankings_game_id = gr_newgame->game_id;
-
- // encode gameid
- memset(gameid,0,sizeof(gameid));
- SV_RankEncodeGameID(s_rankings_game_id,gameid,sizeof(gameid));
-
- // set CS_GRANK rankingsGameID to pass to client
- memset(info,0,sizeof(info));
- Info_SetValueForKey( info, "rankingsGameKey", s_rankings_game_key );
- Info_SetValueForKey( info, "rankingsGameID", gameid );
- SV_SetConfigstring( CS_GRANK, info );
-
- // initialize client status
- for( i = 0; i < sv_maxclients->value; i++ )
- s_ranked_players[i].grank_status = QGR_STATUS_NEW;
-
- // start new match
- match = GRankStartMatch( s_server_context );
- s_server_match = match.match;
-
- // ready to go
- s_rankings_active = qtrue;
- Cvar_Set( "sv_rankingsActive", "1" );
-
- }
- else if( gr_newgame->status == GR_STATUS_BADLEAGUE )
- {
- SV_RankError( "SV_RankNewGameCBF: Invalid League name\n" );
- }
- else
- {
- //GRank handle new game failure
- // force SV_RankEnd() to run
- //SV_RankEnd();
- SV_RankError( "SV_RankNewGameCBF: Unexpected status %s",
- SV_RankStatusString( gr_newgame->status ) );
- }
-}
-
-/*
-================
-SV_RankUserCBF
-================
-*/
-static void SV_RankUserCBF( GR_LOGIN* gr_login, void* cbf_arg )
-{
- ranked_player_t* ranked_player;
- GR_STATUS join_status;
- GR_STATUS cleanup_status;
-
- assert( gr_login != NULL );
- assert( cbf_arg != NULL );
-
- Com_DPrintf( "SV_RankUserCBF( %08X, %08X );\n", gr_login, cbf_arg );
-
- ranked_player = (ranked_player_t*)cbf_arg;
- assert(ranked_player);
- assert( ranked_player->context );
-
- switch( gr_login->status )
- {
- case GR_STATUS_OK:
- // attempt to join the game, proceed to SV_RankJoinGameCBF
- join_status = GRankJoinGameAsync
- (
- ranked_player->context,
- s_rankings_game_id,
- SV_RankJoinGameCBF,
- cbf_arg,
- GR_OPT_END
- );
-
- if( join_status != GR_STATUS_PENDING )
- {
- SV_RankError( "SV_RankUserCBF: Expected GR_STATUS_PENDING "
- "from GRankJoinGameAsync, got %s",
- SV_RankStatusString( join_status ) );
- }
- break;
- case GR_STATUS_NOUSER:
- Com_DPrintf( "SV_RankUserCBF: Got status %s\n",
- SV_RankStatusString( gr_login->status ) );
- ranked_player->final_status = QGR_STATUS_NO_USER;
- break;
- case GR_STATUS_BADPASSWORD:
- Com_DPrintf( "SV_RankUserCBF: Got status %s\n",
- SV_RankStatusString( gr_login->status ) );
- ranked_player->final_status = QGR_STATUS_BAD_PASSWORD;
- break;
- case GR_STATUS_TIMEOUT:
- Com_DPrintf( "SV_RankUserCBF: Got status %s\n",
- SV_RankStatusString( gr_login->status ) );
- ranked_player->final_status = QGR_STATUS_TIMEOUT;
- break;
- default:
- Com_DPrintf( "SV_RankUserCBF: Unexpected status %s\n",
- SV_RankStatusString( gr_login->status ) );
- ranked_player->final_status = QGR_STATUS_ERROR;
- break;
- }
-
- if( ranked_player->final_status != QGR_STATUS_NEW )
- {
- // login or create failed, so clean up before the next attempt
- cleanup_status = GRankCleanupAsync
- (
- ranked_player->context,
- 0,
- SV_RankCleanupCBF,
- (void*)ranked_player,
- GR_OPT_END
- );
-
- if( cleanup_status != GR_STATUS_PENDING )
- {
- SV_RankError( "SV_RankUserCBF: Expected GR_STATUS_PENDING "
- "from GRankCleanupAsync, got %s",
- SV_RankStatusString( cleanup_status ) );
- SV_RankCloseContext( ranked_player );
- }
- }
-}
-
-/*
-================
-SV_RankJoinGameCBF
-================
-*/
-static void SV_RankJoinGameCBF( GR_JOINGAME* gr_joingame, void* cbf_arg )
-{
- ranked_player_t* ranked_player;
- GR_MATCH match;
- GR_STATUS cleanup_status;
-
- assert( gr_joingame != NULL );
- assert( cbf_arg != NULL );
-
- Com_DPrintf( "SV_RankJoinGameCBF( %08X, %08X );\n", gr_joingame, cbf_arg );
-
- ranked_player = (ranked_player_t*)cbf_arg;
-
- assert( ranked_player );
- assert( ranked_player->context != 0 );
-
- if( gr_joingame->status == GR_STATUS_OK )
- {
- int i;
- // save user id
- ranked_player->player_id = gr_joingame->player_id;
- memcpy(ranked_player->token,gr_joingame->token,
- sizeof(GR_PLAYER_TOKEN)) ;
- match = GRankStartMatch( ranked_player->context );
- ranked_player->match = match.match;
- ranked_player->grank = gr_joingame->rank;
-
- // find the index and call SV_RankUserValidate
- for (i=0;i<sv_maxclients->value;i++)
- if ( ranked_player == &s_ranked_players[i] )
- SV_RankUserValidate(i,NULL,NULL,0, gr_joingame->rank,ranked_player->name);
- }
- else
- {
- //GRand handle join game failure
- SV_RankError( "SV_RankJoinGameCBF: Unexpected status %s",
- SV_RankStatusString( gr_joingame->status ) );
-
- cleanup_status = GRankCleanupAsync
- (
- ranked_player->context,
- 0,
- SV_RankCleanupCBF,
- cbf_arg,
- GR_OPT_END
- );
-
- if( cleanup_status != GR_STATUS_PENDING )
- {
- SV_RankError( "SV_RankJoinGameCBF: Expected "
- "GR_STATUS_PENDING from GRankCleanupAsync, got %s",
- SV_RankStatusString( cleanup_status ) );
- SV_RankCloseContext( ranked_player );
- }
- }
-}
-
-/*
-================
-SV_RankSendReportsCBF
-================
-*/
-static void SV_RankSendReportsCBF( GR_STATUS* status, void* cbf_arg )
-{
- ranked_player_t* ranked_player;
- GR_CONTEXT context;
- GR_STATUS cleanup_status;
-
- assert( status != NULL );
- // NULL cbf_arg means server is sending match reports
-
- Com_DPrintf( "SV_RankSendReportsCBF( %08X, %08X );\n", status, cbf_arg );
-
- ranked_player = (ranked_player_t*)cbf_arg;
- if( ranked_player == NULL )
- {
- Com_DPrintf( "SV_RankSendReportsCBF: server\n" );
- context = s_server_context;
- }
- else
- {
- Com_DPrintf( "SV_RankSendReportsCBF: player\n" );
- context = ranked_player->context;
- }
-
- //assert( context != 0 );
- if( *status != GR_STATUS_OK )
- {
- SV_RankError( "SV_RankSendReportsCBF: Unexpected status %s",
- SV_RankStatusString( *status ) );
- }
-
- if( context == 0 )
- {
- Com_DPrintf( "SV_RankSendReportsCBF: WARNING: context == 0" );
- SV_RankCloseContext( ranked_player );
- }
- else
- {
- cleanup_status = GRankCleanupAsync
- (
- context,
- 0,
- SV_RankCleanupCBF,
- cbf_arg,
- GR_OPT_END
- );
-
- if( cleanup_status != GR_STATUS_PENDING )
- {
- SV_RankError( "SV_RankSendReportsCBF: Expected "
- "GR_STATUS_PENDING from GRankCleanupAsync, got %s",
- SV_RankStatusString( cleanup_status ) );
- SV_RankCloseContext( ranked_player );
- }
- }
-}
-
-/*
-================
-SV_RankCleanupCBF
-================
-*/
-static void SV_RankCleanupCBF( GR_STATUS* status, void* cbf_arg )
-{
- ranked_player_t* ranked_player;
- ranked_player = (ranked_player_t*)cbf_arg;
-
- assert( status != NULL );
- // NULL cbf_arg means server is cleaning up
-
- Com_DPrintf( "SV_RankCleanupCBF( %08X, %08X );\n", status, cbf_arg );
-
- if( *status != GR_STATUS_OK )
- {
- SV_RankError( "SV_RankCleanupCBF: Unexpected status %s",
- SV_RankStatusString( *status ) );
- }
-
- SV_RankCloseContext( ranked_player );
-}
-
-/*
-================
-SV_RankCloseContext
-================
-*/
-static void SV_RankCloseContext( ranked_player_t* ranked_player )
-{
- if( ranked_player == NULL )
- {
- // server cleanup
- if( s_server_context == 0 )
- {
- return;
- }
- s_server_context = 0;
- s_server_match = 0;
- }
- else
- {
- // player cleanup
- if( s_ranked_players == NULL )
- {
- return;
- }
- if( ranked_player->context == 0 )
- {
- return;
- }
- ranked_player->context = 0;
- ranked_player->match = 0;
- ranked_player->player_id = 0;
- memset( ranked_player->token, 0, sizeof(GR_PLAYER_TOKEN) );
- ranked_player->grank_status = ranked_player->final_status;
- ranked_player->final_status = QGR_STATUS_NEW;
- ranked_player->name[0] = '\0';
- }
-
- assert( s_rankings_contexts > 0 );
- s_rankings_contexts--;
- Com_DPrintf( "SV_RankCloseContext: s_rankings_contexts = %d\n",
- s_rankings_contexts );
-
- if( s_rankings_contexts == 0 )
- {
- GRankLogLevel( GRLOG_OFF );
-
- if( s_ranked_players != NULL )
- {
- Z_Free( s_ranked_players );
- s_ranked_players = NULL;
- }
-
- s_rankings_active = qfalse;
- Cvar_Set( "sv_rankingsActive", "0" );
- }
-}
-
-/*
-================
-SV_RankAsciiEncode
-
-Encodes src_len bytes of binary data from the src buffer as ASCII text,
-using 6 bits per character. The result string is null-terminated and
-stored in the dest buffer.
-
-The dest buffer must be at least (src_len * 4) / 3 + 2 bytes in length.
-
-Returns the length of the result string, not including the null.
-================
-*/
-static int SV_RankAsciiEncode( char* dest, const unsigned char* src,
- int src_len )
-{
- unsigned char bin[3];
- unsigned char txt[4];
- int dest_len = 0;
- int i;
- int j;
- int num_chars;
-
- assert( dest != NULL );
- assert( src != NULL );
-
- for( i = 0; i < src_len; i += 3 )
- {
- // read three bytes of input
- for( j = 0; j < 3; j++ )
- {
- bin[j] = (i + j < src_len) ? src[i + j] : 0;
- }
-
- // get four 6-bit values from three bytes
- txt[0] = bin[0] >> 2;
- txt[1] = ((bin[0] << 4) | (bin[1] >> 4)) & 63;
- txt[2] = ((bin[1] << 2) | (bin[2] >> 6)) & 63;
- txt[3] = bin[2] & 63;
-
- // store ASCII encoding of 6-bit values
- num_chars = (i + 2 < src_len) ? 4 : ((src_len - i) * 4) / 3 + 1;
- for( j = 0; j < num_chars; j++ )
- {
- dest[dest_len++] = s_ascii_encoding[txt[j]];
- }
- }
-
- dest[dest_len] = '\0';
-
- return dest_len;
-}
-
-/*
-================
-SV_RankAsciiDecode
-
-Decodes src_len characters of ASCII text from the src buffer, stores
-the binary result in the dest buffer.
-
-The dest buffer must be at least (src_len * 3) / 4 bytes in length.
-
-Returns the length of the binary result, or zero for invalid input.
-================
-*/
-static int SV_RankAsciiDecode( unsigned char* dest, const char* src,
- int src_len )
-{
- static unsigned char s_inverse_encoding[256];
- static char s_init = 0;
-
- unsigned char bin[3];
- unsigned char txt[4];
- int dest_len = 0;
- int i;
- int j;
- int num_bytes;
-
- assert( dest != NULL );
- assert( src != NULL );
-
- if( !s_init )
- {
- // initialize lookup table for decoding
- memset( s_inverse_encoding, 255, sizeof(s_inverse_encoding) );
- for( i = 0; i < 64; i++ )
- {
- s_inverse_encoding[s_ascii_encoding[i]] = i;
- }
- s_init = 1;
- }
-
- for( i = 0; i < src_len; i += 4 )
- {
- // read four characters of input, decode them to 6-bit values
- for( j = 0; j < 4; j++ )
- {
- txt[j] = (i + j < src_len) ? s_inverse_encoding[src[i + j]] : 0;
- if (txt[j] == 255)
- {
- return 0; // invalid input character
- }
- }
-
- // get three bytes from four 6-bit values
- bin[0] = (txt[0] << 2) | (txt[1] >> 4);
- bin[1] = (txt[1] << 4) | (txt[2] >> 2);
- bin[2] = (txt[2] << 6) | txt[3];
-
- // store binary data
- num_bytes = (i + 3 < src_len) ? 3 : ((src_len - i) * 3) / 4;
- for( j = 0; j < num_bytes; j++ )
- {
- dest[dest_len++] = bin[j];
- }
- }
-
- return dest_len;
-}
-
-/*
-================
-SV_RankEncodeGameID
-================
-*/
-static void SV_RankEncodeGameID( uint64_t game_id, char* result,
- int len )
-{
- assert( result != NULL );
-
- if( len < ( ( sizeof(game_id) * 4) / 3 + 2) )
- {
- Com_DPrintf( "SV_RankEncodeGameID: result buffer too small\n" );
- result[0] = '\0';
- }
- else
- {
- qint64 gameid = LittleLong64(*(qint64*)&game_id);
- SV_RankAsciiEncode( result, (unsigned char*)&gameid,
- sizeof(qint64) );
- }
-}
-
-/*
-================
-SV_RankDecodePlayerID
-================
-*/
-static uint64_t SV_RankDecodePlayerID( const char* string )
-{
- unsigned char buffer[9];
- int len;
- qint64 player_id;
-
- assert( string != NULL );
-
- len = strlen (string) ;
- Com_DPrintf( "SV_RankDecodePlayerID: string length %d\n",len );
- SV_RankAsciiDecode( buffer, string, len );
- player_id = LittleLong64(*(qint64*)buffer);
- return *(uint64_t*)&player_id;
-}
-
-/*
-================
-SV_RankDecodePlayerKey
-================
-*/
-static void SV_RankDecodePlayerKey( const char* string, GR_PLAYER_TOKEN key )
-{
- unsigned char buffer[1400];
- int len;
- assert( string != NULL );
-
- len = strlen (string) ;
- Com_DPrintf( "SV_RankDecodePlayerKey: string length %d\n",len );
-
- memset(key,0,sizeof(GR_PLAYER_TOKEN));
- memset(buffer,0,sizeof(buffer));
- memcpy( key, buffer, SV_RankAsciiDecode( buffer, string, len ) );
-}
-
-/*
-================
-SV_RankStatusString
-================
-*/
-static char* SV_RankStatusString( GR_STATUS status )
-{
- switch( status )
- {
- case GR_STATUS_OK: return "GR_STATUS_OK";
- case GR_STATUS_ERROR: return "GR_STATUS_ERROR";
- case GR_STATUS_BADPARAMS: return "GR_STATUS_BADPARAMS";
- case GR_STATUS_NETWORK: return "GR_STATUS_NETWORK";
- case GR_STATUS_NOUSER: return "GR_STATUS_NOUSER";
- case GR_STATUS_BADPASSWORD: return "GR_STATUS_BADPASSWORD";
- case GR_STATUS_BADGAME: return "GR_STATUS_BADGAME";
- case GR_STATUS_PENDING: return "GR_STATUS_PENDING";
- case GR_STATUS_BADDOMAIN: return "GR_STATUS_BADDOMAIN";
- case GR_STATUS_DOMAINLOCK: return "GR_STATUS_DOMAINLOCK";
- case GR_STATUS_TIMEOUT: return "GR_STATUS_TIMEOUT";
- case GR_STATUS_INVALIDUSER: return "GR_STATUS_INVALIDUSER";
- case GR_STATUS_INVALIDCONTEXT: return "GR_STATUS_INVALIDCONTEXT";
- default: return "(UNKNOWN)";
- }
-}
-
-/*
-================
-SV_RankError
-================
-*/
-static void SV_RankError( const char* fmt, ... )
-{
- va_list arg_ptr;
- char text[1024];
-
- va_start( arg_ptr, fmt );
- vsprintf( text, fmt, arg_ptr );
- va_end( arg_ptr );
-
- Com_DPrintf( "****************************************\n" );
- Com_DPrintf( "SV_RankError: %s\n", text );
- Com_DPrintf( "****************************************\n" );
-
- s_rankings_active = qfalse;
- Cvar_Set( "sv_rankingsActive", "0" );
- // FIXME - attempt clean shutdown?
-}
-
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+// sv_rankings.c -- global rankings interface
+
+#include "server.h"
+#include "..\rankings\1.0\gr\grapi.h"
+#include "..\rankings\1.0\gr\grlog.h"
+
+typedef struct
+{
+ GR_CONTEXT context;
+ uint64_t game_id;
+ uint64_t match;
+ uint64_t player_id;
+ GR_PLAYER_TOKEN token;
+ grank_status_t grank_status;
+ grank_status_t final_status; // status to set after cleanup
+ uint32_t grank; // global rank
+ char name[32];
+} ranked_player_t;
+
+static int s_rankings_contexts = 0;
+static qboolean s_rankings_active = qfalse;
+static GR_CONTEXT s_server_context = 0;
+static uint64_t s_server_match = 0;
+static char* s_rankings_game_key = NULL;
+static uint64_t s_rankings_game_id = 0;
+static ranked_player_t* s_ranked_players = NULL;
+static qboolean s_server_quitting = qfalse;
+static const char s_ascii_encoding[] =
+ "0123456789abcdef"
+ "ghijklmnopqrstuv"
+ "wxyzABCDEFGHIJKL"
+ "MNOPQRSTUVWXYZ[]";
+
+// private functions
+static void SV_RankNewGameCBF( GR_NEWGAME* gr_newgame, void* cbf_arg );
+static void SV_RankUserCBF( GR_LOGIN* gr_login, void* cbf_arg );
+static void SV_RankJoinGameCBF( GR_JOINGAME* gr_joingame, void* cbf_arg );
+static void SV_RankSendReportsCBF( GR_STATUS* gr_status, void* cbf_arg );
+static void SV_RankCleanupCBF( GR_STATUS* gr_status, void* cbf_arg );
+static void SV_RankCloseContext( ranked_player_t* ranked_player );
+static int SV_RankAsciiEncode( char* dest, const unsigned char* src,
+ int src_len );
+static int SV_RankAsciiDecode( unsigned char* dest, const char* src,
+ int src_len );
+static void SV_RankEncodeGameID( uint64_t game_id, char* result,
+ int len );
+static uint64_t SV_RankDecodePlayerID( const char* string );
+static void SV_RankDecodePlayerKey( const char* string, GR_PLAYER_TOKEN key );
+static char* SV_RankStatusString( GR_STATUS status );
+static void SV_RankError( const char* fmt, ... );
+static char SV_RankGameKey[64];
+
+/*
+================
+SV_RankBegin
+================
+*/
+void SV_RankBegin( char *gamekey )
+{
+ GR_INIT init;
+ GR_STATUS status;
+
+ assert( s_rankings_contexts == 0 );
+ assert( !s_rankings_active );
+ assert( s_ranked_players == NULL );
+
+ if( sv_enableRankings->integer == 0 || Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER )
+ {
+ s_rankings_active = qfalse;
+ if( sv_rankingsActive->integer == 1 )
+ {
+ Cvar_Set( "sv_rankingsActive", "0" );
+ }
+ return;
+ }
+
+ // only allow official game key on pure servers
+ if( strcmp(gamekey, GR_GAMEKEY) == 0 )
+ {
+/*
+ if( Cvar_VariableValue("sv_pure") != 1 )
+ {
+ Cvar_Set( "sv_enableRankings", "0" );
+ return;
+ }
+*/
+
+ // substitute game-specific game key
+ switch( (int)Cvar_VariableValue("g_gametype") )
+ {
+ case GT_FFA:
+ gamekey = "Q3 Free For All";
+ break;
+ case GT_TOURNAMENT:
+ gamekey = "Q3 Tournament";
+ break;
+ case GT_TEAM:
+ gamekey = "Q3 Team Deathmatch";
+ break;
+ case GT_CTF:
+ gamekey = "Q3 Capture the Flag";
+ break;
+ case GT_1FCTF:
+ gamekey = "Q3 One Flag CTF";
+ break;
+ case GT_OBELISK:
+ gamekey = "Q3 Overload";
+ break;
+ case GT_HARVESTER:
+ gamekey = "Q3 Harvester";
+ break;
+ default:
+ break;
+ }
+ }
+ s_rankings_game_key = gamekey;
+
+ // initialize rankings
+ GRankLogLevel( GRLOG_OFF );
+ memset(SV_RankGameKey,0,sizeof(SV_RankGameKey));
+ strncpy(SV_RankGameKey,gamekey,sizeof(SV_RankGameKey)-1);
+ init = GRankInit( 1, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END );
+ s_server_context = init.context;
+ s_rankings_contexts++;
+ Com_DPrintf( "SV_RankBegin(); GR_GAMEKEY is %s\n", gamekey );
+ Com_DPrintf( "SV_RankBegin(); s_rankings_contexts=%d\n",s_rankings_contexts );
+ Com_DPrintf( "SV_RankBegin(); s_server_context=%d\n",init.context );
+
+ // new game
+ if(!strlen(Cvar_VariableString( "sv_leagueName" )))
+ {
+ status = GRankNewGameAsync
+ (
+ s_server_context,
+ SV_RankNewGameCBF,
+ NULL,
+ GR_OPT_LEAGUENAME,
+ (void*)(Cvar_VariableString( "sv_leagueName" )),
+ GR_OPT_END
+ );
+ }
+ else
+ {
+ status = GRankNewGameAsync
+ (
+ s_server_context,
+ SV_RankNewGameCBF,
+ NULL,
+ GR_OPT_END
+ );
+ }
+
+ if( status != GR_STATUS_PENDING )
+ {
+ SV_RankError( "SV_RankBegin: Expected GR_STATUS_PENDING, got %s",
+ SV_RankStatusString( status ) );
+ return;
+ }
+
+ // logging
+ if( com_developer->value )
+ {
+ GRankLogLevel( GRLOG_TRACE );
+ }
+
+ // allocate rankings info for each player
+ s_ranked_players = Z_Malloc( sv_maxclients->value *
+ sizeof(ranked_player_t) );
+ memset( (void*)s_ranked_players, 0 ,sv_maxclients->value
+ * sizeof(ranked_player_t));
+}
+
+/*
+================
+SV_RankEnd
+================
+*/
+void SV_RankEnd( void )
+{
+ GR_STATUS status;
+ int i;
+
+ Com_DPrintf( "SV_RankEnd();\n" );
+
+ if( !s_rankings_active )
+ {
+ // cleanup after error during game
+ if( s_ranked_players != NULL )
+ {
+ for( i = 0; i < sv_maxclients->value; i++ )
+ {
+ if( s_ranked_players[i].context != 0 )
+ {
+ SV_RankCloseContext( &(s_ranked_players[i]) );
+ }
+ }
+ }
+ if( s_server_context != 0 )
+ {
+ SV_RankCloseContext( NULL );
+ }
+
+ return;
+ }
+
+ for( i = 0; i < sv_maxclients->value; i++ )
+ {
+ if( s_ranked_players[i].grank_status == QGR_STATUS_ACTIVE )
+ {
+ SV_RankUserLogout( i );
+ Com_DPrintf( "SV_RankEnd: SV_RankUserLogout %d\n",i );
+ }
+ }
+
+ assert( s_server_context != 0 );
+
+ // send match reports, proceed to SV_RankSendReportsCBF
+ status = GRankSendReportsAsync
+ (
+ s_server_context,
+ 0,
+ SV_RankSendReportsCBF,
+ NULL,
+ GR_OPT_END
+ );
+
+ if( status != GR_STATUS_PENDING )
+ {
+ SV_RankError( "SV_RankEnd: Expected GR_STATUS_PENDING, got %s",
+ SV_RankStatusString( status ) );
+ }
+
+ s_rankings_active = qfalse;
+ Cvar_Set( "sv_rankingsActive", "0" );
+}
+
+/*
+================
+SV_RankPoll
+================
+*/
+void SV_RankPoll( void )
+{
+ GRankPoll();
+}
+
+/*
+================
+SV_RankCheckInit
+================
+*/
+qboolean SV_RankCheckInit( void )
+{
+ return (s_rankings_contexts > 0);
+}
+
+/*
+================
+SV_RankActive
+================
+*/
+qboolean SV_RankActive( void )
+{
+ return s_rankings_active;
+}
+
+/*
+=================
+SV_RankUserStatus
+=================
+*/
+grank_status_t SV_RankUserStatus( int index )
+{
+ if( !s_rankings_active )
+ {
+ return GR_STATUS_ERROR;
+ }
+
+ assert( s_ranked_players != NULL );
+ assert( index >= 0 );
+ assert( index < sv_maxclients->value );
+
+ return s_ranked_players[index].grank_status;
+}
+
+/*
+================
+SV_RankUserGRank
+================
+*/
+int SV_RankUserGrank( int index )
+{
+ if( !s_rankings_active )
+ {
+ return 0;
+ }
+
+ assert( s_ranked_players != NULL );
+ assert( index >= 0 );
+ assert( index < sv_maxclients->value );
+
+ return s_ranked_players[index].grank;
+}
+
+/*
+================
+SV_RankUserReset
+================
+*/
+void SV_RankUserReset( int index )
+{
+ if( !s_rankings_active )
+ {
+ return;
+ }
+
+ assert( s_ranked_players != NULL );
+ assert( index >= 0 );
+ assert( index < sv_maxclients->value );
+
+ switch( s_ranked_players[index].grank_status )
+ {
+ case QGR_STATUS_SPECTATOR:
+ case QGR_STATUS_NO_USER:
+ case QGR_STATUS_BAD_PASSWORD:
+ case QGR_STATUS_USER_EXISTS:
+ case QGR_STATUS_NO_MEMBERSHIP:
+ case QGR_STATUS_TIMEOUT:
+ case QGR_STATUS_ERROR:
+ s_ranked_players[index].grank_status = QGR_STATUS_NEW;
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+================
+SV_RankUserSpectate
+================
+*/
+void SV_RankUserSpectate( int index )
+{
+ if( !s_rankings_active )
+ {
+ return;
+ }
+
+ assert( s_ranked_players != NULL );
+ assert( index >= 0 );
+ assert( index < sv_maxclients->value );
+
+ // GRANK_FIXME - check current status?
+ s_ranked_players[index].grank_status = QGR_STATUS_SPECTATOR;
+}
+
+/*
+================
+SV_RankUserCreate
+================
+*/
+void SV_RankUserCreate( int index, char* username, char* password,
+ char* email )
+{
+ GR_INIT init;
+ GR_STATUS status;
+
+ assert( index >= 0 );
+ assert( index < sv_maxclients->value );
+ assert( username != NULL );
+ assert( password != NULL );
+ assert( email != NULL );
+ assert( s_ranked_players );
+ assert( s_ranked_players[index].grank_status != QGR_STATUS_ACTIVE );
+
+ Com_DPrintf( "SV_RankUserCreate( %d, %s, \"****\", %s );\n", index,
+ username, email );
+
+ if( !s_rankings_active )
+ {
+ Com_DPrintf( "SV_RankUserCreate: Not ready to create\n" );
+ s_ranked_players[index].grank_status = QGR_STATUS_ERROR;
+ return;
+ }
+
+ if( s_ranked_players[index].grank_status == QGR_STATUS_ACTIVE )
+ {
+ Com_DPrintf( "SV_RankUserCreate: Got Create from active player\n" );
+ return;
+ }
+
+ // get a separate context for the new user
+ init = GRankInit( 0, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END );
+ s_ranked_players[index].context = init.context;
+ s_rankings_contexts++;
+ Com_DPrintf( "SV_RankUserCreate(); s_rankings_contexts=%d\n",s_rankings_contexts );
+ Com_DPrintf( "SV_RankUserCreate(); s_ranked_players[%d].context=%d\n",index,init.context );
+
+ // attempt to create a new account, proceed to SV_RankUserCBF
+ status = GRankUserCreateAsync
+ (
+ s_ranked_players[index].context,
+ username,
+ password,
+ email,
+ SV_RankUserCBF,
+ (void*)&s_ranked_players[index],
+ GR_OPT_END
+ );
+
+ if( status == GR_STATUS_PENDING )
+ {
+ s_ranked_players[index].grank_status = QGR_STATUS_PENDING;
+ s_ranked_players[index].final_status = QGR_STATUS_NEW;
+ }
+ else
+ {
+ SV_RankError( "SV_RankUserCreate: Expected GR_STATUS_PENDING, got %s",
+ SV_RankStatusString( status ) );
+ }
+}
+
+/*
+================
+SV_RankUserLogin
+================
+*/
+void SV_RankUserLogin( int index, char* username, char* password )
+{
+ GR_INIT init;
+ GR_STATUS status;
+
+ assert( index >= 0 );
+ assert( index < sv_maxclients->value );
+ assert( username != NULL );
+ assert( password != NULL );
+ assert( s_ranked_players );
+ assert( s_ranked_players[index].grank_status != QGR_STATUS_ACTIVE );
+
+ Com_DPrintf( "SV_RankUserLogin( %d, %s, \"****\" );\n", index, username );
+
+ if( !s_rankings_active )
+ {
+ Com_DPrintf( "SV_RankUserLogin: Not ready for login\n" );
+ s_ranked_players[index].grank_status = QGR_STATUS_ERROR;
+ return;
+ }
+
+ if( s_ranked_players[index].grank_status == QGR_STATUS_ACTIVE )
+ {
+ Com_DPrintf( "SV_RankUserLogin: Got Login from active player\n" );
+ return;
+ }
+
+ // get a separate context for the new user
+ init = GRankInit( 0, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END );
+ s_ranked_players[index].context = init.context;
+ s_rankings_contexts++;
+ Com_DPrintf( "SV_RankUserLogin(); s_rankings_contexts=%d\n",s_rankings_contexts );
+ Com_DPrintf( "SV_RankUserLogin(); s_ranked_players[%d].context=%d\n",index,init.context );
+
+ // login user, proceed to SV_RankUserCBF
+ status = GRankUserLoginAsync
+ (
+ s_ranked_players[index].context,
+ username,
+ password,
+ SV_RankUserCBF,
+ (void*)&s_ranked_players[index],
+ GR_OPT_END
+ );
+
+ if( status == GR_STATUS_PENDING )
+ {
+ s_ranked_players[index].grank_status = QGR_STATUS_PENDING;
+ s_ranked_players[index].final_status = QGR_STATUS_NEW;
+ }
+ else
+ {
+ SV_RankError( "SV_RankUserLogin: Expected GR_STATUS_PENDING, got %s",
+ SV_RankStatusString( status ) );
+ }
+}
+
+/*
+===================
+SV_RankUserValidate
+===================
+*/
+qboolean SV_RankUserValidate( int index, const char* player_id, const char* key, int token_len, int rank, char* name )
+{
+ GR_INIT init;
+ GR_STATUS status;
+ qboolean rVal;
+ ranked_player_t* ranked_player;
+ int i;
+
+ assert( s_ranked_players );
+ assert( s_ranked_players[index].grank_status != QGR_STATUS_ACTIVE );
+
+ rVal = qfalse;
+
+ if( !s_rankings_active )
+ {
+ Com_DPrintf( "SV_RankUserValidate: Not ready to validate\n" );
+ s_ranked_players[index].grank_status = QGR_STATUS_ERROR;
+ return rVal;
+ }
+
+ ranked_player = &(s_ranked_players[index]);
+
+ if ( (player_id != NULL) && (key != NULL))
+ {
+ // the real player_id and key is set when SV_RankJoinGameCBF
+ // is called we do this so that SV_RankUserValidate
+ // can be shared by both server side login and client side login
+
+ // for client side logined in players
+ // server is creating GR_OPT_PLAYERCONTEXT
+ init = GRankInit( 0, SV_RankGameKey, GR_OPT_POLL, GR_OPT_END );
+ ranked_player->context = init.context;
+ s_rankings_contexts++;
+ Com_DPrintf( "SV_RankUserValidate(); s_rankings_contexts=%d\n",s_rankings_contexts );
+ Com_DPrintf( "SV_RankUserValidate(); s_ranked_players[%d].context=%d\n",index,init.context );
+
+ // uudecode player id and player token
+ ranked_player->player_id = SV_RankDecodePlayerID(player_id);
+ Com_DPrintf( "SV_RankUserValidate(); ranked_player->player_id =%u\n", (uint32_t)ranked_player->player_id );
+ SV_RankDecodePlayerKey(key, ranked_player->token);
+
+ // save name and check for duplicates
+ Q_strncpyz( ranked_player->name, name, sizeof(ranked_player->name) );
+ for( i = 0; i < sv_maxclients->value; i++ )
+ {
+ if( (i != index) && (s_ranked_players[i].grank_status == QGR_STATUS_ACTIVE) &&
+ (strcmp( s_ranked_players[i].name, name ) == 0) )
+ {
+ Com_DPrintf( "SV_RankUserValidate: Duplicate login\n" );
+ ranked_player->grank_status = QGR_STATUS_NO_USER;
+ ranked_player->final_status = QGR_STATUS_NEW;
+ ranked_player->grank = 0;
+ return qfalse;
+ }
+ }
+
+ // then validate
+ status = GRankPlayerValidate(
+ s_server_context,
+ ranked_player->player_id,
+ ranked_player->token,
+ token_len,
+ GR_OPT_PLAYERCONTEXT,
+ ranked_player->context,
+ GR_OPT_END);
+ }
+ else
+ {
+ // make server side login (bots) happy
+ status = GR_STATUS_OK;
+ }
+
+ if (status == GR_STATUS_OK)
+ {
+ ranked_player->grank_status = QGR_STATUS_ACTIVE;
+ ranked_player->final_status = QGR_STATUS_NEW;
+ ranked_player->grank = rank;
+ rVal = qtrue;
+ }
+ else if (status == GR_STATUS_INVALIDUSER)
+ {
+ ranked_player->grank_status = QGR_STATUS_INVALIDUSER;
+ ranked_player->final_status = QGR_STATUS_NEW;
+ ranked_player->grank = 0;
+ rVal = qfalse;
+ }
+ else
+ {
+ SV_RankError( "SV_RankUserValidate: Unexpected status %s",
+ SV_RankStatusString( status ) );
+ s_ranked_players[index].grank_status = QGR_STATUS_ERROR;
+ ranked_player->grank = 0;
+ }
+
+ return rVal;
+}
+
+/*
+================
+SV_RankUserLogout
+================
+*/
+void SV_RankUserLogout( int index )
+{
+ GR_STATUS status;
+ GR_STATUS cleanup_status;
+
+ if( !s_rankings_active )
+ {
+ return;
+ }
+
+ assert( index >= 0 );
+ assert( index < sv_maxclients->value );
+ assert( s_ranked_players );
+
+ if( s_ranked_players[index].context == 0 ) {
+ return;
+ }
+
+ Com_DPrintf( "SV_RankUserLogout( %d );\n", index );
+
+ // masqueraded player may not be active yet, if they fail validation,
+ // but still they have a context needs to be cleaned
+ // what matters is the s_ranked_players[index].context
+
+ // send reports, proceed to SV_RankSendReportsCBF
+ status = GRankSendReportsAsync
+ (
+ s_ranked_players[index].context,
+ 0,
+ SV_RankSendReportsCBF,
+ (void*)&s_ranked_players[index],
+ GR_OPT_END
+ );
+
+ if( status == GR_STATUS_PENDING )
+ {
+ s_ranked_players[index].grank_status = QGR_STATUS_PENDING;
+ s_ranked_players[index].final_status = QGR_STATUS_NEW;
+ }
+ else
+ {
+ SV_RankError( "SV_RankUserLogout: Expected GR_STATUS_PENDING, got %s",
+ SV_RankStatusString( status ) );
+
+ cleanup_status = GRankCleanupAsync
+ (
+ s_ranked_players[index].context,
+ 0,
+ SV_RankCleanupCBF,
+ (void*)&s_ranked_players[index],
+ GR_OPT_END
+ );
+
+ if( cleanup_status != GR_STATUS_PENDING )
+ {
+ SV_RankError( "SV_RankUserLogout: Expected "
+ "GR_STATUS_PENDING from GRankCleanupAsync, got %s",
+ SV_RankStatusString( cleanup_status ) );
+ SV_RankCloseContext( &(s_ranked_players[index]) );
+ }
+ }
+}
+
+/*
+================
+SV_RankReportInt
+================
+*/
+void SV_RankReportInt( int index1, int index2, int key, int value,
+ qboolean accum )
+{
+ GR_STATUS status;
+ GR_CONTEXT context;
+ uint64_t match;
+ uint64_t user1;
+ uint64_t user2;
+ int opt_accum;
+
+ if( !s_rankings_active )
+ {
+ return;
+ }
+
+ assert( index1 >= -1 );
+ assert( index1 < sv_maxclients->value );
+ assert( index2 >= -1 );
+ assert( index2 < sv_maxclients->value );
+ assert( s_ranked_players );
+
+// Com_DPrintf( "SV_RankReportInt( %d, %d, %d, %d, %d );\n", index1, index2,
+// key, value, accum );
+
+ // get context, match, and player_id for player index1
+ if( index1 == -1 )
+ {
+ context = s_server_context;
+ match = s_server_match;
+ user1 = 0;
+ }
+ else
+ {
+ if( s_ranked_players[index1].grank_status != QGR_STATUS_ACTIVE )
+ {
+ Com_DPrintf( "SV_RankReportInt: Expecting QGR_STATUS_ACTIVE"
+ " Got Unexpected status %d for player %d\n",
+ s_ranked_players[index1].grank_status, index1 );
+ return;
+ }
+
+ context = s_ranked_players[index1].context;
+ match = s_ranked_players[index1].match;
+ user1 = s_ranked_players[index1].player_id;
+ }
+
+ // get player_id for player index2
+ if( index2 == -1 )
+ {
+ user2 = 0;
+ }
+ else
+ {
+ if( s_ranked_players[index2].grank_status != QGR_STATUS_ACTIVE )
+ {
+ Com_DPrintf( "SV_RankReportInt: Expecting QGR_STATUS_ACTIVE"
+ " Got Unexpected status %d for player %d\n",
+ s_ranked_players[index2].grank_status, index2 );
+ return;
+ }
+
+ user2 = s_ranked_players[index2].player_id;
+ }
+
+ opt_accum = accum ? GR_OPT_ACCUM : GR_OPT_END;
+
+ status = GRankReportInt
+ (
+ context,
+ match,
+ user1,
+ user2,
+ key,
+ value,
+ opt_accum,
+ GR_OPT_END
+ );
+
+ if( status != GR_STATUS_OK )
+ {
+ SV_RankError( "SV_RankReportInt: Unexpected status %s",
+ SV_RankStatusString( status ) );
+ }
+
+ if( user2 != 0 )
+ {
+ context = s_ranked_players[index2].context;
+ match = s_ranked_players[index2].match;
+
+ status = GRankReportInt
+ (
+ context,
+ match,
+ user1,
+ user2,
+ key,
+ value,
+ opt_accum,
+ GR_OPT_END
+ );
+
+ if( status != GR_STATUS_OK )
+ {
+ SV_RankError( "SV_RankReportInt: Unexpected status %s",
+ SV_RankStatusString( status ) );
+ }
+ }
+}
+
+/*
+================
+SV_RankReportStr
+================
+*/
+void SV_RankReportStr( int index1, int index2, int key, char* value )
+{
+ GR_STATUS status;
+ GR_CONTEXT context;
+ uint64_t match;
+ uint64_t user1;
+ uint64_t user2;
+
+ if( !s_rankings_active )
+ {
+ return;
+ }
+
+ assert( index1 >= -1 );
+ assert( index1 < sv_maxclients->value );
+ assert( index2 >= -1 );
+ assert( index2 < sv_maxclients->value );
+ assert( s_ranked_players );
+
+// Com_DPrintf( "SV_RankReportStr( %d, %d, %d, \"%s\" );\n", index1, index2,
+// key, value );
+
+ // get context, match, and player_id for player index1
+ if( index1 == -1 )
+ {
+ context = s_server_context;
+ match = s_server_match;
+ user1 = 0;
+ }
+ else
+ {
+ if( s_ranked_players[index1].grank_status != QGR_STATUS_ACTIVE )
+ {
+ Com_DPrintf( "SV_RankReportStr: Unexpected status %d\n",
+ s_ranked_players[index1].grank_status );
+ return;
+ }
+
+ context = s_ranked_players[index1].context;
+ match = s_ranked_players[index1].match;
+ user1 = s_ranked_players[index1].player_id;
+ }
+
+ // get player_id for player index2
+ if( index2 == -1 )
+ {
+ user2 = 0;
+ }
+ else
+ {
+ if( s_ranked_players[index2].grank_status != QGR_STATUS_ACTIVE )
+ {
+ Com_DPrintf( "SV_RankReportStr: Unexpected status %d\n",
+ s_ranked_players[index2].grank_status );
+ return;
+ }
+
+ user2 = s_ranked_players[index2].player_id;
+ }
+
+ status = GRankReportStr
+ (
+ context,
+ match,
+ user1,
+ user2,
+ key,
+ value,
+ GR_OPT_END
+ );
+
+ if( status != GR_STATUS_OK )
+ {
+ SV_RankError( "SV_RankReportStr: Unexpected status %s",
+ SV_RankStatusString( status ) );
+ }
+
+ if( user2 != 0 )
+ {
+ context = s_ranked_players[index2].context;
+ match = s_ranked_players[index2].match;
+
+ status = GRankReportStr
+ (
+ context,
+ match,
+ user1,
+ user2,
+ key,
+ value,
+ GR_OPT_END
+ );
+
+ if( status != GR_STATUS_OK )
+ {
+ SV_RankError( "SV_RankReportInt: Unexpected status %s",
+ SV_RankStatusString( status ) );
+ }
+ }
+}
+
+/*
+================
+SV_RankQuit
+================
+*/
+void SV_RankQuit( void )
+{
+ int i;
+ int j = 0;
+ // yuck
+
+ while( s_rankings_contexts > 1 )
+ {
+ assert(s_ranked_players);
+ if( s_ranked_players != NULL )
+ {
+ for( i = 0; i < sv_maxclients->value; i++ )
+ {
+ // check for players that weren't yet active in SV_RankEnd
+ if( s_ranked_players[i].grank_status == QGR_STATUS_ACTIVE )
+ {
+ SV_RankUserLogout( i );
+ Com_DPrintf( "SV_RankQuit: SV_RankUserLogout %d\n",i );
+ }
+ else
+ {
+ if( s_ranked_players[i].context )
+ {
+ GR_STATUS cleanup_status;
+ cleanup_status = GRankCleanupAsync
+ (
+ s_ranked_players[i].context,
+ 0,
+ SV_RankCleanupCBF,
+ (void*)&(s_ranked_players[i]),
+ GR_OPT_END
+ );
+
+ if( cleanup_status != GR_STATUS_PENDING )
+ {
+ SV_RankError( "SV_RankQuit: Expected "
+ "GR_STATUS_PENDING from GRankCleanupAsync, got %s",
+ SV_RankStatusString( cleanup_status ) );
+ }
+ }
+ }
+ }
+ }
+ SV_RankPoll();
+
+ // should've finished by now
+ assert( (j++) < 68 );
+ }
+}
+
+/*
+==============================================================================
+
+Private Functions
+
+==============================================================================
+*/
+
+/*
+=================
+SV_RankNewGameCBF
+=================
+*/
+static void SV_RankNewGameCBF( GR_NEWGAME* gr_newgame, void* cbf_arg )
+{
+ GR_MATCH match;
+ int i;
+
+ assert( gr_newgame != NULL );
+ assert( cbf_arg == NULL );
+
+ Com_DPrintf( "SV_RankNewGameCBF( %08X, %08X );\n", gr_newgame, cbf_arg );
+
+ if( gr_newgame->status == GR_STATUS_OK )
+ {
+ char info[MAX_INFO_STRING];
+ char gameid[sizeof(s_ranked_players[i].game_id) * 4 / 3 + 2];
+
+ // save game id
+ s_rankings_game_id = gr_newgame->game_id;
+
+ // encode gameid
+ memset(gameid,0,sizeof(gameid));
+ SV_RankEncodeGameID(s_rankings_game_id,gameid,sizeof(gameid));
+
+ // set CS_GRANK rankingsGameID to pass to client
+ memset(info,0,sizeof(info));
+ Info_SetValueForKey( info, "rankingsGameKey", s_rankings_game_key );
+ Info_SetValueForKey( info, "rankingsGameID", gameid );
+ SV_SetConfigstring( CS_GRANK, info );
+
+ // initialize client status
+ for( i = 0; i < sv_maxclients->value; i++ )
+ s_ranked_players[i].grank_status = QGR_STATUS_NEW;
+
+ // start new match
+ match = GRankStartMatch( s_server_context );
+ s_server_match = match.match;
+
+ // ready to go
+ s_rankings_active = qtrue;
+ Cvar_Set( "sv_rankingsActive", "1" );
+
+ }
+ else if( gr_newgame->status == GR_STATUS_BADLEAGUE )
+ {
+ SV_RankError( "SV_RankNewGameCBF: Invalid League name\n" );
+ }
+ else
+ {
+ //GRank handle new game failure
+ // force SV_RankEnd() to run
+ //SV_RankEnd();
+ SV_RankError( "SV_RankNewGameCBF: Unexpected status %s",
+ SV_RankStatusString( gr_newgame->status ) );
+ }
+}
+
+/*
+================
+SV_RankUserCBF
+================
+*/
+static void SV_RankUserCBF( GR_LOGIN* gr_login, void* cbf_arg )
+{
+ ranked_player_t* ranked_player;
+ GR_STATUS join_status;
+ GR_STATUS cleanup_status;
+
+ assert( gr_login != NULL );
+ assert( cbf_arg != NULL );
+
+ Com_DPrintf( "SV_RankUserCBF( %08X, %08X );\n", gr_login, cbf_arg );
+
+ ranked_player = (ranked_player_t*)cbf_arg;
+ assert(ranked_player);
+ assert( ranked_player->context );
+
+ switch( gr_login->status )
+ {
+ case GR_STATUS_OK:
+ // attempt to join the game, proceed to SV_RankJoinGameCBF
+ join_status = GRankJoinGameAsync
+ (
+ ranked_player->context,
+ s_rankings_game_id,
+ SV_RankJoinGameCBF,
+ cbf_arg,
+ GR_OPT_END
+ );
+
+ if( join_status != GR_STATUS_PENDING )
+ {
+ SV_RankError( "SV_RankUserCBF: Expected GR_STATUS_PENDING "
+ "from GRankJoinGameAsync, got %s",
+ SV_RankStatusString( join_status ) );
+ }
+ break;
+ case GR_STATUS_NOUSER:
+ Com_DPrintf( "SV_RankUserCBF: Got status %s\n",
+ SV_RankStatusString( gr_login->status ) );
+ ranked_player->final_status = QGR_STATUS_NO_USER;
+ break;
+ case GR_STATUS_BADPASSWORD:
+ Com_DPrintf( "SV_RankUserCBF: Got status %s\n",
+ SV_RankStatusString( gr_login->status ) );
+ ranked_player->final_status = QGR_STATUS_BAD_PASSWORD;
+ break;
+ case GR_STATUS_TIMEOUT:
+ Com_DPrintf( "SV_RankUserCBF: Got status %s\n",
+ SV_RankStatusString( gr_login->status ) );
+ ranked_player->final_status = QGR_STATUS_TIMEOUT;
+ break;
+ default:
+ Com_DPrintf( "SV_RankUserCBF: Unexpected status %s\n",
+ SV_RankStatusString( gr_login->status ) );
+ ranked_player->final_status = QGR_STATUS_ERROR;
+ break;
+ }
+
+ if( ranked_player->final_status != QGR_STATUS_NEW )
+ {
+ // login or create failed, so clean up before the next attempt
+ cleanup_status = GRankCleanupAsync
+ (
+ ranked_player->context,
+ 0,
+ SV_RankCleanupCBF,
+ (void*)ranked_player,
+ GR_OPT_END
+ );
+
+ if( cleanup_status != GR_STATUS_PENDING )
+ {
+ SV_RankError( "SV_RankUserCBF: Expected GR_STATUS_PENDING "
+ "from GRankCleanupAsync, got %s",
+ SV_RankStatusString( cleanup_status ) );
+ SV_RankCloseContext( ranked_player );
+ }
+ }
+}
+
+/*
+================
+SV_RankJoinGameCBF
+================
+*/
+static void SV_RankJoinGameCBF( GR_JOINGAME* gr_joingame, void* cbf_arg )
+{
+ ranked_player_t* ranked_player;
+ GR_MATCH match;
+ GR_STATUS cleanup_status;
+
+ assert( gr_joingame != NULL );
+ assert( cbf_arg != NULL );
+
+ Com_DPrintf( "SV_RankJoinGameCBF( %08X, %08X );\n", gr_joingame, cbf_arg );
+
+ ranked_player = (ranked_player_t*)cbf_arg;
+
+ assert( ranked_player );
+ assert( ranked_player->context != 0 );
+
+ if( gr_joingame->status == GR_STATUS_OK )
+ {
+ int i;
+ // save user id
+ ranked_player->player_id = gr_joingame->player_id;
+ memcpy(ranked_player->token,gr_joingame->token,
+ sizeof(GR_PLAYER_TOKEN)) ;
+ match = GRankStartMatch( ranked_player->context );
+ ranked_player->match = match.match;
+ ranked_player->grank = gr_joingame->rank;
+
+ // find the index and call SV_RankUserValidate
+ for (i=0;i<sv_maxclients->value;i++)
+ if ( ranked_player == &s_ranked_players[i] )
+ SV_RankUserValidate(i,NULL,NULL,0, gr_joingame->rank,ranked_player->name);
+ }
+ else
+ {
+ //GRand handle join game failure
+ SV_RankError( "SV_RankJoinGameCBF: Unexpected status %s",
+ SV_RankStatusString( gr_joingame->status ) );
+
+ cleanup_status = GRankCleanupAsync
+ (
+ ranked_player->context,
+ 0,
+ SV_RankCleanupCBF,
+ cbf_arg,
+ GR_OPT_END
+ );
+
+ if( cleanup_status != GR_STATUS_PENDING )
+ {
+ SV_RankError( "SV_RankJoinGameCBF: Expected "
+ "GR_STATUS_PENDING from GRankCleanupAsync, got %s",
+ SV_RankStatusString( cleanup_status ) );
+ SV_RankCloseContext( ranked_player );
+ }
+ }
+}
+
+/*
+================
+SV_RankSendReportsCBF
+================
+*/
+static void SV_RankSendReportsCBF( GR_STATUS* status, void* cbf_arg )
+{
+ ranked_player_t* ranked_player;
+ GR_CONTEXT context;
+ GR_STATUS cleanup_status;
+
+ assert( status != NULL );
+ // NULL cbf_arg means server is sending match reports
+
+ Com_DPrintf( "SV_RankSendReportsCBF( %08X, %08X );\n", status, cbf_arg );
+
+ ranked_player = (ranked_player_t*)cbf_arg;
+ if( ranked_player == NULL )
+ {
+ Com_DPrintf( "SV_RankSendReportsCBF: server\n" );
+ context = s_server_context;
+ }
+ else
+ {
+ Com_DPrintf( "SV_RankSendReportsCBF: player\n" );
+ context = ranked_player->context;
+ }
+
+ //assert( context != 0 );
+ if( *status != GR_STATUS_OK )
+ {
+ SV_RankError( "SV_RankSendReportsCBF: Unexpected status %s",
+ SV_RankStatusString( *status ) );
+ }
+
+ if( context == 0 )
+ {
+ Com_DPrintf( "SV_RankSendReportsCBF: WARNING: context == 0" );
+ SV_RankCloseContext( ranked_player );
+ }
+ else
+ {
+ cleanup_status = GRankCleanupAsync
+ (
+ context,
+ 0,
+ SV_RankCleanupCBF,
+ cbf_arg,
+ GR_OPT_END
+ );
+
+ if( cleanup_status != GR_STATUS_PENDING )
+ {
+ SV_RankError( "SV_RankSendReportsCBF: Expected "
+ "GR_STATUS_PENDING from GRankCleanupAsync, got %s",
+ SV_RankStatusString( cleanup_status ) );
+ SV_RankCloseContext( ranked_player );
+ }
+ }
+}
+
+/*
+================
+SV_RankCleanupCBF
+================
+*/
+static void SV_RankCleanupCBF( GR_STATUS* status, void* cbf_arg )
+{
+ ranked_player_t* ranked_player;
+ ranked_player = (ranked_player_t*)cbf_arg;
+
+ assert( status != NULL );
+ // NULL cbf_arg means server is cleaning up
+
+ Com_DPrintf( "SV_RankCleanupCBF( %08X, %08X );\n", status, cbf_arg );
+
+ if( *status != GR_STATUS_OK )
+ {
+ SV_RankError( "SV_RankCleanupCBF: Unexpected status %s",
+ SV_RankStatusString( *status ) );
+ }
+
+ SV_RankCloseContext( ranked_player );
+}
+
+/*
+================
+SV_RankCloseContext
+================
+*/
+static void SV_RankCloseContext( ranked_player_t* ranked_player )
+{
+ if( ranked_player == NULL )
+ {
+ // server cleanup
+ if( s_server_context == 0 )
+ {
+ return;
+ }
+ s_server_context = 0;
+ s_server_match = 0;
+ }
+ else
+ {
+ // player cleanup
+ if( s_ranked_players == NULL )
+ {
+ return;
+ }
+ if( ranked_player->context == 0 )
+ {
+ return;
+ }
+ ranked_player->context = 0;
+ ranked_player->match = 0;
+ ranked_player->player_id = 0;
+ memset( ranked_player->token, 0, sizeof(GR_PLAYER_TOKEN) );
+ ranked_player->grank_status = ranked_player->final_status;
+ ranked_player->final_status = QGR_STATUS_NEW;
+ ranked_player->name[0] = '\0';
+ }
+
+ assert( s_rankings_contexts > 0 );
+ s_rankings_contexts--;
+ Com_DPrintf( "SV_RankCloseContext: s_rankings_contexts = %d\n",
+ s_rankings_contexts );
+
+ if( s_rankings_contexts == 0 )
+ {
+ GRankLogLevel( GRLOG_OFF );
+
+ if( s_ranked_players != NULL )
+ {
+ Z_Free( s_ranked_players );
+ s_ranked_players = NULL;
+ }
+
+ s_rankings_active = qfalse;
+ Cvar_Set( "sv_rankingsActive", "0" );
+ }
+}
+
+/*
+================
+SV_RankAsciiEncode
+
+Encodes src_len bytes of binary data from the src buffer as ASCII text,
+using 6 bits per character. The result string is null-terminated and
+stored in the dest buffer.
+
+The dest buffer must be at least (src_len * 4) / 3 + 2 bytes in length.
+
+Returns the length of the result string, not including the null.
+================
+*/
+static int SV_RankAsciiEncode( char* dest, const unsigned char* src,
+ int src_len )
+{
+ unsigned char bin[3];
+ unsigned char txt[4];
+ int dest_len = 0;
+ int i;
+ int j;
+ int num_chars;
+
+ assert( dest != NULL );
+ assert( src != NULL );
+
+ for( i = 0; i < src_len; i += 3 )
+ {
+ // read three bytes of input
+ for( j = 0; j < 3; j++ )
+ {
+ bin[j] = (i + j < src_len) ? src[i + j] : 0;
+ }
+
+ // get four 6-bit values from three bytes
+ txt[0] = bin[0] >> 2;
+ txt[1] = ((bin[0] << 4) | (bin[1] >> 4)) & 63;
+ txt[2] = ((bin[1] << 2) | (bin[2] >> 6)) & 63;
+ txt[3] = bin[2] & 63;
+
+ // store ASCII encoding of 6-bit values
+ num_chars = (i + 2 < src_len) ? 4 : ((src_len - i) * 4) / 3 + 1;
+ for( j = 0; j < num_chars; j++ )
+ {
+ dest[dest_len++] = s_ascii_encoding[txt[j]];
+ }
+ }
+
+ dest[dest_len] = '\0';
+
+ return dest_len;
+}
+
+/*
+================
+SV_RankAsciiDecode
+
+Decodes src_len characters of ASCII text from the src buffer, stores
+the binary result in the dest buffer.
+
+The dest buffer must be at least (src_len * 3) / 4 bytes in length.
+
+Returns the length of the binary result, or zero for invalid input.
+================
+*/
+static int SV_RankAsciiDecode( unsigned char* dest, const char* src,
+ int src_len )
+{
+ static unsigned char s_inverse_encoding[256];
+ static char s_init = 0;
+
+ unsigned char bin[3];
+ unsigned char txt[4];
+ int dest_len = 0;
+ int i;
+ int j;
+ int num_bytes;
+
+ assert( dest != NULL );
+ assert( src != NULL );
+
+ if( !s_init )
+ {
+ // initialize lookup table for decoding
+ memset( s_inverse_encoding, 255, sizeof(s_inverse_encoding) );
+ for( i = 0; i < 64; i++ )
+ {
+ s_inverse_encoding[s_ascii_encoding[i]] = i;
+ }
+ s_init = 1;
+ }
+
+ for( i = 0; i < src_len; i += 4 )
+ {
+ // read four characters of input, decode them to 6-bit values
+ for( j = 0; j < 4; j++ )
+ {
+ txt[j] = (i + j < src_len) ? s_inverse_encoding[src[i + j]] : 0;
+ if (txt[j] == 255)
+ {
+ return 0; // invalid input character
+ }
+ }
+
+ // get three bytes from four 6-bit values
+ bin[0] = (txt[0] << 2) | (txt[1] >> 4);
+ bin[1] = (txt[1] << 4) | (txt[2] >> 2);
+ bin[2] = (txt[2] << 6) | txt[3];
+
+ // store binary data
+ num_bytes = (i + 3 < src_len) ? 3 : ((src_len - i) * 3) / 4;
+ for( j = 0; j < num_bytes; j++ )
+ {
+ dest[dest_len++] = bin[j];
+ }
+ }
+
+ return dest_len;
+}
+
+/*
+================
+SV_RankEncodeGameID
+================
+*/
+static void SV_RankEncodeGameID( uint64_t game_id, char* result,
+ int len )
+{
+ assert( result != NULL );
+
+ if( len < ( ( sizeof(game_id) * 4) / 3 + 2) )
+ {
+ Com_DPrintf( "SV_RankEncodeGameID: result buffer too small\n" );
+ result[0] = '\0';
+ }
+ else
+ {
+ qint64 gameid = LittleLong64(*(qint64*)&game_id);
+ SV_RankAsciiEncode( result, (unsigned char*)&gameid,
+ sizeof(qint64) );
+ }
+}
+
+/*
+================
+SV_RankDecodePlayerID
+================
+*/
+static uint64_t SV_RankDecodePlayerID( const char* string )
+{
+ unsigned char buffer[9];
+ int len;
+ qint64 player_id;
+
+ assert( string != NULL );
+
+ len = strlen (string) ;
+ Com_DPrintf( "SV_RankDecodePlayerID: string length %d\n",len );
+ SV_RankAsciiDecode( buffer, string, len );
+ player_id = LittleLong64(*(qint64*)buffer);
+ return *(uint64_t*)&player_id;
+}
+
+/*
+================
+SV_RankDecodePlayerKey
+================
+*/
+static void SV_RankDecodePlayerKey( const char* string, GR_PLAYER_TOKEN key )
+{
+ unsigned char buffer[1400];
+ int len;
+ assert( string != NULL );
+
+ len = strlen (string) ;
+ Com_DPrintf( "SV_RankDecodePlayerKey: string length %d\n",len );
+
+ memset(key,0,sizeof(GR_PLAYER_TOKEN));
+ memset(buffer,0,sizeof(buffer));
+ memcpy( key, buffer, SV_RankAsciiDecode( buffer, string, len ) );
+}
+
+/*
+================
+SV_RankStatusString
+================
+*/
+static char* SV_RankStatusString( GR_STATUS status )
+{
+ switch( status )
+ {
+ case GR_STATUS_OK: return "GR_STATUS_OK";
+ case GR_STATUS_ERROR: return "GR_STATUS_ERROR";
+ case GR_STATUS_BADPARAMS: return "GR_STATUS_BADPARAMS";
+ case GR_STATUS_NETWORK: return "GR_STATUS_NETWORK";
+ case GR_STATUS_NOUSER: return "GR_STATUS_NOUSER";
+ case GR_STATUS_BADPASSWORD: return "GR_STATUS_BADPASSWORD";
+ case GR_STATUS_BADGAME: return "GR_STATUS_BADGAME";
+ case GR_STATUS_PENDING: return "GR_STATUS_PENDING";
+ case GR_STATUS_BADDOMAIN: return "GR_STATUS_BADDOMAIN";
+ case GR_STATUS_DOMAINLOCK: return "GR_STATUS_DOMAINLOCK";
+ case GR_STATUS_TIMEOUT: return "GR_STATUS_TIMEOUT";
+ case GR_STATUS_INVALIDUSER: return "GR_STATUS_INVALIDUSER";
+ case GR_STATUS_INVALIDCONTEXT: return "GR_STATUS_INVALIDCONTEXT";
+ default: return "(UNKNOWN)";
+ }
+}
+
+/*
+================
+SV_RankError
+================
+*/
+static void SV_RankError( const char* fmt, ... )
+{
+ va_list arg_ptr;
+ char text[1024];
+
+ va_start( arg_ptr, fmt );
+ vsprintf( text, fmt, arg_ptr );
+ va_end( arg_ptr );
+
+ Com_DPrintf( "****************************************\n" );
+ Com_DPrintf( "SV_RankError: %s\n", text );
+ Com_DPrintf( "****************************************\n" );
+
+ s_rankings_active = qfalse;
+ Cvar_Set( "sv_rankingsActive", "0" );
+ // FIXME - attempt clean shutdown?
+}
+