the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at master 539 lines 20 kB view raw
1#include "stdafx.h" 2#include "Include\SenClientCore.h" 3#include "Include\SenClientMain.h" 4#include "Include\SenClientMain.h" 5 6#include "..\GameConfig\Minecraft.spa.h" 7#include "..\..\Minecraft.h" 8#include "..\..\MultiplayerLocalPlayer.h" 9#include "..\..\..\Minecraft.World\Dimension.h" 10#include "..\..\..\Minecraft.World\net.minecraft.world.level.h" 11#include "..\..\..\Minecraft.World\net.minecraft.world.level.storage.h" 12 13#include "SentientManager.h" 14#include "MinecraftTelemetry.h" 15#include "DynamicConfigurations.h" 16 17// Global instance 18CTelemetryManager *TelemetryManager = new CSentientManager(); 19 20HRESULT CSentientManager::Init() 21{ 22 Sentient::SenSysTitleID sentitleID; 23 sentitleID = (Sentient::SenSysTitleID)TITLEID_MINECRAFT; 24 25 HRESULT hr = SentientInitialize( sentitleID ); 26 27 m_lastHeartbeat = m_initialiseTime; 28 29 m_bFirstFlush = true; 30 31 return hr; 32} 33 34HRESULT CSentientManager::Tick() 35{ 36 HRESULT hr = S_OK; 37 38 // Update Sentient System 39 HRESULT sentientResult = Sentient::SentientUpdate(); 40 41 switch(sentientResult) 42 { 43 case S_OK: 44 { 45 // Sentient is connected 46 //OutputDebugString ("\nSentient: CONNECTED\n"); 47 if(g_NetworkManager.IsInSession()) 48 { 49 float currentTime = app.getAppTime(); 50 if(currentTime - m_lastHeartbeat > 60) 51 { 52 m_lastHeartbeat = currentTime; 53 for(DWORD i = 0; i < XUSER_MAX_COUNT; ++i) 54 { 55 if(Minecraft::GetInstance()->localplayers[i] != NULL) 56 { 57 SenStatHeartBeat(i, m_lastHeartbeat - m_initialiseTime); 58 } 59 } 60 } 61 62 if(m_bFirstFlush) 63 { 64 for(DWORD i = 0; i < XUSER_MAX_COUNT; ++i) 65 { 66 if(Minecraft::GetInstance()->localplayers[i] != NULL && m_fLevelStartTime[i] - currentTime > 60) 67 { 68 Flush(); 69 } 70 } 71 } 72 } 73 74 MinecraftDynamicConfigurations::Tick(); 75 } 76 break; 77 78 case Sentient::SENTIENT_S_NOT_SIGNED_IN_TO_LIVE: 79 { 80 // Login required 81 //DebugPrintf("\nSentient: WARNING: an Xbox LIVE-enabled user needs to be logged in.\n"); 82 83 // Add title specific code here. . . 84 } 85 break; 86 87 case Sentient::SENTIENT_S_INITIALIZING_CONNECTION: 88 { 89 // Server connection in progress 90 app.DebugPrintf("Sentient: Establishing connection to sentient server.\n"); 91 92 // Add title specific code here. . . 93 } 94 break; 95 96 case Sentient::SENTIENT_S_SERVER_CONNECTION_FAILED: 97 { 98 // Server connection failed 99 app.DebugPrintf("\nSentient: WARNING: connection to sentient server failed.\n"); 100 101 // Add title specific code here. . . 102 } 103 break; 104 105 default: 106 { 107 // Unknown failure 108 app.DebugPrintf("Sentient: Unknown result from SentientUpdate()"); 109 110 // Add title specific code here. . . 111 } 112 break; 113 } 114 115 return hr; 116} 117 118HRESULT CSentientManager::Flush() 119{ 120 m_bFirstFlush = false; 121 return Sentient::SentientFlushStats(); 122} 123 124BOOL CSentientManager::RecordPlayerSessionStart(DWORD dwUserId) 125{ 126 return SenStatPlayerSessionStart( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetTitleBuildId(), 0, 0, 0, (INT)app.getDeploymentType() ); 127} 128 129BOOL CSentientManager::RecordPlayerSessionExit(DWORD dwUserId, int _) 130{ 131 return SenStatPlayerSessionExit( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId) ); 132} 133 134BOOL CSentientManager::RecordHeartBeat(DWORD dwUserId) 135{ 136 // Handled elswhere 137 return FALSE; 138} 139 140BOOL CSentientManager::RecordLevelStart(DWORD dwUserId, ESen_FriendOrMatch friendsOrMatch, ESen_CompeteOrCoop competeOrCoop, int difficulty, DWORD numberOfLocalPlayers, DWORD numberOfOnlinePlayers) 141{ 142 if(dwUserId == ProfileManager.GetPrimaryPad() ) m_bFirstFlush = true; 143 144 ++m_levelInstanceID; 145 m_fLevelStartTime[dwUserId] = app.getAppTime(); 146 return SenStatLevelStart( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID(), GetSingleOrMultiplayer(), friendsOrMatch, competeOrCoop, GetDifficultyLevel(difficulty), numberOfLocalPlayers, numberOfOnlinePlayers, GetLicense(), GetDefaultGameControls(), GetAudioSettings(dwUserId), 0, 0 ); 147} 148 149BOOL CSentientManager::RecordLevelExit(DWORD dwUserId, ESen_LevelExitStatus levelExitStatus) 150{ 151 float levelDuration = app.getAppTime() - m_fLevelStartTime[dwUserId]; 152 return SenStatLevelExit( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID(), levelExitStatus, GetLevelExitProgressStat1(), GetLevelExitProgressStat2(), (INT)levelDuration ); 153} 154 155BOOL CSentientManager::RecordLevelSaveOrCheckpoint(DWORD dwUserId, INT saveOrCheckPointID, INT saveSizeInBytes) 156{ 157 float levelDuration = app.getAppTime() - m_fLevelStartTime[dwUserId]; 158 return SenStatLevelSaveOrCheckpoint( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID(), GetLevelExitProgressStat1(), GetLevelExitProgressStat2(), (INT)levelDuration, saveOrCheckPointID, saveSizeInBytes ); 159} 160 161BOOL CSentientManager::RecordLevelResume(DWORD dwUserId, ESen_FriendOrMatch friendsOrMatch, ESen_CompeteOrCoop competeOrCoop, int difficulty, DWORD numberOfLocalPlayers, DWORD numberOfOnlinePlayers, INT saveOrCheckPointID) 162{ 163 return SenStatLevelResume( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID(), GetSingleOrMultiplayer(), friendsOrMatch, competeOrCoop, GetDifficultyLevel(difficulty), numberOfLocalPlayers, numberOfOnlinePlayers, GetLicense(), GetDefaultGameControls(), saveOrCheckPointID, GetAudioSettings(dwUserId), 0, 0 ); 164} 165 166 167BOOL CSentientManager::RecordPauseOrInactive(DWORD dwUserId) 168{ 169 return SenStatPauseOrInactive( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID() ); 170} 171 172BOOL CSentientManager::RecordUnpauseOrActive(DWORD dwUserId) 173{ 174 return SenStatUnpauseOrActive( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID() ); 175} 176 177BOOL CSentientManager::RecordMenuShown(DWORD dwUserId, INT menuID, INT optionalMenuSubID) 178{ 179 return SenStatMenuShown( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), menuID, optionalMenuSubID, GetLevelInstanceID(), GetMultiplayerInstanceID() ); 180} 181 182BOOL CSentientManager::RecordAchievementUnlocked(DWORD dwUserId, INT achievementID, INT achievementGamerscore) 183{ 184 return SenStatAchievementUnlocked( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID(), achievementID, achievementGamerscore ); 185} 186 187BOOL CSentientManager::RecordMediaShareUpload(DWORD dwUserId, ESen_MediaDestination mediaDestination, ESen_MediaType mediaType) 188{ 189 return SenStatMediaShareUpload( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID(), mediaDestination, mediaType ); 190} 191 192BOOL CSentientManager::RecordUpsellPresented(DWORD dwUserId, ESen_UpsellID upsellId, INT marketplaceOfferID) 193{ 194 return SenStatUpsellPresented( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID(), upsellId, marketplaceOfferID ); 195} 196 197BOOL CSentientManager::RecordUpsellResponded(DWORD dwUserId, ESen_UpsellID upsellId, INT marketplaceOfferID, ESen_UpsellOutcome upsellOutcome) 198{ 199 return SenStatUpsellResponded( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID(), upsellId, marketplaceOfferID, upsellOutcome ); 200} 201 202BOOL CSentientManager::RecordPlayerDiedOrFailed(DWORD dwUserId, INT lowResMapX, INT lowResMapY, INT lowResMapZ, INT mapID, INT playerWeaponID, INT enemyWeaponID, ETelemetryChallenges enemyTypeID) 203{ 204 INT secs = GetSecondsSinceInitialize(); 205 return SenStatPlayerDiedOrFailed( dwUserId, GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID(), lowResMapX, lowResMapY, lowResMapZ, mapID, playerWeaponID, enemyWeaponID, enemyTypeID, secs, secs ); 206} 207 208BOOL CSentientManager::RecordEnemyKilledOrOvercome(DWORD dwUserId, INT lowResMapX, INT lowResMapY, INT lowResMapZ, INT mapID, INT playerWeaponID, INT enemyWeaponID, ETelemetryChallenges enemyTypeID) 209{ 210 INT secs = GetSecondsSinceInitialize(); 211 return SenStatEnemyKilledOrOvercome( dwUserId, GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID(), lowResMapX, lowResMapY, lowResMapZ, mapID, playerWeaponID, enemyWeaponID, enemyTypeID, secs, secs ); 212} 213 214BOOL CSentientManager::RecordSkinChanged(DWORD dwUserId, DWORD dwSkinId) 215{ 216 return SenStatSkinChanged( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID(), dwSkinId ); 217} 218 219BOOL CSentientManager::RecordBanLevel(DWORD dwUserId) 220{ 221 return SenStatBanLevel( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID() ); 222} 223 224BOOL CSentientManager::RecordUnBanLevel(DWORD dwUserId) 225{ 226 return SenStatUnBanLevel( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID() ); 227} 228 229BOOL CSentientManager::RecordTexturePackLoaded(DWORD dwUserId, INT texturePackId, INT purchased) 230{ 231 return SenStatTexturePackChanged( dwUserId, GetSecondsSinceInitialize(), GetMode(dwUserId), GetSubMode(dwUserId), GetLevelId(dwUserId), GetSubLevelId(dwUserId), GetLevelInstanceID(), GetMultiplayerInstanceID(), texturePackId, purchased ); 232} 233 234 235/* 236Number of seconds elapsed since Sentient initialize. 237Title needs to track this and report it as a property. 238These times will be used to create timelines and understand durations. 239This should be tracked independently of saved games (restoring a save should not reset the seconds since initialize) 240*/ 241INT CSentientManager::GetSecondsSinceInitialize() 242{ 243 return (INT)(app.getAppTime() - m_initialiseTime); 244} 245 246/* 247An in-game setting that significantly differentiates the play style of the game. 248(This should be captured as an integer and correspond to mode specific to the game.) 249Teams will have to provide the game mappings that correspond to the integers. 250The intent is to allow teams to capture data on the highest level categories of gameplay in their game. 251For example, a game mode could be the name of the specific mini game (eg: golf vs darts) or a specific multiplayer mode (eg: hoard vs beast.) ModeID = 0 means undefined or unknown. 252The intent is to answer the question "How are players playing your game?" 253*/ 254INT CSentientManager::GetMode(DWORD dwUserId) 255{ 256 INT mode = (INT)eTelem_ModeId_Undefined; 257 258 Minecraft *pMinecraft = Minecraft::GetInstance(); 259 260 if( pMinecraft->localplayers[dwUserId] != NULL && pMinecraft->localplayers[dwUserId]->level != NULL && pMinecraft->localplayers[dwUserId]->level->getLevelData() != NULL ) 261 { 262 GameType *gameType = pMinecraft->localplayers[dwUserId]->level->getLevelData()->getGameType(); 263 264 if (gameType->isSurvival()) 265 { 266 mode = (INT)eTelem_ModeId_Survival; 267 } 268 else if (gameType->isCreative()) 269 { 270 mode = (INT)eTelem_ModeId_Creative; 271 } 272 else 273 { 274 mode = (INT)eTelem_ModeId_Undefined; 275 } 276 } 277 return mode; 278} 279 280/* 281Used when a title has more heirarchy required. 282OptionalSubMode ID = 0 means undefined or unknown. 283For titles that have sub-modes (Sports/Football). 284Mode is always an indicator of "How is the player choosing to play my game?" so these do not have to be consecutive. 285LevelIDs and SubLevelIDs can be reused as they will always be paired with a Mode/SubModeID, Mode should be unique - SubMode can be shared between modes. 286*/ 287INT CSentientManager::GetSubMode(DWORD dwUserId) 288{ 289 INT subMode = (INT)eTelem_SubModeId_Undefined; 290 291 if(Minecraft::GetInstance()->isTutorial()) 292 { 293 subMode = (INT)eTelem_SubModeId_Tutorial; 294 } 295 else 296 { 297 subMode = (INT)eTelem_SubModeId_Normal; 298 } 299 300 return subMode; 301} 302 303/* 304This is a more granular view of mode, allowing teams to get a sense of the levels or maps players are playing and providing some insight into how players progress through a game. 305Teams will have to provide the game mappings that correspond to the integers. 306The intent is that a level is highest level at which modes can be dissected and provides an indication of player progression in a game. 307The intent is that level start and ends do not occur more than every 2 minutes or so, otherwise the data reported will be difficult to understand. 308Levels are unique only within a given modeID - so you can have a ModeID =1, LevelID =1 and a different ModeID=2, LevelID = 1 indicate two completely different levels. 309LevelID = 0 means undefined or unknown. 310*/ 311INT CSentientManager::GetLevelId(DWORD dwUserId) 312{ 313 INT levelId = (INT)eTelem_LevelId_Undefined; 314 315 levelId = (INT)eTelem_LevelId_PlayerGeneratedLevel; 316 317 return levelId; 318} 319 320/* 321Used when a title has more heirarchy required. OptionalSubLevel ID = 0 means undefined or unknown. 322For titles that have sub-levels. 323Level is always an indicator of "How far has the player progressed." so when possible these should be consecutive or at least monotonically increasing. 324LevelIDs and SubLevelIDs can be reused as they will always be paired with a Mode/SubModeID 325*/ 326INT CSentientManager::GetSubLevelId(DWORD dwUserId) 327{ 328 INT subLevelId = (INT)eTelem_SubLevelId_Undefined; 329 330 Minecraft *pMinecraft = Minecraft::GetInstance(); 331 332 if(pMinecraft->localplayers[dwUserId] != NULL) 333 { 334 switch(pMinecraft->localplayers[dwUserId]->dimension) 335 { 336 case 0: 337 subLevelId = (INT)eTelem_SubLevelId_Overworld; 338 break; 339 case -1: 340 subLevelId = (INT)eTelem_SubLevelId_Nether; 341 break; 342 case 1: 343 subLevelId = (INT)eTelem_SubLevelId_End; 344 break; 345 }; 346 } 347 348 return subLevelId; 349} 350 351/* 352Build version of the title, used to track changes in development as well as patches/title updates 353Allows developer to separate out stats from different builds 354*/ 355INT CSentientManager::GetTitleBuildId() 356{ 357 return (INT)VER_PRODUCTBUILD; 358} 359 360/* 361Generated by the game every time LevelStart or LevelResume is called. 362This should be a unique ID (can be sequential) within a session. 363Helps differentiate level attempts when a play plays the same mode/level - especially with aggregated stats 364*/ 365INT CSentientManager::GetLevelInstanceID() 366{ 367 return (INT)m_levelInstanceID; 368} 369 370/* 371MultiplayerinstanceID is a title-generated value that is the same for all players in the same multiplayer session. 372Link up players into a single multiplayer session ID. 373*/ 374INT CSentientManager::GetMultiplayerInstanceID() 375{ 376 return m_multiplayerInstanceID; 377} 378 379INT CSentientManager::GenerateMultiplayerInstanceId() 380{ 381 FILETIME SystemTimeAsFileTime; 382 GetSystemTimeAsFileTime( &SystemTimeAsFileTime ); 383 384 return *((INT *)&SystemTimeAsFileTime.dwLowDateTime); 385} 386 387void CSentientManager::SetMultiplayerInstanceId(INT value) 388{ 389 m_multiplayerInstanceID = value; 390} 391 392/* 393Indicates whether the game is being played in single or multiplayer mode and whether multiplayer is being played locally or over live. 394How social is your game? How do people play it? 395*/ 396INT CSentientManager::GetSingleOrMultiplayer() 397{ 398 INT singleOrMultiplayer = (INT)eSen_SingleOrMultiplayer_Undefined; 399 400 // Unused 401 //eSen_SingleOrMultiplayer_Single_Player 402 //eSen_SingleOrMultiplayer_Multiplayer_Live 403 404 if(app.GetLocalPlayerCount() == 1 && g_NetworkManager.GetOnlinePlayerCount() == 0) 405 { 406 singleOrMultiplayer = (INT)eSen_SingleOrMultiplayer_Single_Player; 407 } 408 else if(app.GetLocalPlayerCount() > 1 && g_NetworkManager.GetOnlinePlayerCount() == 0) 409 { 410 singleOrMultiplayer = (INT)eSen_SingleOrMultiplayer_Multiplayer_Local; 411 } 412 else if(app.GetLocalPlayerCount() == 1 && g_NetworkManager.GetOnlinePlayerCount() > 0) 413 { 414 singleOrMultiplayer = (INT)eSen_SingleOrMultiplayer_Multiplayer_Live; 415 } 416 else if(app.GetLocalPlayerCount() > 1 && g_NetworkManager.GetOnlinePlayerCount() > 0) 417 { 418 singleOrMultiplayer = (INT)eSen_SingleOrMultiplayer_Multiplayer_Both_Local_and_Live; 419 } 420 421 return singleOrMultiplayer; 422} 423 424/* 425An in-game setting that differentiates the challenge imposed on the user. 426Normalized to a standard 5-point scale. Are players changing the difficulty? 427*/ 428INT CSentientManager::GetDifficultyLevel(INT diff) 429{ 430 INT difficultyLevel = (INT)eSen_DifficultyLevel_Undefined; 431 432 switch(diff) 433 { 434 case 0: 435 difficultyLevel = (INT)eSen_DifficultyLevel_Easiest; 436 break; 437 case 1: 438 difficultyLevel = (INT)eSen_DifficultyLevel_Easier; 439 break; 440 case 2: 441 difficultyLevel = (INT)eSen_DifficultyLevel_Normal; 442 break; 443 case 3: 444 difficultyLevel = (INT)eSen_DifficultyLevel_Harder; 445 break; 446 } 447 448 // Unused 449 //eSen_DifficultyLevel_Hardest = 5, 450 451 return difficultyLevel; 452} 453 454/* 455Differentiates trial/demo from full purchased titles 456Is this a full title or demo? 457*/ 458INT CSentientManager::GetLicense() 459{ 460 INT license = eSen_License_Undefined; 461 462 if(ProfileManager.IsFullVersion()) 463 { 464 license = (INT)eSen_License_Full_Purchased_Title; 465 } 466 else 467 { 468 license = (INT)eSen_License_Trial_or_Demo; 469 } 470 return license; 471} 472 473/* 474This is intended to capture whether players played using default control scheme or customized the control scheme. 475Are players customizing your controls? 476*/ 477INT CSentientManager::GetDefaultGameControls() 478{ 479 INT defaultGameControls = eSen_DefaultGameControls_Undefined; 480 481 // Unused 482 //eSen_DefaultGameControls_Custom_controls 483 484 defaultGameControls = eSen_DefaultGameControls_Default_controls; 485 486 return defaultGameControls; 487} 488 489/* 490Are players changing default audio settings? 491This is intended to capture whether players are playing with or without volume and whether they make changes from the default audio settings. 492*/ 493INT CSentientManager::GetAudioSettings(DWORD dwUserId) 494{ 495 INT audioSettings = (INT)eSen_AudioSettings_Undefined; 496 497 if(dwUserId == ProfileManager.GetPrimaryPad()) 498 { 499 BYTE volume = app.GetGameSettings(dwUserId,eGameSetting_SoundFXVolume); 500 501 if(volume == 0) 502 { 503 audioSettings = (INT)eSen_AudioSettings_Off; 504 } 505 else if(volume == DEFAULT_VOLUME_LEVEL) 506 { 507 audioSettings = (INT)eSen_AudioSettings_On_Default; 508 } 509 else 510 { 511 audioSettings = (INT)eSen_AudioSettings_On_CustomSetting; 512 } 513 } 514 return audioSettings; 515} 516 517/* 518Refers to the highest level performance metric for your game. 519For example, a performance metric could points earned, race time, total kills, etc. 520This is entirely up to you and will help us understand how well the player performed, or how far the player progressed �in the level before exiting. 521How far did users progress before failing/exiting the level? 522*/ 523INT CSentientManager::GetLevelExitProgressStat1() 524{ 525 // 4J Stu - Unused 526 return 0; 527} 528 529/* 530Refers to the highest level performance metric for your game. 531For example, a performance metric could points earned, race time, total kills, etc. 532This is entirely up to you and will help us understand how well the player performed, or how far the player progressed �in the level before exiting. 533How far did users progress before failing/exiting the level? 534*/ 535INT CSentientManager::GetLevelExitProgressStat2() 536{ 537 // 4J Stu - Unused 538 return 0; 539}