Leaderboards.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Game_local.h"
  23. #include "Leaderboards.h"
  24. #include "MultiplayerGame.h"
  25. /*
  26. ================================================================================================
  27. Leaderboard stats column definitions - per Game Type.
  28. ================================================================================================
  29. */
  30. static columnDef_t public_Deathmatch[] = {
  31. { "Frags", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
  32. //{ "Deaths", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
  33. //{ "Wins", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
  34. //{ "Score", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
  35. };
  36. static columnDef_t public_Tourney[] = {
  37. { "Wins", 64, AGGREGATE_SUM, STATS_COLUMN_DISPLAY_NUMBER },
  38. };
  39. static columnDef_t public_TeamDeathmatch[] = {
  40. { "Wins", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
  41. };
  42. static columnDef_t public_LastmanStanding[] = {
  43. { "Wins", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
  44. };
  45. static columnDef_t public_CaptureTheFlag[] = {
  46. { "Wins", 64, AGGREGATE_MAX, STATS_COLUMN_DISPLAY_NUMBER },
  47. };
  48. // This should match up to the ordering of the gameType_t. ( in MultiplayerGame.h )
  49. const columnGameMode_t gameMode_columnDefs[] = {
  50. { public_Deathmatch, ARRAY_COUNT( public_Deathmatch ), RANK_GREATEST_FIRST, false, false, "DM" }, // DEATHMATCH
  51. { public_Tourney, ARRAY_COUNT( public_Tourney ), RANK_GREATEST_FIRST, false, false, "TOURNEY" }, // TOURNEY
  52. { public_TeamDeathmatch, ARRAY_COUNT( public_TeamDeathmatch ), RANK_GREATEST_FIRST, false, false, "TDM" }, // TEAM DEATHMATCH
  53. { public_LastmanStanding, ARRAY_COUNT( public_LastmanStanding ), RANK_GREATEST_FIRST, false, false, "LMS" }, // LASTMAN STANDING
  54. { public_CaptureTheFlag, ARRAY_COUNT( public_CaptureTheFlag ), RANK_GREATEST_FIRST, false, false, "CTF" }, // CAPTURE THE FLAG
  55. };
  56. /*
  57. =====================================
  58. RetreiveLeaderboardID
  59. Each map will move in blocks of n*modes.
  60. ex. map 0 will have 0 - 4 Leaderboard id's blocked out.
  61. map 1 will have 5 - 10 leaderboard id's blocked out.
  62. if gamemode is added it will move in blocks of ARRAY_COUNT( modes )
  63. =====================================
  64. */
  65. int LeaderboardLocal_GetID( int mapIndex, int gametype ) {
  66. assert( gametype > GAME_RANDOM );
  67. return mapIndex * ARRAY_COUNT( gameMode_columnDefs ) + gametype;
  68. }
  69. /*
  70. =====================================
  71. LeaderboardLocal_Init
  72. =====================================
  73. */
  74. void LeaderboardLocal_Init() {
  75. const idList< mpMap_t > maps = common->GetMapList();
  76. const char ** gameModes = NULL;
  77. const char ** gameModesDisplay = NULL;
  78. int numModes = game->GetMPGameModes( &gameModes, &gameModesDisplay );
  79. // Iterate through all the available maps, and generate leaderboard Defs, and IDs for each.
  80. for( int mapIdx = 0; mapIdx < maps.Num(); mapIdx++ ) {
  81. for( int modeIdx = 0; modeIdx < numModes; modeIdx++ ) {
  82. // Check the supported modes on the map.
  83. if( maps[ mapIdx ].supportedModes & BIT( modeIdx ) ) {
  84. const columnGameMode_t gamemode = gameMode_columnDefs[ modeIdx ];
  85. // Generate a Leaderboard ID for the map/mode
  86. int boardID = LeaderboardLocal_GetID( mapIdx, modeIdx );
  87. // Create and Register the leaderboard with the sys_stats registered Leaderboards
  88. leaderboardDefinition_t * newLeaderboardDef = Sys_CreateLeaderboardDef( boardID,
  89. gamemode.numColumns,
  90. gamemode.columnDef,
  91. gamemode.rankOrder,
  92. gamemode.supportsAttachments,
  93. gamemode.checkAgainstCurrent );
  94. // Set the leaderboard name.
  95. const char* mapname = idLocalization::GetString( maps[ mapIdx ].mapName );
  96. newLeaderboardDef->boardName.Format( "%s %s", mapname, gamemode.abrevName );
  97. // sanity check.
  98. if( Sys_FindLeaderboardDef( boardID ) != newLeaderboardDef ) {
  99. idLib::Error( "Leaderboards_Init leaderboard creation failed" );
  100. }
  101. }
  102. }
  103. }
  104. }
  105. /*
  106. =====================================
  107. LeaderboardLocal_Shutdown
  108. =====================================
  109. */
  110. void LeaderboardLocal_Shutdown() {
  111. Sys_DestroyLeaderboardDefs();
  112. }
  113. /*
  114. =====================================
  115. LeaderboardLocal_Upload
  116. =====================================
  117. */
  118. const static int FRAG_MULTIPLIER = 100;
  119. const static int DEATH_MULTIPLIER = -50;
  120. const static int WINS_MULTIPLIER = 20;
  121. void LeaderboardLocal_Upload( lobbyUserID_t lobbyUserID,int gameType, leaderboardStats_t & stats ) {
  122. assert( gameType > GAME_RANDOM );
  123. int mapIdx = 0;
  124. // Need to figure out What stat columns we want to base rank on. ( for now we'll use wins )
  125. const column_t * gameTypeColumn = NULL;
  126. const column_t defaultStats[] = { stats.wins };
  127. // calculate DM score.
  128. int dmScore = stats.frags * FRAG_MULTIPLIER + stats.deaths * DEATH_MULTIPLIER + stats.wins * WINS_MULTIPLIER ;
  129. // TODO: Once leaderboard menu has correct columns- enable this -> const column_t dmStats[] = { stats.frags, stats.deaths, stats.wins, dmScore };
  130. const column_t dmStats[] = { dmScore };
  131. // Calculate TDM score.
  132. int tdmScore = stats.frags * FRAG_MULTIPLIER + stats.teamfrags * FRAG_MULTIPLIER + stats.deaths * DEATH_MULTIPLIER + stats.wins * WINS_MULTIPLIER ;
  133. const column_t tdmStats[] = { tdmScore };
  134. // Calculate Tourney score.
  135. int tourneyScore = stats.wins;
  136. const column_t tnyStats[] = { tourneyScore };
  137. // Calculate LMS score.
  138. int lmsFrags = stats.frags;
  139. if( lmsFrags < 0 ) {
  140. lmsFrags = 0; // LMS NO LIVES LEFT = -20 on fragCount.
  141. }
  142. int lmsScore = lmsFrags * FRAG_MULTIPLIER + stats.wins * ( WINS_MULTIPLIER * 10 ) ;
  143. const column_t lmsStats[] = { lmsScore };
  144. // Calculate CTF score.
  145. int ctfScore = stats.frags * FRAG_MULTIPLIER + stats.deaths * DEATH_MULTIPLIER + stats.wins * ( WINS_MULTIPLIER * 10 );
  146. const column_t ctfStats[] = { ctfScore };
  147. switch( gameType ) {
  148. case GAME_DM:
  149. gameTypeColumn = dmStats;
  150. break;
  151. case GAME_TDM:
  152. gameTypeColumn = tdmStats;
  153. break;
  154. case GAME_TOURNEY:
  155. gameTypeColumn = tnyStats;
  156. break;
  157. case GAME_LASTMAN:
  158. gameTypeColumn = lmsStats;
  159. break;
  160. case GAME_CTF: {
  161. gameTypeColumn = ctfStats;
  162. break;
  163. }
  164. default:
  165. gameTypeColumn = defaultStats;
  166. }
  167. const idMatchParameters & matchParameters = session->GetActingGameStateLobbyBase().GetMatchParms();
  168. const idList< mpMap_t > maps = common->GetMapList();
  169. // need to find the map Index number
  170. for( mapIdx = 0; mapIdx < maps.Num(); mapIdx++ ) {
  171. if( matchParameters.mapName.Cmp( maps[ mapIdx ].mapFile ) == 0 ) {
  172. break;
  173. }
  174. }
  175. int boardID = LeaderboardLocal_GetID( mapIdx, gameType );
  176. const leaderboardDefinition_t * board = Sys_FindLeaderboardDef( boardID );
  177. if( board ) {
  178. session->LeaderboardUpload( lobbyUserID, board, gameTypeColumn );
  179. } else {
  180. idLib::Warning( "LeaderboardLocal_Upload invalid leaderboard with id of %d", boardID );
  181. }
  182. }
  183. class idLeaderboardCallbackTest : public idLeaderboardCallback {
  184. void Call() {
  185. idLib::Printf( "Leaderboard information retrieved in user callback.\n" );
  186. idLib::Printf( "%d total entries in leaderboard %d.\n", numRowsInLeaderboard, def->id );
  187. for ( int i = 0; i < rows.Num(); i++ ) {
  188. idLib::Printf( "%d: %s rank:%lld", i, rows[i].name.c_str(), rows[i].rank );
  189. for ( int j = 0; j < def->numColumns; j++ ) {
  190. idLib::Printf( ", score[%d]: %lld", j, rows[i].columns[j] );
  191. }
  192. idLib::Printf( "\n" );
  193. }
  194. }
  195. idLeaderboardCallback * Clone() const {
  196. return new (TAG_PSN) idLeaderboardCallbackTest( *this );
  197. }
  198. };
  199. CONSOLE_COMMAND( testLeaderboardDownload, "<id 0 - n > <start = 1> <end = 100>", 0 ) {
  200. idLeaderboardCallbackTest leaderboardCallbackTest;
  201. int leaderboardID = 0;
  202. int start = 1;
  203. int end = 100;
  204. if ( args.Argc() > 1 ) {
  205. leaderboardID = atoi( args.Argv( 1 ) );
  206. }
  207. if ( args.Argc() > 2 ) {
  208. start = atoi( args.Argv( 2 ) );
  209. }
  210. if ( args.Argc() > 3 ) {
  211. end = atoi( args.Argv( 3 ) );
  212. }
  213. const leaderboardDefinition_t * leaderboardDef = Sys_FindLeaderboardDef( leaderboardID );
  214. if( leaderboardDef ) {
  215. session->LeaderboardDownload( 0, leaderboardDef, start, end, leaderboardCallbackTest );
  216. } else {
  217. idLib::Warning( "Sys_FindLeaderboardDef() Unable to find board %d\n", leaderboardID );
  218. }
  219. }
  220. CONSOLE_COMMAND( testLeaderboardUpload, "<gameType 0 - 4 > <frags = 0> <wins = 1>", 0 ) {
  221. idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
  222. lobbyUserID_t user = lobby.GetLobbyUserIdByOrdinal( 0 );
  223. gameType_t gameType = GAME_DM;
  224. int frags = 0;
  225. int wins = 1;
  226. if ( args.Argc() > 1 ) {
  227. gameType = static_cast<gameType_t>( atoi( args.Argv( 1 ) ) );
  228. }
  229. if ( args.Argc() > 2 ) {
  230. frags = atoi( args.Argv( 2 ) );
  231. }
  232. if ( args.Argc() > 3 ) {
  233. wins = atoi( args.Argv( 3 ) );
  234. }
  235. leaderboardStats_t stats = { frags, wins, 0, 0 };
  236. LeaderboardLocal_Upload( user, gameType , stats );
  237. session->LeaderboardFlush();
  238. }
  239. CONSOLE_COMMAND( testLeaderboardUpload_SendToClients, "<gameType 0 - 4 > <frags = 0> <wins = 1>", 0 ) {
  240. for( int playerIdx = 0; playerIdx < gameLocal.numClients; playerIdx++ ) {
  241. leaderboardStats_t stats = { 1, 1, 1, 1 };
  242. LeaderboardLocal_Upload( gameLocal.lobbyUserIDs[ playerIdx ], gameLocal.gameType, stats );
  243. }
  244. // Dont do this more than once.
  245. session->LeaderboardFlush();
  246. }