Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

557 lines
19 KiB

  1. //===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "mm_title.h"
  7. #include "matchmaking/cstrike15/imatchext_cstrike15.h"
  8. #include "inputsystem/iinputsystem.h"
  9. #include "platforminputdevice.h"
  10. #include "netmessages_signon.h"
  11. #ifndef NO_STEAM
  12. #include "steam/isteamuserstats.h"
  13. #endif
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. static ConVar cl_titledataversionblock1( "cl_titledataversionblock1", "14", FCVAR_DEVELOPMENTONLY, "stats for console title data block1 i/o version." );
  17. static ConVar cl_titledataversionblock2( "cl_titledataversionblock2", "8", FCVAR_DEVELOPMENTONLY, "stats for console title data block2 i/o version." );
  18. static ConVar cl_titledataversionblock3( "cl_titledataversionblock3", "48", FCVAR_DEVELOPMENTONLY, "stats for console title data block3 i/o version." );
  19. static TitleDataFieldsDescription_t const * PrepareTitleDataStorageDescription()
  20. {
  21. #if defined( _X360 )
  22. #define TD_ENTRY( szName, nTD, eDataType, numBytesOffset ) \
  23. { \
  24. TitleDataFieldsDescription_t aTDFD = { szName, TitleDataFieldsDescription_t::nTD, TitleDataFieldsDescription_t::eDataType, numBytesOffset }; \
  25. s_tdfd.AddToTail( aTDFD ); \
  26. if ( numBytesOffset >= XPROFILE_SETTING_MAX_SIZE ) \
  27. Warning( "\nnumBytesOffset %d is > XPROFILE_SETTING_MAX_SIZE in TD_ENTRY for %s\n\n", numBytesOffset, szName ); \
  28. }
  29. #else
  30. #define TD_ENTRY( szName, nTD, eDataType, numBytesOffset ) \
  31. { \
  32. TitleDataFieldsDescription_t aTDFD = { szName, TitleDataFieldsDescription_t::nTD, TitleDataFieldsDescription_t::eDataType, numBytesOffset }; \
  33. s_tdfd.AddToTail( aTDFD ); \
  34. }
  35. #endif // _X360
  36. static CUtlVector< TitleDataFieldsDescription_t > s_tdfd;
  37. #if defined( _X360 )
  38. // versioning info for the title data blocks
  39. char *pTitleDataBlock[3];
  40. pTitleDataBlock[0] = new char[30];
  41. pTitleDataBlock[1] = new char[30];
  42. pTitleDataBlock[2] = new char[30];
  43. Q_snprintf( pTitleDataBlock[0], 30, "TITLEDATA.BLOCK1.VERSION" );
  44. Q_snprintf( pTitleDataBlock[1], 30, "TITLEDATA.BLOCK2.VERSION" );
  45. Q_snprintf( pTitleDataBlock[2], 30, "TITLEDATA.BLOCK3.VERSION" );
  46. TD_ENTRY( pTitleDataBlock[0], DB_TD1, DT_uint16, offsetof( TitleData1, versionNumber ) );
  47. TD_ENTRY( pTitleDataBlock[1], DB_TD2, DT_uint16, offsetof( TitleData2, versionNumber ) );
  48. TD_ENTRY( pTitleDataBlock[2], DB_TD3, DT_uint16, offsetof( TitleData3, versionNumber ) );
  49. // stats
  50. #define CFG( cppType, name ) \
  51. TD_ENTRY( "STATS.usr." #name, DB_TD1, DT_##cppType, offsetof( TitleData1, usrStats.name ) )
  52. #include "xlast_csgo/inc_stats_usr.inc"
  53. #undef CFG
  54. // loadouts
  55. //we are using an array so we can pack this info as tight as possible to fit into the 1K block
  56. #define CFG( loadoutnum, equipmentnum ) \
  57. int numLoadouts = loadoutnum; \
  58. int numEquipmentSlots = equipmentnum;
  59. #include "xlast_csgo/inc_loadouts_usr.inc"
  60. #undef CFG
  61. int loadoutDataIndex = 0;
  62. for ( int team=0; team<2; ++team )
  63. {
  64. char teamName[10];
  65. if ( team == 0 )
  66. Q_snprintf( teamName, 10, "CT" );
  67. else
  68. Q_snprintf( teamName, 10, "T" );
  69. for (int i=0; i<numLoadouts; ++i)
  70. {
  71. for (int j=0; j<numEquipmentSlots; ++j)
  72. {
  73. char *loadoutName = new char[30];
  74. Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.EQUIP%.1d.ID", teamName, i, j );
  75. TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
  76. loadoutName = new char[30];
  77. Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.EQUIP%.1d.QUANTITY", teamName, i, j );
  78. TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
  79. }
  80. char *loadoutName = new char[30];
  81. Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.PRIMARY", teamName, i );
  82. TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
  83. loadoutName = new char[30];
  84. Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.SECONDARY", teamName, i );
  85. TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
  86. loadoutName = new char[30];
  87. Q_snprintf( loadoutName, 30, "%s.LOAD%.1d.FLAGS", teamName, i );
  88. TD_ENTRY( loadoutName, DB_TD3, DT_uint8, offsetof( TitleData3, loadoutData[ loadoutDataIndex++ ] ) );
  89. }
  90. }
  91. // medals
  92. #define CFG( buffer ) \
  93. for ( int i=0; i< buffer; ++i ) \
  94. { \
  95. char *pAwardedName = new char[ 20 ]; \
  96. Q_snprintf( pAwardedName, 20, "MEDALS.AWARDED%.3d", i ); \
  97. TD_ENTRY( pAwardedName, DB_TD2, DT_uint8, offsetof( TitleData2, CSMedalsAwarded[i] ) ) \
  98. char *pMedalInfoName = new char[ 25 ]; \
  99. Q_snprintf( pMedalInfoName, 25, "MEDALS.MEDALINFO%.3d", i ); \
  100. TD_ENTRY( pMedalInfoName, DB_TD2, DT_uint32, offsetof( TitleData2, CSMedalsMedalInfo[i] ) ) \
  101. }
  102. #include "xlast_csgo/inc_medals_usr.inc"
  103. #undef CFG
  104. #endif // _X360
  105. #if defined( _GAMECONSOLE )
  106. // system convars
  107. #define CFG( name, scfgType, cppType ) \
  108. TD_ENTRY( TITLE_DATA_PREFIX "CFG.sys." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvSystem.name ) )
  109. #include "xlast_csgo/inc_gameconsole_settings_sys.inc"
  110. #undef CFG
  111. // profile-specific convars
  112. #define CFG( name, scfgType, cppType ) \
  113. TD_ENTRY( TITLE_DATA_PREFIX "CFG.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUser.name ) ) \
  114. TD_ENTRY( TITLE_DATA_PREFIX "CFG.usrSS." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUserSS.name ) )
  115. #include "xlast_csgo/inc_gameconsole_settings_usr.inc"
  116. #include "xlast_csgo/inc_gameconsole_device_specific_settings_usr.inc"
  117. #undef CFG
  118. // joystick bindings
  119. #define ACTION( name )
  120. #define BINDING( name, cppType ) \
  121. TD_ENTRY( TITLE_DATA_PREFIX "BINDING." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, JoystickBindings.name ) )
  122. #include "xlast_csgo/inc_bindings_usr.inc"
  123. #undef BINDING
  124. #if defined( _PS3 )
  125. // PS3 also has keyboard bindings.
  126. #define BINDING( name, cppType ) \
  127. TD_ENTRY( TITLE_DATA_PREFIX "BINDING." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, JoystickBindings.name ) )
  128. #include "xlast_csgo/inc_ps3_key_bindings_usr.inc"
  129. #undef BINDING
  130. // For PS3 we have two additional sets of button bindings one for Sharp Shooter, the other for Move.
  131. // We also have a few device specific convar settings.
  132. // PS Move specific bindings and settings
  133. #define BINDING( name, cppType ) \
  134. TD_ENTRY( TITLE_DATA_PREFIX TITLE_DATA_DEVICE_MOVE_PREFIX "BINDING." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, JoystickBindings.PSMove.name ) )
  135. #include "xlast_csgo/inc_bindings_usr.inc"
  136. #undef BINDING
  137. #define CFG( name, scfgType, cppType ) \
  138. TD_ENTRY( TITLE_DATA_PREFIX TITLE_DATA_DEVICE_MOVE_PREFIX "CFG.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUser.PSMove.name ) )
  139. #include "xlast_csgo/inc_gameconsole_device_specific_settings_usr.inc"
  140. #undef CFG
  141. // Sharp Shooter specific bindings and settings
  142. #define BINDING( name, cppType ) \
  143. TD_ENTRY( TITLE_DATA_PREFIX TITLE_DATA_DEVICE_SHARP_SHOOTER_PREFIX "BINDING." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, JoystickBindings.SharpShooter.name ) )
  144. #include "xlast_csgo/inc_bindings_usr.inc"
  145. #undef BINDING
  146. #define CFG( name, scfgType, cppType ) \
  147. TD_ENTRY( TITLE_DATA_PREFIX TITLE_DATA_DEVICE_SHARP_SHOOTER_PREFIX "CFG.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, cvUser.SharpShooter.name ) )
  148. #include "xlast_csgo/inc_gameconsole_device_specific_settings_usr.inc"
  149. #undef CFG
  150. #endif
  151. #undef ACTION
  152. // Player Rankings by mode, controller, w/ optional history
  153. int rankIndex = 0;
  154. int numControllers = PlatformInputDevice::GetInputDeviceCountforPlatform();
  155. for ( int m = 0; m < ELOTitleData::NUM_GAME_MODES_ELO_RANKED; m++ )
  156. {
  157. for ( int c = 1; c <= numControllers; c++ )
  158. {
  159. char *pRankingName = new char[ 30 ];
  160. V_snprintf( pRankingName, 30, TITLE_DATA_PREFIX "ELO.MODE%d.CTR%d", m, c );
  161. TD_ENTRY( pRankingName, DB_TD3, DT_ELO, offsetof( TitleData3, playerRankingsData[ rankIndex ] ) );
  162. rankIndex++;
  163. }
  164. // Record the bracket and some info for calculating it. Only legal controllers are game console controllers.
  165. char *pBracketInfoName = new char[ 30 ];
  166. V_snprintf( pBracketInfoName, 30, TITLE_DATA_PREFIX"ELO.MODE%d.BRACKETINFO", m );
  167. TD_ENTRY( pBracketInfoName, DB_TD3, DT_uint16, offsetof( TitleData3, EloBracketInfo[ m ] ) );
  168. }
  169. #endif // _GAMECONSOLE
  170. #if defined ( _X360 )
  171. // matchmaking data
  172. #define CFG( cppType, name ) \
  173. TD_ENTRY( "MMDATA.usr." #name, DB_TD3, DT_##cppType, offsetof( TitleData3, usrMMData.name ) )
  174. #include "xlast_csgo/inc_mmdata_usr.inc"
  175. #undef CFG
  176. #endif // #if defined ( _X360 )
  177. // END MARKER
  178. TD_ENTRY( (const char*) NULL, DB_TD3, DT_0, 0 )
  179. #undef TD_ENTRY
  180. #if defined( _X360 )
  181. COMPILE_TIME_ASSERT( sizeof( TitleData1 ) < XPROFILE_SETTING_MAX_SIZE );
  182. COMPILE_TIME_ASSERT( sizeof( TitleData2 ) < XPROFILE_SETTING_MAX_SIZE );
  183. COMPILE_TIME_ASSERT( sizeof( TitleData3 ) < XPROFILE_SETTING_MAX_SIZE );
  184. #endif
  185. return s_tdfd.Base();
  186. }
  187. TitleDataFieldsDescription_t const * CMatchTitle::DescribeTitleDataStorage()
  188. {
  189. static TitleDataFieldsDescription_t const *s_pTDFD = PrepareTitleDataStorageDescription();
  190. return s_pTDFD;
  191. }
  192. TitleAchievementsDescription_t const * CMatchTitle::DescribeTitleAchievements()
  193. {
  194. static TitleAchievementsDescription_t tad[] =
  195. {
  196. //#include "left4dead2.xhelp.achtitledesc.txt"
  197. // END MARKER
  198. { NULL, 0 }
  199. };
  200. return tad;
  201. }
  202. TitleAvatarAwardsDescription_t const * CMatchTitle::DescribeTitleAvatarAwards()
  203. {
  204. static TitleAvatarAwardsDescription_t taad[] =
  205. {
  206. //#include "left4dead2.xhelp.avawtitledesc.txt"
  207. // END MARKER
  208. { NULL, 0 }
  209. };
  210. return taad;
  211. }
  212. TitleDlcDescription_t const * CMatchTitle::DescribeTitleDlcs()
  213. {
  214. static TitleDlcDescription_t tdlcs[] =
  215. {
  216. //{ PORTAL2_DLCID_COOP_BOT_SKINS, PORTAL2_DLC_APPID_COOP_BOT_SKINS, PORTAL2_DLC_PKGID_COOP_BOT_SKINS, "DLC.0x12" },
  217. //{ PORTAL2_DLCID_COOP_BOT_HELMETS, PORTAL2_DLC_APPID_COOP_BOT_HELMETS, PORTAL2_DLC_PKGID_COOP_BOT_HELMETS, "DLC.0x13" },
  218. //{ PORTAL2_DLCID_COOP_BOT_ANTENNA, PORTAL2_DLC_APPID_COOP_BOT_ANTENNA, PORTAL2_DLC_PKGID_COOP_BOT_ANTENNA, "DLC.0x14" },
  219. // END MARKER
  220. { 0, 0, 0 }
  221. };
  222. return tdlcs;
  223. }
  224. // Title leaderboards
  225. KeyValues * CMatchTitle::DescribeTitleLeaderboard( char const *szLeaderboardView )
  226. {
  227. #if !defined( NO_STEAM )
  228. if ( StringAfterPrefix( szLeaderboardView, "WINS_" ) )
  229. {
  230. if ( IsPC() || IsPS3() )
  231. {
  232. KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
  233. " :score wins_ratio " // :score is the leaderboard value mapped to game name "besttime"
  234. " :payloadformat { " // This describes the payload format.
  235. " payload0 { "
  236. " :score total_wins"
  237. " :format int "
  238. " :upload sum "
  239. " } "
  240. " payload1 { "
  241. " :score total_losses "
  242. " :format int "
  243. " :upload sum "
  244. " } "
  245. " payload2 { "
  246. " :score win_as_ct "
  247. " :format int "
  248. " :upload sum "
  249. " } "
  250. " payload3 { "
  251. " :score win_as_t "
  252. " :format int "
  253. " :upload sum "
  254. " } "
  255. " payload4 { "
  256. " :score loss_as_ct "
  257. " :format int "
  258. " :upload sum "
  259. " } "
  260. " payload5 { "
  261. " :score loss_as_t "
  262. " :format int "
  263. " :upload sum "
  264. " } "
  265. " } "
  266. );
  267. pSettings->SetString( ":scoreformula", "( payload0 / max( payload0 + payload1, 1 ) ) * ( min( payload0 + payload1, 20 ) / 20 ) * 10000000" );
  268. pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
  269. pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeNumeric ); // Note: this is actually 1/100th seconds type, Steam change pending
  270. pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodForceUpdate ); // Upload method when writing to leaderboard
  271. return pSettings;
  272. }
  273. }
  274. else if ( StringAfterPrefix( szLeaderboardView, "CS_" ) )
  275. {
  276. if ( IsPC() || IsPS3() )
  277. {
  278. KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
  279. " :score average_contribution " // :score is the leaderboard value mapped to game name "besttime"
  280. " :payloadformat { " // This describes the payload format.
  281. " payload0 { "
  282. " :score mvp_awards"
  283. " :format int "
  284. " :upload sum "
  285. " } "
  286. " payload1 { "
  287. " :score rounds_played "
  288. " :format int "
  289. " :upload sum "
  290. " } "
  291. " payload2 { "
  292. " :score total_contribution "
  293. " :format int "
  294. " :upload sum "
  295. " } "
  296. " payload3 { "
  297. " :score kills "
  298. " :format int "
  299. " :upload sum "
  300. " } "
  301. " payload4 { "
  302. " :score deaths "
  303. " :format int "
  304. " :upload sum "
  305. " } "
  306. " payload5 { "
  307. " :score damage "
  308. " :format int "
  309. " :upload sum "
  310. " } "
  311. " } "
  312. );
  313. pSettings->SetString( ":scoreformula", "( payload2 / max( payload1, 1 ) )" );
  314. pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
  315. pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeNumeric ); // Note: this is actually 1/100th seconds type, Steam change pending
  316. pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodForceUpdate ); // Upload method when writing to leaderboard
  317. return pSettings;
  318. }
  319. }
  320. else if ( StringAfterPrefix( szLeaderboardView, "KD_" ) )
  321. {
  322. if ( IsPC() || IsPS3() )
  323. {
  324. KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
  325. " :score kd_ratio " // :score is the leaderboard value mapped to game name "besttime"
  326. " :payloadformat { " // This describes the payload format.
  327. " payload0 { "
  328. " :score kills"
  329. " :format int "
  330. " :upload sum "
  331. " } "
  332. " payload1 { "
  333. " :score deaths "
  334. " :format int "
  335. " :upload sum "
  336. " } "
  337. " payload2 { "
  338. " :score rounds_played "
  339. " :format int "
  340. " :upload sum "
  341. " } "
  342. " payload3 { "
  343. " :score shots_fired "
  344. " :format int "
  345. " :upload sum "
  346. " } "
  347. " payload4 { "
  348. " :score head_shots "
  349. " :format int "
  350. " :upload sum "
  351. " } "
  352. " payload5 { "
  353. " :score shots_hit "
  354. " :format int "
  355. " :upload sum "
  356. " } "
  357. " } "
  358. );
  359. pSettings->SetString( ":scoreformula", "( payload0 / max( payload1, 1 ) ) * ( min( payload2, 20 ) / 20 ) * 10000000" );
  360. pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
  361. pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeNumeric ); // Note: this is actually 1/100th seconds type, Steam change pending
  362. pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodForceUpdate ); // Upload method when writing to leaderboard
  363. return pSettings;
  364. }
  365. }
  366. else if ( StringAfterPrefix( szLeaderboardView, "STARS_" ) )
  367. {
  368. if ( IsPC() || IsPS3() )
  369. {
  370. KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
  371. " :score numstars " // :score is the leaderboard value mapped to game name "besttime"
  372. " :scoresum 1 "
  373. " :payloadformat { " // This describes the payload format.
  374. " payload0 { "
  375. " :score bombs_planted "
  376. " :format int "
  377. " :upload sum "
  378. " } "
  379. " payload1 { "
  380. " :score bombs_detonated "
  381. " :format int "
  382. " :upload sum "
  383. " } "
  384. " payload2 { "
  385. " :score bombs_defused "
  386. " :format int "
  387. " :upload sum "
  388. " } "
  389. " payload3 { "
  390. " :score hostages_rescued "
  391. " :format int "
  392. " :upload sum "
  393. " } "
  394. " } "
  395. );
  396. pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
  397. pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeNumeric ); // Note: this is actually 1/100th seconds type, Steam change pending
  398. pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodKeepBest ); // Upload method when writing to leaderboard
  399. return pSettings;
  400. }
  401. }
  402. else if ( StringAfterPrefix( szLeaderboardView, "GP_" ) )
  403. {
  404. if ( IsPC() || IsPS3() )
  405. {
  406. KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
  407. " :score num_rounds " // :score is the leaderboard value mapped to game name "besttime"
  408. " :scoresum 1 "
  409. " :payloadformat { " // This describes the payload format.
  410. " payload0 { "
  411. " :score time_played "
  412. " :format uint64 "
  413. " :upload sum "
  414. " } "
  415. " payload1 { "
  416. " :score time_played_ct "
  417. " :format uint64 "
  418. " :upload sum "
  419. " } "
  420. " payload2 { "
  421. " :score time_played_t "
  422. " :format uint64 "
  423. " :upload sum "
  424. " } "
  425. " payload3 { "
  426. " :score total_medals "
  427. " :format int "
  428. " :upload last " // the last value written is the authoritative value of total achievement medals unlocked
  429. " } "
  430. " } "
  431. );
  432. pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
  433. pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeNumeric ); // Note: this is actually 1/100th seconds type, Steam change pending
  434. pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodKeepBest ); // Upload method when writing to leaderboard
  435. return pSettings;
  436. }
  437. }
  438. #endif // !NO_STEAM
  439. /*
  440. // Check if this is a survival leaderboard
  441. if ( char const *szSurvivalMap = StringAfterPrefix( szLeaderboardView, "survival_" ) )
  442. {
  443. if ( IsX360() )
  444. {
  445. // Find the corresponding record in the mission script
  446. KeyValues *pSettings = new KeyValues( "settings" );
  447. KeyValues::AutoDelete autodelete_pSettings( pSettings );
  448. pSettings->SetString( "game/mode", "survival" );
  449. KeyValues *pMissionInfo = NULL;
  450. KeyValues *pMapInfo = g_pMatchExtL4D->GetMapInfoByBspName( pSettings, szSurvivalMap, &pMissionInfo );
  451. if ( !pMapInfo || !pMissionInfo )
  452. return NULL;
  453. // Find the leaderboard description in the map info
  454. KeyValues *pLbDesc = pMapInfo->FindKey( "x360leaderboard" );
  455. if ( !pLbDesc )
  456. return NULL;
  457. // Insert the required keys
  458. pLbDesc = pLbDesc->MakeCopy();
  459. static KeyValues *s_pRatingKey = KeyValues::FromString( ":rating", // X360 leaderboards are rated
  460. " name besttime " // game name of the rating field is "besttime"
  461. " type uint64 " // type is uint64
  462. " rule max" // rated field must be greater than cached value so that it can be written
  463. );
  464. pLbDesc->AddSubKey( s_pRatingKey->MakeCopy() );
  465. pLbDesc->SetString( "besttime/type", "uint64" );
  466. return pLbDesc;
  467. }
  468. if ( IsPC() || IsPS3() )
  469. {
  470. KeyValues *pSettings = KeyValues::FromString( "SteamLeaderboard",
  471. " :score besttime " // :score is the leaderboard value mapped to game name "besttime"
  472. );
  473. pSettings->SetInt( ":sort", k_ELeaderboardSortMethodDescending ); // Sort order when fetching and displaying leaderboard data
  474. pSettings->SetInt( ":format", k_ELeaderboardDisplayTypeTimeMilliSeconds ); // Note: this is actually 1/100th seconds type, Steam change pending
  475. pSettings->SetInt( ":upload", k_ELeaderboardUploadScoreMethodKeepBest ); // Upload method when writing to leaderboard
  476. return pSettings;
  477. }
  478. }
  479. */
  480. return NULL;
  481. }