Files
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

2119 lines
62 KiB
C++

//
// name: app.cpp
// description: Basic application startup and shutdown
//
// rage headers
#if __XENON
#include "system/xtl.h"
#endif
#include "diag/tracker_remote2.h"
#include "grprofile/timebars.h"
#include "input/dongle.h"
#include "net/nethardware.h"
#include "profile/telemetry.h"
#include "rline/rlnp.h"
#include "rline/rlnptrophy.h"
#include "rline/scmatchmaking/rlscmatchmanager.h"
#include "system/controlMgr.h"
#include "system/fileMgr.h"
#include "system/StreamingInstall.winrt.h"
#include "system/param.h"
#include "system/system.h"
#include "telemetry/telemetry_rage.h"
#include "bink/movie.h"
//framework
#include "fwnet/netremotelog.h"
#include "fwsys/timer.h"
// game headers
#include "app.h"
#include "game.h"
#include "memorytest.h"
#include "audio/frontendaudioentity.h"
#include "audio/northaudioengine.h"
#include "audiohardware/driver.h"
#include "camera/CamInterface.h"
#include "camera/viewports/ViewportManager.h"
#include "control/gamelogic.h"
#include "Control/replay/Replay.h"
#include "core/dongle.h"
#include "Cutscene/CutSceneManagerNew.h"
#include "debug/debug.h"
#include "frontend/GameStreamMgr.h"
#include "frontend/landing_page/LandingPageArbiter.h"
#include "frontend/loadingscreens.h"
#include "frontend/PauseMenu.h"
#include "frontend/ProfileSettings.h"
#include "frontend/WarningScreen.h"
#include "game/Clock.h"
#include "ik/IkManager.h"
#include "modelinfo/mlomodelinfo.h"
#include "network/live/livemanager.h"
#include "network/events/NetworkEventTypes.h"
#include "network/Objects/NetworkObjectPopulationMgr.h"
#include "network/Sessions/NetworkSession.h"
#include "tools/SectorTools.h"
#include "tools/TracingStats.h"
#include "objects/DummyObject.h"
#include "pathserver/navgenparam.h"
#include "profile/cputrace.h"
#include "renderer/renderThread.h"
#include "renderer/DrawLists/drawListMgr.h"
#include "renderer/DrawLists/drawListNY.h"
#include "replaycoordinator/ReplayCoordinator.h"
#include "saveload/savegame_autoload.h"
#include "saveload/GenericGameStorage.h"
#include "scene/scene.h"
#include "script/script.h"
#include "streaming/populationstreaming.h"
#include "streaming/streaming.h"
#include "system/EntitlementManager.h"
#include "system/PMCPerfControl.h"
#include "system/gcmPerfmon.h"
#include "system/param.h"
#include "system/taskscheduler.h"
#include "system/telemetry.h"
#include "vehicles/vehiclepopulation.h"
#include "security/ragesecengine.h"
#include "Weapons/Projectiles/Projectile.h"
#if __PPU
#include "sn/libsn.h"
#include <stdlib.h> // for malloc_stats
#include <sys/memory.h>
#include <np.h>
#include <cell/sysmodule.h>
#include <sysutil/sysutil_gamecontent.h>
#endif // __PPU
#if __WIN32PC && __STEAM_BUILD
#pragma warning(disable: 4265)
#include "../../3rdParty/Steam/public/steam/steam_api.h"
#pragma warning(error: 4265)
#pragma comment(lib, "steam_api64.lib")
#include "data/certificateverify.h"
#include "diag/seh.h"
#endif
#if EPIC_API_SUPPORTED
#include "system/epic.h"
#endif
#if RSG_LAUNCHER_CHECK
#define CHECK_BOOTSTRAP_PROCESS "PlayGTAV.exe"
#define CHECK_PARENT_PROCESS "GTAVLauncher.exe"
#define CHECK_MTL_PROCESS "Launcher.exe"
#define CHECK_MTL_STEAMHELPER "RockstarSteamHelper.exe"
#include "system/xtl.h"
#include <tlhelp32.h>
#include <Psapi.h>
#endif // RSG_LAUNCHER_CHECK
#include "Control/replay/ReplaySettings.h"
#include "Network/Objects/NetworkObjectMgr.h"
#if !__FINAL
PARAM(trackerreport, "[GAME] Filename for the initial memory track");
namespace rage
{
XPARAM(forceboothdd);
XPARAM(noSocialClub);
XPARAM(BlockOnLostFocus);
extern char* XEX_TITLE_ID_HDD; // defined in gta5\src\dev\game\Core\main.cpp
}
#endif // !__FINAL
#if RAGE_TIMEBARS
#if __FINAL_LOGGING
NOSTRIP_PARAM(dumptimebarsonlongmainthread, "[app] Dump timebars if the main thread takes a certain amount of time (optionally time in ms as argument)");
NOSTRIP_PARAM(disableframemarkers, "[PROFILE] Disable frame encapsulating user markers, make Razor dump all per frame timings into one large bucket");
#else
PARAM(dumptimebarsonlongmainthread, "[app] Dump timebars if the main thread takes a certain amount of time (optionally time in ms as argument)");
PARAM(disableframemarkers, "[PROFILE] Disable frame encapsulating user markers, make Razor dump all per frame timings into one large bucket");
#endif
#endif // RAGE_TIMEBARS
#if RSG_PC
NOSTRIP_PC_XPARAM(benchmark);
#endif
#if RAGE_TRACKING
NOSTRIP_XPARAM(autoMemTrack);
#endif // RAGE_TRACKING
// Builds given to Natural Motion must be dongled. The NM_BUILD preprocessor variable is be defined locally when
// creating a build from NM.
#ifdef NM_BUILD
#define USE_DONGLE (ENABLE_DONGLE & 1)
#else // NM_BUILD
#define USE_DONGLE (ENABLE_DONGLE & 1)
#endif // NM_BUILD
#if USE_DONGLE
PARAM(testdongle, "dongle string to encode");
#endif
#if ENABLE_DONGLE
static const char* sEncodeString = "testingtesting123";
static const char* sMCFileName = "root.sys";
#endif
#if !__FINAL
sysTimer initTimer;
#endif
#if __BANK
bool g_maponlyHogEnabled = false;
bool g_cheapmodeHogEnabled = false;
bool g_maponlyExtraHogEnabled = false;
bool g_stealExtraMemoryEnabled = false;
#endif
#if RSG_ORBIS || BACKTRACE_ENABLED
static const size_t sCoredumpHandlerStackSize = 16 * 1024;
static char s_ThreadlStack[256];
static char s_debugInfoSummary[1024];
static void ChanneledOutputForStackTrace(const char* fmt, ...)
{
char buffer[256];
va_list args;
va_start(args, fmt);
vformatf(buffer, sizeof(buffer), fmt, args);
va_end(args);
safecat(s_ThreadlStack, buffer);
safecat(s_ThreadlStack, "\n");
}
#if RSG_ORBIS
static void WriteUserDataAsText(const char* format, ...)
{
static char formatted_text[1024]; // temporary log buffer
va_list ap;
va_start(ap, format);
int size = vsnprintf(formatted_text, sizeof(formatted_text), format, ap);
va_end(ap);
sceCoredumpWriteUserData(formatted_text, size);
}
#else
static HANDLE s_crashContextHandle = INVALID_HANDLE_VALUE;
static void WriteUserDataAsText(const char* format, ...)
{
if (s_crashContextHandle == INVALID_HANDLE_VALUE)
return;
static char formatted_text[1024]; // temporary log buffer
va_list ap;
va_start(ap, format);
int size = vsnprintf(formatted_text, sizeof(formatted_text), format, ap);
va_end(ap);
WriteFile(s_crashContextHandle, formatted_text, (DWORD)size, NULL, NULL);
}
#endif
//The maximum total size for user data is 524288 bytes (512 Kb)
static int CoredumpHandler(void * UNUSED_PARAM(pCommon))
{
WriteUserDataAsText("###### DEBUG INFO ######\n\n");
//-------
// int streamVirtual=0, streamPhysical=0;
// CStreamingDebug::CalculateRequestedObjectSizes(streamVirtual, streamPhysical);
// streamVirtual >>= 10;
// streamPhysical >>= 10;
u32 milliseconds = fwTimer::GetSystemTimeInMilliseconds();
u32 seconds = (u32) (milliseconds / 1000) % 60 ;
u32 minutes = (u32) ((milliseconds / (1000*60)) % 60);
u32 hours = (u32) ((milliseconds / (1000*60*60)) % 24);
char gameState[32];
CApp* app = CApp::GetInstance();
if (app)
{
switch (app->GetState())
{
case CApp::State_InitSystem: sprintf(gameState, "System Init"); break;
case CApp::State_InitGame: sprintf(gameState, "Game Init"); break;
case CApp::State_RunGame: sprintf(gameState, "Game Running"); break;
case CApp::State_ShutdownGame: sprintf(gameState, "Game Shutdown"); break;
case CApp::State_ShutdownSystem: sprintf(gameState, "System Shutdown"); break;
default: sprintf(gameState, "Unknown"); break;
}
}
WriteUserDataAsText("============ SYSTEM INFO ============\n");
WriteUserDataAsText("Game State : %s\n", gameState);
WriteUserDataAsText("System Time (%dh %dm %ds)\n", hours, minutes, seconds);
WriteUserDataAsText("Raw System Time (%dms)\n", sysTimer::GetSystemMsTime());
WriteUserDataAsText("Frame (%d) - Paused (%s)\n", fwTimer::GetFrameCount(), fwTimer::IsGamePaused()?"yes":"no");
WriteUserDataAsText("Streaming Requests (%d)\n", strStreamingEngine::GetInfo().GetNumberRealObjectsRequested_Cached());
WriteUserDataAsText(" HDD: %s, ODD: %s\n", IsDeviceLocked(pgStreamer::HARDDRIVE)?"locked":"unlocked", IsDeviceLocked(pgStreamer::OPTICAL)?"locked":"unlocked");
//WriteUserDataAsText("Streaming Memory (Physical:%dKb | Virtual:%dKb)\n", streamPhysical, streamVirtual);
strStreamingAllocator& strAlloc = strStreamingEngine::GetAllocator();
WriteUserDataAsText("Streaming Memory Occupancy :\n");
WriteUserDataAsText(" Physical : used(%dMb) - available(%dMb)\n", strAlloc.GetPhysicalMemoryUsed_Cached()>>20, strAlloc.GetPhysicalMemoryAvailable_Cached()>>20);
WriteUserDataAsText(" Virtual : used(%dMb) - available(%dMb)\n", strAlloc.GetVirtualMemoryUsed_Cached()>>20, strAlloc.GetVirtualMemoryAvailable_Cached()>>20);
WriteUserDataAsText(" External : physical used(%dMb) - virtual used (%dMb)\n\n", strAlloc.GetExternalPhysicalMemoryUsed_Cached()>>20, strAlloc.GetExternalVirtualMemoryUsed_Cached()>>20);
//-------
WriteUserDataAsText("============ MISC INFO ============\n");
WriteUserDataAsText("Version : %s\n", CDebug::GetVersionNumber());
// Player and Camera Pos, Game time, current vehicle/mission
Vector3 pos = CGameWorld::FindLocalPlayerCoors_Safe();
Vector3 posn = camInterface::GetPos();
Vector3 front = camInterface::GetFront();
WriteUserDataAsText("Player Position (%f;%f;%f)\n", pos.x, pos.y, pos.z);
WriteUserDataAsText("Camera Position (%f;%f;%f)\n", posn.x, posn.y, posn.z);
WriteUserDataAsText("Camera Front (%f;%f;%f)\n", front.x, front.y, front.z);
WriteUserDataAsText("Game Time (%d:%d)\n", CClock::GetHour(), CClock::GetMinute());
WriteUserDataAsText("Wanted Level : %d\n", CGameWorld::sm_localPlayerWanted);
WriteUserDataAsText("IK : %u\n", CBaseIkManager::GetDebugInfoAsUInt());
// Arrest State
{
char arrested[32];
eArrestState arrestState = CGameWorld::sm_localPlayerArrestState;
switch (arrestState)
{
case eArrestState::ArrestState_Arrested: sprintf(arrested, "Arrested"); break;
case eArrestState::ArrestState_Max: sprintf(arrested, "Max"); break;
case eArrestState::ArrestState_None:
default: sprintf(arrested, "None"); break;
}
WriteUserDataAsText("Arrest State : %s\n", arrested);
}
// Player Health and Death State
{
char deathStr[32];
eDeathState deathState = CGameWorld::sm_localPlayerDeathState;
switch (deathState)
{
case eDeathState::DeathState_Alive: sprintf(deathStr, "Alive"); break;
case eDeathState::DeathState_Dead: sprintf(deathStr, "Dead"); break;
case eDeathState::DeathState_Dying: sprintf(deathStr, "Dying"); break;
case eDeathState::DeathState_Max: sprintf(deathStr, "Max"); break;
default: sprintf(deathStr, "Unknown"); break;
}
WriteUserDataAsText("Player Health : %0.2f/%0.2f\n", CGameWorld::sm_localPlayerHealth, CGameWorld::sm_localPlayerMaxHealth);
WriteUserDataAsText("Death State : %s\n", deathStr);
}
// Language
{
char sysLanguage[32];
u32 language = CPauseMenu::sm_languageFromSystemLanguage;
switch (language)
{
case LANGUAGE_ENGLISH: sprintf(sysLanguage, "English"); break;
case LANGUAGE_FRENCH: sprintf(sysLanguage, "French"); break;
case LANGUAGE_GERMAN: sprintf(sysLanguage, "German"); break;
case LANGUAGE_ITALIAN: sprintf(sysLanguage, "Italian"); break;
case LANGUAGE_SPANISH: sprintf(sysLanguage, "Spanish"); break;
case LANGUAGE_PORTUGUESE: sprintf(sysLanguage, "Portuguese"); break;
case LANGUAGE_POLISH: sprintf(sysLanguage, "Polish"); break;
case LANGUAGE_RUSSIAN: sprintf(sysLanguage, "Russian"); break;
case LANGUAGE_KOREAN: sprintf(sysLanguage, "Korean"); break;
case LANGUAGE_CHINESE_TRADITIONAL: sprintf(sysLanguage, "Chinese"); break;
case LANGUAGE_CHINESE_SIMPLIFIED: sprintf(sysLanguage, "ChineseSimplified"); break;
case LANGUAGE_JAPANESE: sprintf(sysLanguage, "Japanese"); break;
case LANGUAGE_MEXICAN: sprintf(sysLanguage, "Mexican"); break;
case LANGUAGE_UNDEFINED:
default: sprintf(sysLanguage, "Undefined"); break;
}
WriteUserDataAsText("System Language : %s\n", sysLanguage);
}
WriteUserDataAsText("Current Mission : %s\n", (CTheScripts::GetPlayerIsOnAMission() || CTheScripts::GetPlayerIsOnARandomEvent()) ? CDebug::GetCurrentMissionName() : "None");
WriteUserDataAsText("Current Vehicle : %s\n", CGameWorld::GetFollowPlayerCarName() ? CGameWorld::GetFollowPlayerCarName() : "None");
// __BANK has a lot more info since we can afford it
#if __BANK
if( CutSceneManager::GetInstance() && CutSceneManager::GetInstance()->IsRunning() )
{
const CutSceneManager::DebugRenderState& cutsceneState = CutSceneManager::GetDebugRenderState();
WriteUserDataAsText("Current Cutscene : %s\n", cutsceneState.cutsceneName);
}
else
WriteUserDataAsText("Current Cutscene : None\n");
CDebugScene::GetDebugSummary(s_debugInfoSummary, true);
WriteUserDataAsText("%s", s_debugInfoSummary);
#else
//num peds
CPed::Pool * pPedPool = CPed::GetPool();
if (pPedPool)
{
int totalCount = pPedPool->GetNoOfUsedSpaces();
int otherCount = CPedPopulation::ms_nNumOnFootOther + CPedPopulation::ms_nNumInVehOther;
int missionCount = 0;
int reusedCount = 0;
int reusePool = 0;
for(int i=0; i<pPedPool->GetSize(); i++)
{
if(const CPed * pPed = pPedPool->GetSlot(i))
{
missionCount += (pPed->PopTypeIsMission() ? 1 : 0);
reusedCount += (pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PedWasReused) ? 1 : 0);
reusePool += (pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PedIsInReusePool) ? 1 : 0);
}
}
WriteUserDataAsText("Peds: %d/%d (mission: %d, scenario (foot/car) %d/%d, ambient: (foot/car) %d/%d, other: %d, reusePool: %d) Reused: %d\n",
totalCount, pPedPool->GetSize(), missionCount,
CPedPopulation::ms_nNumOnFootScenario,
CPedPopulation::ms_nNumInVehScenario,
CPedPopulation::ms_nNumOnFootAmbient,
CPedPopulation::ms_nNumInVehAmbient, otherCount, reusePool, reusedCount);
}
//num cars
CVehicle::Pool * pVehiclePool = CVehicle::GetPool();
if (pVehiclePool)
{
int iVehiclePoolSize, iTotalNumVehicles, iNumMission, iNumParked, iNumAmbient, iNumReal, iNumRealTraffic, iNumDummy, iNumSuperDummy, iNumInReusePool, iNumBeingDeleted, iNumNetRegistered, iNumUnknown;
iTotalNumVehicles = 0;
iNumMission = 0;
iNumParked = 0;
iNumAmbient = 0;
iNumReal = 0;
iNumRealTraffic = 0;
iNumDummy = 0;
iNumSuperDummy = 0;
iNumInReusePool = 0;
iNumBeingDeleted = 0;
iNumNetRegistered = 0;
iNumUnknown = 0;
s32 i = (s32) pVehiclePool->GetSize();
while(i--)
{
CVehicle * pVehicle = pVehiclePool->GetSlot(i);
if (pVehicle)
{
iTotalNumVehicles++;
if(pVehicle->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
{
iNumBeingDeleted++;
}
if(pVehicle->GetNetworkObject())
{
iNumNetRegistered++;
}
if(pVehicle->GetIsInReusePool())
{
// in reuse pool
iNumInReusePool++;
continue; // don't count vehicles in the reuse pool towards the population counts
}
if (pVehicle->PopTypeIsMission())
{
if (pVehicle->m_nVehicleFlags.bCountAsParkedCarForPopulation)
iNumParked++;
else
iNumMission++;
}
else if(pVehicle->PopTypeIsRandom())
{
if (pVehicle->PopTypeGet() == POPTYPE_RANDOM_PARKED)
iNumParked++;
else
iNumAmbient++;
}
else
{
iNumUnknown++;
}
switch(pVehicle->GetVehicleAiLod().GetDummyMode())
{
case VDM_REAL:
{
iNumReal++;
if(pVehicle->PopTypeGet() == POPTYPE_RANDOM_AMBIENT || pVehicle->PopTypeGet()== POPTYPE_RANDOM_PATROL)
{
iNumRealTraffic++;
}
break;
}
case VDM_DUMMY: { iNumDummy++; break; }
case VDM_SUPERDUMMY: { iNumSuperDummy++; break; }
default: { Assert(false); }
}
}
}
iVehiclePoolSize = (int) pVehiclePool->GetSize();
WriteUserDataAsText("Cars: %d/%d/%d (ambient: %d, mission: %d, parked: %d, reusePool: %d unknown: %d deleted: %d net: %d) (real: %d(%d traffic), dummy: %d, superdummy: %d)\n",
iTotalNumVehicles, iVehiclePoolSize, CVehiclePopulation::GetDesiredNumberAmbientVehicles_Cached(), iNumAmbient,
iNumMission, iNumParked, iNumInReusePool, iNumUnknown, iNumBeingDeleted, iNumNetRegistered,
iNumReal, iNumRealTraffic, iNumDummy, iNumSuperDummy);
}
// Objects
CObject::Pool* objPool = CObject::GetPool();
if (objPool)
{
int totalObjects = (int) objPool->GetNoOfUsedSpaces();
int numOfPickups, numOfDoors, numOfWeapons, numOfParachutes, numOfCarParachutes, numOfHelmets, numOfProjectiles, numOfProjectileClones, numOfProjectileThrowns, numOfOwnedByScript, numOfOwnedByFrag, numOfOwnedByTemp, numOfOwnedByRandom, numOfOwnedByGame, numBeingDeleted;
numOfPickups = 0;
numOfDoors = 0;
numOfWeapons = 0;
numOfParachutes = 0;
numOfCarParachutes = 0;
numOfHelmets = 0;
numOfProjectiles = 0;
numOfProjectileThrowns = 0;
numOfProjectileClones = 0;
numBeingDeleted=0;
numOfOwnedByScript = 0;
numOfOwnedByFrag = 0;
numOfOwnedByTemp = 0;
numOfOwnedByRandom = 0;
numOfOwnedByGame = 0;
s32 i = (s32) totalObjects;
while(i--)
{
CObject * pObject = objPool->GetSlot(i);
if (pObject)
{
if(pObject->IsPickup())
{
numOfPickups++;
}
if(pObject->IsADoor())
{
numOfDoors++;
}
if(pObject->GetWeapon())
{
numOfWeapons++;
}
if(pObject->GetIsParachute())
{
numOfParachutes++;
}
if(pObject->GetIsCarParachute())
{
numOfCarParachutes++;
}
if(pObject->m_nObjectFlags.bIsHelmet)
{
numOfHelmets++;
}
if(pObject->GetAsProjectile())
{
numOfProjectiles++;
const CProjectile* pProjectile = static_cast<const CObject *>(pObject)->GetAsProjectile();
if(pProjectile && pProjectile->GetNetworkIdentifier().IsClone())
{
numOfProjectileClones++;
}
}
if(pObject->GetAsProjectileThrown())
{
numOfProjectileThrowns++;
}
if(pObject->GetOwnedBy() == ENTITY_OWNEDBY_SCRIPT)
{
numOfOwnedByScript++;
}
if(pObject->GetOwnedBy() == ENTITY_OWNEDBY_FRAGMENT_CACHE)
{
numOfOwnedByFrag++;
}
if(pObject->GetOwnedBy() == ENTITY_OWNEDBY_TEMP)
{
numOfOwnedByTemp++;
}
if(pObject->GetOwnedBy() == ENTITY_OWNEDBY_RANDOM)
{
numOfOwnedByRandom++;
}
if(pObject->GetOwnedBy() == ENTITY_OWNEDBY_GAME)
{
numOfOwnedByGame++;
}
if(pObject->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
{
numBeingDeleted++;
}
}
}
WriteUserDataAsText("Objects: %d/%d (Pickup:(%d), Doors:(%d), Weapons:(%d), Parachutes:(%d), CarParachutes:(%d), Helmets:(%d), Projectiles:(%d/%d/%d), OwnedByScript:(%d), OwnedByFrag:(%d), OwnedByTemp:(%d), OwnedByRand:(%d), OwnedByGame:(%d), BeingDeleted:(%d)",
totalObjects, objPool->GetSize(), numOfPickups, numOfDoors, numOfWeapons, numOfParachutes, numOfCarParachutes, numOfHelmets, numOfProjectiles, numOfProjectileThrowns, numOfProjectileClones, numOfOwnedByScript, numOfOwnedByFrag, numOfOwnedByTemp, numOfOwnedByRandom, numOfOwnedByGame, numBeingDeleted);
}
CDummyObject::Pool* dummyObjPool = CDummyObject::GetPool();
if(dummyObjPool)
{
int totalDummy = (int) dummyObjPool->GetNoOfUsedSpaces();
WriteUserDataAsText("Dummy Objects: %d/%d\n",
totalDummy, dummyObjPool->GetSize());
}
// Display active (and total) peds, ragdolls, objects, and vehicles.
phLevelNew* phLvl = CPhysics::GetLevel();
if (phLvl)
{
s32 instPedsActive = 0;
s32 instPedsTotal = 0;
s32 instVehiclesActive = 0;
s32 instVehiclesTotal = 0;
s32 instObjectsTotal = 0;
s32 instObjectsActive = 0;
s32 instRagdollsActive = 0;
s32 instRagdollsTotal = 0;
for(int i = 0; i < phLvl->GetMaxObjects(); ++i)
{
if(!phLvl->IsNonexistent(i))
{
phInst* pInst = phLvl->GetInstance(i);
CEntity* pEntity = CPhysics::GetEntityFromInst(pInst);
if(pEntity && pInst->GetClassType() != PH_INST_MAPCOL)
{
bool bActive = phLvl->IsActive(pInst->GetLevelIndex());
if(pEntity->GetIsTypeVehicle())
{
instVehiclesTotal++;
instVehiclesActive += bActive ? 1 : 0;
}
if(pEntity->GetIsTypeObject())
{
instObjectsTotal++;
instObjectsActive += bActive ? 1 : 0;
}
if(pEntity->GetIsTypePed())
{
instPedsTotal++;
instPedsActive += bActive ? 1 : 0;
if(((CPed*)pEntity)->GetRagdollState()==RAGDOLL_STATE_PHYS)
{
instRagdollsTotal++;
instRagdollsActive += bActive ? 1 : 0;
}
}
}
}
}
WriteUserDataAsText("Physics instances (active/total): objects %d/%d, cars: %d/%d, peds: %d/%d, ragdolls: %d/%d\n",
instObjectsActive, instObjectsTotal, instVehiclesActive, instVehiclesTotal,
instPedsActive, instPedsTotal, instRagdollsActive, instRagdollsTotal);
}
#endif
// Not available in final builds :(
#if !__NO_OUTPUT
CInteriorInst* pIntInst = NULL;
s32 roomIdx = -1;
pIntInst = CPortalVisTracker::GetPrimaryInteriorInst();
roomIdx = CPortalVisTracker::GetPrimaryRoomIdx();
if (pIntInst && roomIdx>0 && roomIdx!=INTLOC_INVALID_INDEX)
{
// get the interior and room names
CMloModelInfo *pModelInfo = reinterpret_cast<CMloModelInfo*>(pIntInst->GetBaseModelInfo());
const char* interiorName = "Unable to find Interior";
if (pModelInfo)
{
interiorName = pModelInfo->GetModelName();
}
const char* roomName = "Unable To Find Room";
if (pModelInfo)
{
roomName = pModelInfo->GetRooms()[roomIdx].m_name;
}
CInteriorProxy* pInteriorProxy = pIntInst->GetProxy();
WriteUserDataAsText("Interior:<%s (%d)>; Room:<%s (%d)>; Group:<%d>\n\n", interiorName, CInteriorProxy::GetPool()->GetJustIndex(pInteriorProxy), roomName, roomIdx, pInteriorProxy->GetGroupId());
}
else
{
WriteUserDataAsText("Exterior\n\n");
}
#endif
//-------
WriteUserDataAsText("============ NETWORK INFO ============\n");
WriteUserDataAsText("Network Opening : %s\n", NetworkInterface::IsNetworkOpening() ? "Yes" : "No");
WriteUserDataAsText("Network Open : %s\n", NetworkInterface::IsNetworkOpen() ? "Yes" : "No");
WriteUserDataAsText("Network Closing : %s\n", NetworkInterface::IsNetworkClosing() ? "Yes" : "No");
WriteUserDataAsText("Network Close Pending : %s\n", NetworkInterface::IsNetworkClosePending() ? "Yes" : "No");
WriteUserDataAsText("Network Forcing Crash On Shutdown While Processing : %s\n", NetworkInterface::IsForcingCrashOnShutdownWhileProcessing() ? "Yes" : "No");
if (NetworkInterface::IsGameInProgress())
{
WriteUserDataAsText("Game In Progress : Yes\n");
WriteUserDataAsText("Number of physical players : %d\n", NetworkInterface::GetNumPhysicalPlayers());
// WriteUserDataAsText("Network session state : %d\n", CNetwork::GetNetworkSession().GetStateAsInt());
// WriteUserDataAsText("Network session transition state: %d\n", CNetwork::GetNetworkSession().GetTransitionStateAsInt());
WriteUserDataAsText("Network session valid : %s\n", CNetwork::IsNetworkSessionValid() ? "Yes" : "No");
if(CNetwork::IsNetworkSessionValid())
{
WriteUserDataAsText(CNetwork::GetNetworkSession().IsSessionActive() ? "Network Session Active : Yes\n" : "Network Session Active : No\n");
WriteUserDataAsText("Network session transition member count : %d\n", CNetwork::GetNetworkSession().GetTransitionMemberCount());
WriteUserDataAsText("Network Session in transition : %s\n", CNetwork::GetNetworkSession().IsTransitionActive() ? "Yes" : "No");
}
s32 fDesiredNumAmbientVehicles = static_cast<s32>(CVehiclePopulation::GetDesiredNumberAmbientVehicles());
s32 TotalAmbientCarsOnMap = CVehiclePopulation::ms_numRandomCars;
s32 TargetNumLocalVehicles = CNetworkObjectPopulationMgr::GetLocalTargetNumVehicles();
s32 TotalNumLocalVehicles = CNetworkObjectPopulationMgr::GetNumCachedLocalVehicles();
s32 pedMemoryUse = CPopulationStreaming::GetCachedTotalMemUsedByPeds() >> 20;
s32 vehicleMemoryUse = CPopulationStreaming::GetCachedTotalMemUsedByVehicles() >> 20;
s32 pedBudget = gPopStreaming.GetCurrentMemForPeds() >> 20;
s32 vehicleBudget = gPopStreaming.GetCurrentMemForVehicles() >> 20;
WriteUserDataAsText("Ped Memory Use/Budget : %d/%d\n", pedMemoryUse, pedBudget);
WriteUserDataAsText("Vehicle Memory Use/Budget : %d/%d\n", vehicleMemoryUse, vehicleBudget);
WriteUserDataAsText("Reassignment in progress : %s\n", netObjectReassignMgr::IsReassignmentInProgress_Cached() ? "Yes" : "No");
WriteUserDataAsText("Total Ambient Cars On Map : %d\n", TotalAmbientCarsOnMap);
WriteUserDataAsText("Desired Number of Ambient Vehicles : %d\n", fDesiredNumAmbientVehicles);
WriteUserDataAsText("Total Number of Local Vehicles : %d\n", TotalNumLocalVehicles);
WriteUserDataAsText("Target Number of Local Vehicles : %d\n\n", TargetNumLocalVehicles);
netGameEvent::Pool *pEventPool = netGameEvent::GetPool();
if (pEventPool)
{
s32 usedEvents = pEventPool->GetNoOfUsedSpaces();
s32 freeSpaces = pEventPool->GetNoOfFreeSpaces();
int total = usedEvents+freeSpaces;
WriteUserDataAsText("Network Event Usage: %d/%d\n", usedEvents, total);
}
const static int NUM_EVENT_TYPES_TO_LOG = 5;
for(int i=0; i < NUM_EVENT_TYPES_TO_LOG; i++)
{
if (CNetworkEventsDumpInfo::ms_eventTypesCount[i].count > 0)
{
WriteUserDataAsText("Network Events Type: %s Count %d\n", CNetworkEventsDumpInfo::ms_eventTypesCount[i].eventName, CNetworkEventsDumpInfo::ms_eventTypesCount[i].count);
}
}
for (auto it = CNetworkEventsDumpInfo::ms_scriptedEvents.Begin(); it != CNetworkEventsDumpInfo::ms_scriptedEvents.End(); ++it)
{
WriteUserDataAsText("Network Scripted Events: %s Count %d\n", (*it).eventName, (*it).count);
}
for(int i = 0; i < (int)NetworkObjectTypes::NUM_NET_OBJ_TYPES; i++)
{
WriteUserDataAsText("Sync Tree %d", i);
CProjectSyncTree* tree = NetworkInterface::GetObjectManager().GetSyncTree((NetworkObjectType)i);
if (tree)
{
const atFixedArray<netSyncDataNode*, netSyncDataNode::MAX_SYNC_DATA_NODES>& nodes = tree->GetDataNodes();
for (int sn = 0; sn < nodes.GetCount(); sn++)
{
auto dataNode = nodes[sn];
if (dataNode)
{
WriteUserDataAsText("\tNode %d Updated: %s\n", sn, nodes[sn]->GetWasUpdated() ? "TRUE" : "FALSE");
}
else
{
WriteUserDataAsText("\tNode %d is null\n", sn);
}
}
}
else
{
WriteUserDataAsText("\tTree is null\n");
}
}
}
else
{
WriteUserDataAsText("Game In Progress : No\n\n");
}
//-------
WriteUserDataAsText("============ SCALEFORM INFO ============\n");
WriteUserDataAsText("Last Movie : %s\n", CScaleformMgr::sm_LastMovieName);
WriteUserDataAsText("Last ActionScript Method : %s\n", CScaleformMgr::sm_LastMethodName);
if (CScaleformMgr::sm_LastMethodParamName)
WriteUserDataAsText("Last ActionScript Method Params : %s\n", CScaleformMgr::sm_LastMethodParamName);
WriteUserDataAsText("Is Inside ActionScript : %s\n", CScaleformMgr::sm_bIsInsideActionScript?"yes":"no");
WriteUserDataAsText("Active Movies\n");
for(s32 movieIdx=0 ; movieIdx<CScaleformMgr::sm_ScaleformArray.GetCount(); ++movieIdx)
{
if (CScaleformMgr::IsMovieActive(movieIdx))
WriteUserDataAsText(" - MovieId(%3d) Filename(%s)\n", CScaleformMgr::sm_ScaleformArray[movieIdx].iMovieId, CScaleformMgr::sm_ScaleformArray[movieIdx].cFilename);
}
//-------
WriteUserDataAsText("\n============ SCRIPT THREAD INFO ============\n");
WriteUserDataAsText("Number of scrThreads : %d\n", GtaThread::CountActiveThreads());
// Not thread safe !
// WriteUserDataAsText("scrThreads stack traces :\n\n");
// for (int threadIdx = 0; threadIdx < scrThread::GetThreadCount(); ++threadIdx)
// {
// scrThread::GetThreadByIndex(threadIdx).PrintStackTrace(ChanneledOutputForStackTrace);
// if (s_ThreadlStack[0] != 0)
// {
// WriteUserDataAsText(s_ThreadlStack);
// s_ThreadlStack[0] = 0;
// WriteUserDataAsText("----\n");
// }
// }
if (scrThread::GetActiveThreadGlobal())
{
GtaThread *pActiveThread = (GtaThread*) scrThread::GetActiveThreadGlobal();
WriteUserDataAsText("Active scrThread %s PC=%d Handler=0x%p State=%d\n", pActiveThread->GetScriptName(), pActiveThread->GetProgramCounter(0), pActiveThread->m_Handler, (s32) pActiveThread->GetState());
WriteUserDataAsText("Active scrThread StackTrace :\n");
pActiveThread->PrintStackTrace(ChanneledOutputForStackTrace);
if (s_ThreadlStack[0] != 0)
{
WriteUserDataAsText(s_ThreadlStack);
s_ThreadlStack[0] = 0;
WriteUserDataAsText("\n");
}
}
else
{
WriteUserDataAsText("No Active scrThread\n");
}
//-------
WriteUserDataAsText("============ MOVIE INFO ============\n");
bwMovie::CoredumpHandler(BACKTRACE_ONLY(s_crashContextHandle));
//-------
WriteUserDataAsText("\n###### END OF DEBUG INFO ######");
return 0;
}
#endif //RSG_ORBIS || BACKTRACE_ENABLED
static PMCPerfControl* s_perfControl;
// This bool is set on the end of the FsmEnter. It is used by various systems involved in tight loops
// (Warning Screen, Game Session State machine) to drive some specific rendering behaviour. Please
// be careful of side effects if modiying g_gameRunning
bool g_GameRunning = false;
CApp* CApp::sm_Self = NULL;
// A dummy sync point to be able to perform tuner captures from the main thread's point of view
// and perform loading tuner captures.
#if RSG_PC
bool CApp::IsGameRunning()
{
return (sm_Self && (sm_Self->GetState() == State_RunGame) && camInterface::IsFadedIn() && !fwTimer::IsGamePaused()) ? true : false;
}
#endif // RSG_PC
fwFsm::Return CApp::UpdateState(const s32 state, const s32, const fwFsm::Event event)
{
fwFsmBegin
fwFsmState(State_InitSystem)
fwFsmOnUpdate
return InitSystem();
fwFsmState(State_InitGame)
fwFsmOnUpdate
return InitGame();
fwFsmState(State_RunGame)
fwFsmOnEnter
return RunGame_OnEnter();
fwFsmOnUpdate
return RunGame();
fwFsmState(State_ShutdownGame)
fwFsmOnUpdate
return ShutdownGame();
fwFsmState(State_ShutdownSystem)
fwFsmOnUpdate
return ShutdownSystem();
fwFsmEnd
}
#if __PPU
void CheckGameContent()
{
int ret = 0;
unsigned int type = 0;
unsigned int attributes = 0;
CellGameContentSize content_size;
char directoryName[CELL_GAME_DIRNAME_SIZE];
cellSysmoduleLoadModule(CELL_SYSMODULE_SYSUTIL_GAME);
ret = cellGameBootCheck(&type, &attributes, &content_size, directoryName);
if (CELL_GAME_RET_OK == ret)
{
char contentInfoPath[CELL_GAME_PATH_MAX];
char usrdirPath[CELL_GAME_PATH_MAX];
ret = cellGameContentPermit(contentInfoPath, usrdirPath);
if (CELL_GAME_RET_OK == ret)
{
if (CELL_GAME_GAMETYPE_HDD == type)
{
Displayf("contentInfoPath = %s", contentInfoPath);
Displayf("usrdirPath = %s", usrdirPath);
Warningf("I AM TRYING TO BOOT FROM THE HARD DRIVE. THIS MAY NOT BE WHAT YOU WANTED.");
Warningf("In the XMB, Debug Settings > Game Type (Debugger) should be 'Disc Boot Game' and GameContentUtil Boot Path (Debugger) should be 'For Development'.");
if (strlen(usrdirPath) > 0)
{
if (usrdirPath[strlen(usrdirPath)-1] != '/')
{
strncat(usrdirPath, "/", CELL_GAME_PATH_MAX);
}
}
CFileMgr::SetGameHddBootPath(usrdirPath);
}
}
else
{
Assertf(0, "CheckGameContent - cellGameContentPermit failed");
}
}
else
{
Assertf(0, "CheckGameContent - cellGameBootCheck failed");
}
cellSysmoduleUnloadModule(CELL_SYSMODULE_SYSUTIL_GAME);
#if !__FINAL
if (PARAM_forceboothdd.Get())
{
char buf[128] = {0};
formatf(buf, "/dev_hdd0/game/%s/USRDIR/", XEX_TITLE_ID_HDD);
CFileMgr::SetGameHddBootPath(buf);
}
#endif // !__FINAL
}
#endif // __PPU
// PURPOSE: Initialise the basic systems.
fwFsm::Return CApp::InitSystem()
{
#if RSG_ORBIS
s_ThreadlStack[0] = 0;
s_debugInfoSummary[0] = 0;
ASSERT_ONLY( int ret = ) sceCoredumpRegisterCoredumpHandler(CoredumpHandler, sCoredumpHandlerStackSize, (void*)0);
Assert(ret == SCE_OK);
#endif //RSG_ORBIS
//@@: location CAPP_INITSYSTEM
#if RSG_PC && __ASSERT
// On PC, we need to enable queueing bugs for logging as soon as possible
diagLogBugCallback = CDebug::OnLogBug;
#endif
#if RSG_PC
diagTerminateCallback = CApp::WriteExitFile;
#endif
#if RSG_PC && __STEAM_BUILD
rtry
{
wchar_t steamPath[MAX_PATH] = {0};
rcheck(GetModuleFileNameW(NULL, steamPath, sizeof(steamPath)), catchall, );
wchar_t* lastSlash = wcsrchr(steamPath, '\\');
if (lastSlash == NULL)
{
lastSlash = wcsrchr(steamPath, '/');
}
if (lastSlash != NULL)
{
lastSlash[1] = L'\0';
}
safecat(steamPath, L"steam_api64.dll");
rverify(CertificateVerify::Verify(steamPath, NULL), catchall, gnetError("Certification Verification failed for steam"));
rcheck(gnetVerifyf(SteamAPI_Init(), "SteamAPI_Init failed - Is Steam running and are you signed in to a R* developer account?"), catchall, );
rverify(SteamUser(), catchall, gnetError("SteamUser() was null"));
g_rlPc.RefreshSteamAuthTicket(false);
}
rcatchall
{
Errorf("Could not initialize Steam API!! Make sure you have the Steam_api64.dll in the same directory as the game and you launched the game through Steam");
}
#endif
#if EPIC_API_SUPPORTED
const char* epicProductName = "GTAV"; // arbitrary, matches value set in launcher
const char* epicProductVersion = "1.0"; // arbitrary, matches value set in launcher
const char* epicProductId = "68d2cc08f9a94b8fb51af4f5cfa6d41b";
const char* epicSandboxId = "0584d2013f0149a791e7b9bad0eec102";
const char* epicClientId = "c19208b6eec5423d951186aee0ad7b9c";
const char* epicClientSecret = "YaEL5raCmwNB4TuZf5AU";
const char* epicDeploymentId = "f8cb4ad3fab34f4ca902480ae7875a82";
if (!g_SysEpic.InitClass(epicProductName, epicProductVersion, epicProductId, epicSandboxId, epicClientId, epicClientSecret, epicDeploymentId))
{
Errorf("Could not initialize Epic API!! Make sure you have the EOSSDK-Win64-Shipping.dll in the same directory as the game and you launched the game through Epic");
}
#endif
#if RSG_LAUNCHER_CHECK
if (!CheckParentProcess())
{
// We don't want crash dumps for launcher check
RAGE_PC_FATAL_SILENT(ERR_NO_LAUNCHER);
}
#endif
#if RAGE_TIMEBARS
float maxAllowedTime = 0.0f;
// If the user wants to dump timebars if the main thread takes a long time...
if (PARAM_dumptimebarsonlongmainthread.Get())
{
// See if the user also specified the amount of ms that we may take.
PARAM_dumptimebarsonlongmainthread.Get(maxAllowedTime);
// If not, default to 200ms.
if (!maxAllowedTime)
{
maxAllowedTime = 200.0f;
}
}
#endif // RAGE_TIMEBARS
PF_INIT_TIMEBARS("Main", MAIN_THREAD_MAX_TIMEBARS, maxAllowedTime);
PF_INIT_STARTUPBAR("Startup", 1000);
PF_BEGIN_STARTUPBAR();
PF_START_STARTUPBAR("System Init");
pm_Init();
CDebug::SetPrinterFn();
// need to call this before the drawlist mgr is initialised
InitNYDrawListDebug();
#if !__FINAL || !__NO_OUTPUT
netRemoteLog::InitClass("gta5");
#endif
#if __PPU
// Copied from rage\base\src\rline\rlnp.cpp
ASSERT_ONLY(int errorCode = ) sceNp2Init(rlNp::GetSceNpPoolSize(), rlNp::GetSceNpPoolMemory());
Assertf(errorCode == 0, "CApp::InitGame - sceNp2Init failed - error code = %x", errorCode);
CheckGameContent();
#endif // __PPU
#if !__FINAL || !RSG_PC
if(!CSystem::Init("Fuzzy"))
#else
if(!CSystem::Init("Grand Theft Auto V"))
#endif
{
SetState(State_ShutdownGame);
return fwFsm::Continue;
}
#if __STEAM_BUILD
int steamAppId = CGameConfig::Get().GetConfigOnlineServices().m_SteamAppId;
if (SteamAPI_RestartAppIfNecessary(steamAppId))
{
// We don't want crash dumps for steam check
RAGE_PC_FATAL_SILENT(ERR_NO_STEAM);
}
#endif
// Dongle Validation
#if ENABLE_DONGLE
#if USE_DONGLE
CheckDongle();
#endif
const char* cVersionNumber = CDebug::GetVersionNumber();
if(cVersionNumber && !strncmp(cVersionNumber, "NM", 2))
CheckDongle();
#endif
InitNYDrawList();
SetState(State_InitGame);
return fwFsm::Continue;
}
XPARAM(maponly);
XPARAM(maponlyextra);
XPARAM(cheapmode);
XPARAM(nocars);
XPARAM(nopeds);
XPARAM(noambient);
XPARAM(marketing);
XPARAM(stealextramemory);
// PURPOSE: Initialise the game systems.
fwFsm::Return CApp::InitGame()
{
//does this need to be called? doing so here seems to remove all the
//time stuff tracked from CApp::InitSystem()
//PF_BEGIN_STARTUPBAR();
PF_START_STARTUPBAR("PreLoadingScreensInit");
#if RSG_PC
CGame::InitCallbacks();
#endif
CGame::PreLoadingScreensInit();
// map the desired platform: device
#if __PS3 && !__FINAL
sys_memory_info_t mem_info;
sys_memory_get_user_memory_size( &mem_info );
bool runningOnDevKit = mem_info.total_user_memory > 256 << 20;
#else
bool runningOnDevKit = !__FINAL;
#endif // __PS3
if ( runningOnDevKit && (PARAM_maponly.Get() || PARAM_maponlyextra.Get() || PARAM_cheapmode.Get())){
#if !__FINAL && !defined(NAVGEN_TOOL)
// alloc 2x10Mb chunks
CFileMgr::AllocateDummyResourceMem();
PARAM_nocars.Set("true");
PARAM_nopeds.Set("true");
PARAM_noambient.Set("true");
BANK_ONLY(g_maponlyHogEnabled = true);
if (PARAM_cheapmode.Get())
{
BANK_ONLY(g_cheapmodeHogEnabled = true;)
}
if (PARAM_maponlyextra.Get())
{
CFileMgr::AllocateExtraResourceMem((10 << 20) + (4 << 20));
BANK_ONLY(g_maponlyExtraHogEnabled = true;)
}
#endif //!__FINAL
}
#if !__FINAL && !defined(NAVGEN_TOOL)
int size = 0;
if (PARAM_stealextramemory.Get(size) && size)
{
CFileMgr::AllocateExtraResourceMem(size);
BANK_ONLY(g_stealExtraMemoryEnabled = true;)
}
#endif //!__FINAL
#if(BUGSTAR_INTEGRATION_ACTIVE)
CBugstarIntegration::Init();
#endif
#if __XENON && __INCLUDE_MEMORY_TEST_IN_XENON_BUILD
// Perform startup memory test if LT+RT+LB+RB are held down on either pad
CMemoryTest::StartTest();
#endif
#if TRACING_STATS
gTracingStats.Init();
#endif
PF_START_STARTUPBAR("CText init");
CText::Init(INIT_CORE);
PF_START_STARTUPBAR("CLoadingScreens init");
CLoadingScreens::Init( LOADINGSCREEN_CONTEXT_LEGALMAIN, 0);
if (!StreamingInstall::HasInstallFinished())
{
CWarningScreen::Init(INIT_CORE);
CGameStreamMgr::Init(INIT_CORE);
PF_PUSH_STARTUPBAR("Streaming install");
StreamingInstall::BlockUntilFinished();
CFileMgr::CleanupAsyncInstall();
PF_POP_STARTUPBAR();
}
#if RSG_DURANGO
else
{
// to shutdown the resume watcher and package transfer watcher started at init.
// Durango only, to avoid messing with other platforms (B*2564598)
StreamingInstall::Shutdown();
}
#endif // RSG_DURANGO
PF_START_STARTUPBAR("Game Init");
CGame::Init();
#if RSG_PC
//Allow device resets to occur now that the game is fully initialized. Request a devcie reset through the settings manager to ensure that all game properties are properly initialized.
// GRCDEVICE.SetIgnoreDeviceReset(false);
// CSettingsManager::GetInstance().RequestNewSettings(CSettingsManager::GetInstance().GetCopyOfSettings());
#endif
SetState(State_RunGame);
return fwFsm::Continue;
}
// PURPOSE: Game initialiation before game loop
fwFsm::Return CApp::RunGame_OnEnter()
{
//////////////////////////////////////////////////////////////////////////
// required for various Sony TRC bugs related to deleting of trophy data
// and attempting to re-unlock trophies. This could all go away if we stop
// caching trophy bits in the saved game profile.
#if RSG_NP
// wait until trophy status is known (installed or not) before loading profile
rlNpTrophy::WaitUntilTrophyStatusIsKnown();
// write out profile if the trophy data has been re-installed on startup (due to being missing)
if (rlNpTrophy::GetHasTrophyDataBeenReinstalled())
{
CLiveManager::GetAchMgr().ClearAchievements();
CProfileSettings::GetInstance().WriteNow(true);
rlNpTrophy::SetHasTrophyDataBeenReinstalled(false);
}
#endif
//////////////////////////////////////////////////////////////////////////
// This shouldn't be here, we've already called this function much much sooner than this and we're throwing out nearly 30s of data.
// PF_BEGIN_STARTUPBAR();
CGame::UpdateSessionState();
#if !__FINAL && RAGE_TRACKING
const char* trackerReport = NULL;
if (PARAM_trackerreport.Get(trackerReport))
RAGE_TRACK_REPORT_NAME(trackerReport);
#endif
RAGETRACE_INIT();
RAGETRACE_INITTHREAD("Main", 1024, 1);
//gRenderThreadInterface.Init(); // create the render thread and let it take control of the d3d device
PF_START_STARTUPBAR("Setup devices");
CFileMgr::SetupDevicesAfterInit();
#if __PPU
struct std::malloc_managed_size mms;
std::malloc_stats(&mms);
if (mms.current_system_size > 0) {
Errorf("**** current system usage %dK; in use is %dk",mms.current_system_size>>10,mms.current_inuse_size>>10);
}
#endif
s_perfControl = rage_new PMCPerfControl();
#if !__FINAL
Printf("Game init took %f seconds\n", initTimer.GetTime());
#endif
#if !__FINAL
for(s32 bucket = 0; bucket < 16; ++bucket)
{
sysMemAllocator::SetBucketName(bucket, sysMemGetBucketName((MemoryBucketIds)bucket));
}
#endif
sysMemAllocator::GetMaster().GetAllocator(MEMTYPE_GAME_VIRTUAL)->UpdateMemorySnapshot();
sysMemAllocator::GetMaster().GetAllocator(MEMTYPE_RESOURCE_VIRTUAL)->UpdateMemorySnapshot();
sysMemAllocator::GetMaster().GetAllocator(MEMTYPE_GAME_PHYSICAL)->UpdateMemorySnapshot();
sysMemAllocator::GetMaster().GetAllocator(MEMTYPE_RESOURCE_PHYSICAL)->UpdateMemorySnapshot();
g_GameRunning = true;
PF_FINISH_STARTUPBAR();
SetInstance(this);
#if RSG_PC
grcResourceCache::SetGameRunningCallback(IsGameRunning);
GRCDEVICE.SetDisablePauseOnLostFocusSystemOverride(false);
GRCDEVICE.LockDeviceResetAvailability();
if (g_rlPc.GetGamepadManager())
{
#if __FINAL
g_rlPc.GetGamepadManager()->SetBackgroundInputEnabled(!GRCDEVICE.IsAllowPauseOnFocusLoss());
#else
bool bGameWillPauseOnFocusLoss = GRCDEVICE.IsAllowPauseOnFocusLoss() && PARAM_BlockOnLostFocus.Get();
g_rlPc.GetGamepadManager()->SetBackgroundInputEnabled(!bGameWillPauseOnFocusLoss);
#endif
}
#endif // RSG_PC
return fwFsm::Continue;
}
// PURPOSE: Main game loop.
fwFsm::Return CApp::RunGame()
{
#if RAGE_TIMEBARS
if (!PARAM_disableframemarkers.Get())
{
char buffer[32];
formatf(buffer, "Frame %u", fwTimer::GetFrameCount());
pfTimebarFuncs::g_PushMarkerCb(buffer, 0);
}
#endif // RAGE_TIMEBARS
#if STALL_DETECTION
if (! ( CPauseMenu::IsActive()
|| camInterface::IsFadedOut()
|| CLoadingScreens::AreActive()
)
)
{
m_StallDetection.BeginFrame();
}
#endif // STALL_DETECTION
#if RAGE_TRACKING
if ( PARAM_autoMemTrack.Get() )
{
static int loopCount = 0;
if( loopCount == 60 )
{
diagTracker* t = diagTracker::GetCurrent();
if( t )
{
((diagTrackerRemote2*)t)->RequestDump();
}
}
loopCount++;
}
#endif
PF_FRAMEINIT_TIMEBARS(gDrawListMgr->GetUpdateThreadFrameNumber());
RAGETRACE_FRAMESTART();
//@@: location CAPP_RUNGAME
PF_PUSH_TIMEBAR_BUDGETED("System begin", 0.1f);
s_perfControl->StartFrame();
#if RAGE_TIMEBARS
// Indicate what kind of build this is in PIX/Tuner, so we now what's going on when looking at captures.
{
char buildType[256];
char version[32];
formatf(buildType, RSG_CONFIGURATION " %s %s", CDebug::GetPackageType(), CDebug::GetFullVersionString(version, sizeof(version)));
pfAutoMarker versionMarker(buildType, 6);
}
#endif // RAGE_TIMEBARS
#if GTA_REPLAY
CReplayMgr::StartFrame();
#endif // GTA_REPLAY
#if __PPU && __DEV
snSafeLoadPoint();
#endif
RAGETRACE_PUSHNAME("CSystem::BeginUpdate");
CSystem::BeginUpdate();
RAGETRACE_POP();
#if (SECTOR_TOOLS_EXPORT)
CSectorTools::Run( );
#endif // OBJECT_CHECKER_EXPORT
PF_POP_TIMEBAR(); // "System begin"
CGame::Update();
#if DEBUG_SCREENSHOTS
PF_START_TIMEBAR_DETAIL("Screenshots");
CDebug::HandleScreenShotRequests();
#endif
pm_Display();
//***RENDERTHREAD LOOP********************************************
CTaskScheduler::StartBatchingTasks(5);
gRenderThreadInterface.GPU_IdleSection(); // call stuff which needs done when GPU is idle
gRenderThreadInterface.Synchronise();
CControlMgr::StartUpdateThread();
CGame::Render(); // it's called render, but it won't really - it just builds the draw lists & contexts
CControlMgr::WaitForUpdateThread();
//******************************************************
#if USE_TELEMETRY
CTelemetry::Update();
TelemetryUpdate();
#endif
//@@: location CAPP_RUNGAME_AUDIO_WAIT
PF_PUSH_TIMEBAR_BUDGETED("AudioWait", 1.0f);
if (!CSavegameAutoload::IsPerformingAutoLoadAtStartOfGame()
UI_LANDING_PAGE_ONLY(&& !CLandingPageArbiter::IsLandingPageActive())
)
{
audNorthAudioEngine::FinishUpdate();
}
PF_POP_TIMEBAR();
#if GTA_REPLAY
if(audDriver::GetMixer() && audDriver::GetMixer()->IsCapturing() && audDriver::GetMixer()->IsFrameRendering())
{
const float kfFrameStep = 1.0f / 31.0f;
// Only trigger an extra engine update when exporting at 30fps or less to make sure enough audio is generated this frame
if(audDriver::GetMixer()->GetTotalAudioTimeNs() < audDriver::GetMixer()->GetTotalVideoTimeNs() && CReplayMgr::GetExportFrameStep() >= kfFrameStep)
{
g_AudioEngine.TriggerUpdate();
g_AudioEngine.WaitForAudioFrame();
}
CReplayPlaybackController const& c_replayPlaybackController = CReplayCoordinator::GetReplayPlaybackController();
while (audDriver::GetMixer()->GetTotalAudioTimeNs() < audDriver::GetMixer()->GetTotalVideoTimeNs() && c_replayPlaybackController.IsExportingAudioFrameCaptureAllowed())
{
audDriver::GetMixer()->EnterReplaySwitchLock();
audDriver::GetMixer()->TriggerUpdate();
audDriver::GetMixer()->WaitOnMixBuffers();
audDriver::GetMixer()->ExitReplaySwitchLock();
//Displayf("Catch Up Audio: %LLu chasing (%LLu)",
// NANOSECONDS_TO_ONE_H_NS_UNITS(audDriver::GetMixer()->GetTotalAudioTimeNs()),
// NANOSECONDS_TO_ONE_H_NS_UNITS(audDriver::GetMixer()->GetTotalVideoTimeNs()));
}
}
if(audDriver::GetMixer()->m_ShouldStartFixedFrameRender)
{
audDriver::GetMixer()->m_ShouldStartFixedFrameRender = false;
audDriver::GetMixer()->SetReplayMixerModeRendering();
}
if(audDriver::GetMixer()->m_ShouldStopFixedFrameRender)
{
audDriver::GetMixer()->m_ShouldStopFixedFrameRender = false;
audDriver::GetMixer()->SetReplayMixerModeLoading();
}
#endif
#if __DEV
CGameWorld::AllowDelete(true);
#endif
#if GTA_REPLAY
PF_PUSH_TIMEBAR_IDLE("ReplayMgrPostRender");
CReplayMgr::PostRender();
PF_POP_TIMEBAR();
#endif
s_perfControl->EndFrame();
#if TRACING_STATS
gTracingStats.Process();
#endif
#if RSG_PC
CApp::CheckExit();
#else
if (CApp::WantToExit())
{
SetState(State_ShutdownGame);
}
#endif
#if __PS3 && !__FINAL
pfDummySync();
#endif // __PS3 && !__FINAL
RAGETRACE_PUSHNAME("CSystem::EndUpdate");
CSystem::EndUpdate();
RAGETRACE_POP();
m_StallDetection.EndFrame();
#if RAGE_TIMEBARS
if (!PARAM_disableframemarkers.Get())
{
pfTimebarFuncs::g_PopMarkerCb(0);
}
#endif // RAGE_TIMEBARS
return fwFsm::Continue;
}
#if RSG_PC
static bool sbDisabledScui = false;
static bool sbClosedScui = false;
static bool sbWarningAccept = false;
static bool sbWarningTriggered = false;
void GracefullyShutdownNetwork()
{
#if __STEAM_BUILD
if (SteamUser())
{
CSteamID steamId = SteamUser()->GetSteamID();
SteamUser()->EndAuthSession(steamId);
}
#endif
#if EPIC_API_SUPPORTED
g_SysEpic.ShutdownClass();
#endif
CNetwork::Bail(sBailParameters(BAIL_EXIT_GAME, BAIL_CTX_EXIT_GAME_FROM_APP));
CNetwork::ShutdownConnectionManager();
rlShutdown();
netShutdown();
}
void ProcessScuiCloseLogic()
{
if(g_rlPc.GetUiInterface())
{
if (!sbClosedScui && g_rlPc.GetUiInterface()->IsVisible())
{
Displayf("Closing SCUI for Quit Game message");
g_rlPc.GetUiInterface()->CloseUi();
sbClosedScui = true;
}
if (!sbDisabledScui)
{
Displayf("Disabling SCUI for Quit Game message");
g_rlPc.GetUiInterface()->EnableHotkey(false);
sbDisabledScui = true;
}
}
}
bool CApp::IsShutdownConfirmed()
{
return sbWarningAccept || CPauseMenu::GetGameWantsToExitToWindows();
}
bool CApp::IsScuiDisabledForShutdown()
{
return sbDisabledScui || sbClosedScui || sbWarningTriggered;
}
void CApp::CheckExit()
{
// ===========================================
// PROCESSING THE STARTING OF THE EXIT FLOW
// ===========================================
if (GRCDEVICE.QueryCloseWindow() || g_rlPc.HasUserRequestedShutdownViaScui() || CSystem::WantToRestart())
{
if(!NetworkInterface::GetNetworkExitFlow().IsExitFlowRunning())
{
// Network game is in progress, start the exit flow
if(NetworkInterface::IsGameInProgress())
{
NetworkInterface::GetNetworkExitFlow().StartExitSaveFlow();
}
// If network game is not in progress, the user may have requested a restart. Thus, they have already
// accepted the warning
else if (CSystem::WantToRestart())
{
sbWarningTriggered = false;
sbWarningAccept = true;
}
// Otherwise just go for straight to the warning message
else
{
#if RSG_PC
if (PARAM_benchmark.Get())
{
sbWarningTriggered = false;
sbWarningAccept = true;
}
else
#endif
{
sbWarningTriggered = true;
}
}
}
// If we have yet to close the SCUI, but the interface is valid and visible, close it and disable reopening.
ProcessScuiCloseLogic();
}
// ===========================================
// PROCESSING THE UI WARNINGS OF THE EXIT FLOW
// ===========================================
// If the exit flow is running and it hasn't finished, display the spinner warning
if (NetworkInterface::GetNetworkExitFlow().IsSaveFlowRunning() && !NetworkInterface::GetNetworkExitFlow().IsSaveFlowFinished())
{
// Game Restart comes from the Pause Menu which the user has already accepted
if (CSystem::WantToRestart())
{
CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", NetworkInterface::GetNetworkExitFlow().GetExitString(), FE_WARNING_SPINNER);
}
else
{
CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", "EXIT_SURE_2", FE_WARNING_SPINNER);
}
}
else if (NetworkInterface::GetNetworkExitFlow().IsShutdownTasksRunning() && !NetworkInterface::GetNetworkExitFlow().IsReadyToShutdown())
{
CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", NetworkInterface::GetNetworkExitFlow().GetExitString(), FE_WARNING_SPINNER);
}
else if (NetworkInterface::GetNetworkExitFlow().IsReadyToShutdown())
{
CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", NetworkInterface::GetNetworkExitFlow().GetExitString(), FE_WARNING_SPINNER);
sbWarningAccept = true;
}
// ===========================================
// PROCESSING THE INPUT OF THE EXIT FLOW
// ===========================================
if (NetworkInterface::GetNetworkExitFlow().IsSaveFlowFinished() || sbWarningTriggered)
{
// If we have yet to close the SCUI, but the interface is valid and visible, close it and disable reopening.
ProcessScuiCloseLogic();
if(sbWarningTriggered)
{
// Entitlement Manager worker thread is not safe, and must be allowed to finish.
#if RSG_ENTITLEMENT_ENABLED
if (CEntitlementManager::IsReadyToShutdown())
{
CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", "EXIT_SURE_2", FE_WARNING_YES_NO);
}
else
#endif
{
CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", "EXIT_SURE_2", FE_WARNING_SPINNER);
}
}
else if (CSystem::WantToRestart())
{
NetworkInterface::GetNetworkExitFlow().StartShutdownTasks();
}
else
{
// Warning messages for Network game, based on whether the save succeeded
if(NetworkInterface::GetNetworkExitFlow().GetExitFlowState() == NetworkExitFlow::ETD_SAVE_SUCCESS)
{
CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "WARNING_EXIT_WINDOWS2", "EXIT_SURE_2", FE_WARNING_YES_NO);
}
else if(NetworkInterface::GetNetworkExitFlow().GetExitFlowState() == NetworkExitFlow::ETD_SAVE_FAILED)
{
CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "SG_HDNG", "SG_FAILED_ON_QUIT", FE_WARNING_YES_NO);
}
else
{
Assertf(0, "Exit State is incorrect");
}
}
eWarningButtonFlags result = CWarningScreen::CheckAllInput(false);
if (result == FE_WARNING_YES)
{
sbWarningTriggered = false;
GRCDEVICE.ClearCloseWindow();
NetworkInterface::GetNetworkExitFlow().StartShutdownTasks();
}
if (result == FE_WARNING_NO)
{
sbWarningTriggered = false;
sbWarningAccept = false;
GRCDEVICE.ClearCloseWindow();
// Clear SCUI shutdown flags
sbClosedScui = false;
sbDisabledScui = false;
g_rlPc.SetUserRequestedShutdownViaScui(false);
// Re-Enable SCUI hotkey
if(g_rlPc.GetUiInterface())
{
g_rlPc.GetUiInterface()->EnableHotkey(true);
}
// Reset Network shutdown
if(NetworkInterface::GetNetworkExitFlow().IsExitFlowRunning())
{
NetworkInterface::GetNetworkExitFlow().ResetExitFlowState();
}
}
}
if (CSystem::WantToExit())
{
GRCDEVICE.ClearCloseWindow();
}
// ===========================================
// PROCESSING THE RESTART/SHUT DOWN
// ===========================================
if (IsShutdownConfirmed() && !NetworkInterface::GetNetworkExitFlow().IsShutdownTasksRunning())
{
bool bWantRestart = false;
if (CSystem::WantToRestart())
{
if (g_rlPc.GetProfileManager() != NULL && g_rlPc.GetProfileManager()->IsSignedIn())
{
g_rlPc.GetProfileManager()->CreateSigninTransferProfile();
}
if (g_rlPc.GetDownloaderPipe() && g_rlPc.GetDownloaderPipe()->IsConnected())
{
g_rlPc.GetDownloaderPipe()->RestartGame();
}
else
{
bWantRestart = true;
}
}
#if USE_RAGESEC
// By now the tasks and plugins should be canceled but there is a case where
// not all are so this will help with that. Two updates for good measure
if (rageSecPluginManager::CancelTasks())
{
netTask::Update();
netTask::Update();
}
#endif
GracefullyShutdownNetwork();
#if USE_RAGESEC
// Turns out on PC we just call Exit(), and I want to do proper memory
// sanitization / cleanup / DLL-handle-freeing, so I need to inject
// myself and shutdown rageSecEngine
rageSecEngine::Shutdown(rage::SHUTDOWN_CORE);
#endif
CGenericGameStorage::FinishAllUnfinishedSaveGameOperations();
WriteExitFile(NULL);
if (bWantRestart)
{
Restart();
}
ExitProcess(0);
}
}
void CApp::Restart()
{
#if RSG_PC
const int MODULE_FILENAME_LENGTH = 1024;
TCHAR strModuleFileName[MODULE_FILENAME_LENGTH];
DWORD dwResult = GetModuleFileName(NULL, strModuleFileName, MODULE_FILENAME_LENGTH);
if ((dwResult > 0) && (dwResult < MODULE_FILENAME_LENGTH))
{
LPTSTR lpCommandLine = GetCommandLine();
LPTSTR lpCommandLineArg = strstr(lpCommandLine, ".exe");
if (lpCommandLineArg == NULL)
{
lpCommandLineArg = strstr(lpCommandLine, ".EXE");
}
if (lpCommandLineArg)
{
lpCommandLineArg += 4;
if (*lpCommandLineArg == '\"')
{
lpCommandLineArg++;
}
}
SHELLEXECUTEINFO oShellExecuteInfo;
memset(&oShellExecuteInfo, 0, sizeof(SHELLEXECUTEINFO));
oShellExecuteInfo.cbSize = sizeof(SHELLEXECUTEINFO);
oShellExecuteInfo.lpVerb = "open";
oShellExecuteInfo.lpFile = strModuleFileName;
oShellExecuteInfo.lpParameters = lpCommandLineArg;
oShellExecuteInfo.nShow = SW_SHOW;
ShellExecuteEx(&oShellExecuteInfo);
}
#endif RSG_PC
}
void CApp::WriteExitFile(const char* error)
{
static bool s_bOpenedExitFile = false;
// If we've created the out-file already, and have no additional info, there's nothing to do
if (s_bOpenedExitFile && !error)
{
return;
}
// Grab the current pid
DWORD pid = GetCurrentProcessId();
if (pid == 0)
{
return;
}
// Check for an in-file
char path_buffer[MAX_PATH] = {0};
int formattedLength = snprintf(path_buffer, MAX_PATH, "%s\\in-%u", CFileMgr::GetAppDataRootDirectory(), (unsigned int)pid);
if (formattedLength >= MAX_PATH)
{
return;
}
if (!ASSET.Exists(path_buffer, ""))
{
// No in-file; we're probably not being run from the launcher
return;
}
// Create the full file path
formattedLength = snprintf(path_buffer, MAX_PATH, "%s\\out-%u", CFileMgr::GetAppDataRootDirectory(), (unsigned int)pid);
if (formattedLength >= MAX_PATH)
{
return;
}
FileHandle handle = NULL;
// If we've already written an out-file this session, append rather than re-create
if (s_bOpenedExitFile)
{
handle = CFileMgr::OpenFileForAppending(path_buffer);
}
else
{
handle = CFileMgr::OpenFileForWriting(path_buffer);
}
if (handle != NULL)
{
s_bOpenedExitFile = true;
// If we have an error, write it out to the out-file
if (error != NULL)
{
CFileMgr::Write(handle, error, static_cast<s32>(strlen(error)));
}
CFileMgr::CloseFile(handle);
}
}
#endif
// PURPOSE: Shutdown game systems.
fwFsm::Return CApp::ShutdownGame()
{
#if __PPU
// Copied from rage\base\src\rline\rlnp.cpp
sceNp2Term();
#endif // __PPU
delete s_perfControl;
s_perfControl = NULL;
CGame::Shutdown();
SetState(State_ShutdownSystem);
return fwFsm::Continue;
}
// PURPOSE: Shutdown low level systems.
fwFsm::Return CApp::ShutdownSystem()
{
#if RSG_ORBIS
ASSERT_ONLY( int ret = ) sceCoredumpUnregisterCoredumpHandler();
Assert(ret == SCE_OK);
#endif //RSG_ORBIS
CSystem::Shutdown();
#if !__FINAL || !__NO_OUTPUT
netRemoteLog::ShutdownClass();
#endif
#if TRACING_STATS
gTracingStats.Shutdown();
#endif
Displayf("Goodbye");
return fwFsm::Quit;
}
// PURPOSE: Check whether some system has indicated we need to shutdown.
bool CApp::WantToExit()
{
return CSystem::WantToExit();
}
// PURPOSE: Check for a valid dongle.
void CApp::CheckDongle()
{
#if ENABLE_DONGLE
char debugString [500];
bool checkAuthenticity = true;
#if __PPU || RSG_ORBIS || RSG_DURANGO
checkAuthenticity = CDongle::ValidateCodeFile(sEncodeString, debugString, sMCFileName);
#else
checkAuthenticity = ioDongle::ReadCodeFile(sEncodeString, sMCFileName, debugString);
#endif // __PPU
if(!checkAuthenticity)
{
Quitf("Auth fail!");
while(1){} // Endless Loop
}
#endif //ENABLE_DONGLE
}
#if RSG_LAUNCHER_CHECK
bool CApp::CheckParentProcess()
{
PROCESSENTRY32 procentry;
HMODULE hMod ;
char szFileName[ MAX_PATH ] ;
DWORD dwSize2 = 0, pid = 0, crtpid = 0, parentpid = 0;
BOOL bContinue = FALSE;
bool bConfirmed = false;
HANDLE hSnapShot, hProcess;
atString launcherProcess;
atString bootstrapProcess;
atString mtlProcess;
atString mtlSteamHelper;
crtpid = GetCurrentProcessId();
if (crtpid == 0)
{
Errorf("Could not get process ID from the game.");
return false;
}
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapShot == INVALID_HANDLE_VALUE)
{
Errorf("Could not get a valid snapshot of the process.");
return false;
}
procentry.dwSize = sizeof(PROCESSENTRY32);
bContinue = Process32First(hSnapShot, &procentry);
if (!bContinue)
{
Errorf("Could not get the process information from the snapshot.");
return false;
}
while (bContinue)
{
if (crtpid == procentry.th32ProcessID)
pid = procentry.th32ParentProcessID;
procentry.dwSize = sizeof(PROCESSENTRY32);
bContinue = !pid && Process32Next(hSnapShot, &procentry);
}
parentpid = pid;
hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, parentpid);
if (!hProcess)
{
Errorf("Could not do OpenProcess on parent process.");
return false;
}
launcherProcess = CHECK_PARENT_PROCESS;
launcherProcess.Lowercase();
bootstrapProcess = CHECK_BOOTSTRAP_PROCESS;
bootstrapProcess.Lowercase();
mtlProcess = CHECK_MTL_PROCESS;
mtlProcess.Lowercase();
mtlSteamHelper = CHECK_MTL_STEAMHELPER;
mtlSteamHelper.Lowercase();
bConfirmed = false;
if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &dwSize2))
{
if (GetModuleFileNameEx(hProcess, hMod, szFileName, sizeof(szFileName)))
{
const char* exeName = ASSET.FileName(szFileName);
atString currentName(exeName);
currentName.Lowercase();
if (currentName == launcherProcess ||
currentName == bootstrapProcess ||
currentName == mtlProcess ||
currentName == mtlSteamHelper)
{
bConfirmed = true;
}
}
}
CloseHandle(hProcess);
if (!bConfirmed)
{
Errorf("Parent process name does not match the one we are looking for: %s or %s", launcherProcess.c_str(), bootstrapProcess.c_str());
}
return bConfirmed;
}
#endif
#if BACKTRACE_ENABLED && (RSG_PC || RSG_DURANGO)
void CApp::WriteCrashContextLog(const wchar_t* path)
{
s_crashContextHandle = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
if (s_crashContextHandle != INVALID_HANDLE_VALUE)
{
CoredumpHandler(NULL);
CloseHandle(s_crashContextHandle);
s_crashContextHandle = INVALID_HANDLE_VALUE;
}
}
void CApp::CollectAdditionalAttributes()
{
u32 currentTime = fwTimer::GetSystemTimeInMilliseconds();
// Game length
sysException::SetAnnotation("game_lifetime", currentTime);
// Last Launched Script
rage::sysException::BacktraceScriptData lastLaunchedScript = sysException::BacktraceGetLastLaunchedScriptData();
sysException::SetAnnotation("last_launched_script_id", lastLaunchedScript.m_LastLaunchedScriptProgramId);
sysException::SetAnnotation("time_since_last_launched_script", currentTime - lastLaunchedScript.m_TimeSinceLastLaunchedScript);
// Network info
const char* networkState = "unknown";
if (NetworkInterface::IsGameInProgress())
{
networkState = "game_in_progress";
}
else if (NetworkInterface::IsNetworkOpening())
{
networkState = "opening";
}
else if (NetworkInterface::IsNetworkOpen())
{
networkState = "open";
}
else if (NetworkInterface::IsNetworkClosing())
{
networkState = "closing";
}
else if (NetworkInterface::IsNetworkClosePending())
{
networkState = "close_pending";
}
sysException::SetAnnotation("network_state", networkState);
// Number of players
int activePlayers;
if (!NetworkInterface::IsInAnyMultiplayer())
{
// We are not in a Multiplayer session so player count is either 0 or 1,
// If the ped factory has not been init yet, then we are 0
// Otherwise it depends if we can find a local player
if(CPedFactory::GetFactory() == NULL)
{
activePlayers = 0;
}
else if(CGameWorld::FindLocalPlayer())
{
activePlayers = 1;
}
else
{
activePlayers = 0;
}
}
else
{
activePlayers = NetworkInterface::GetNumActivePlayers();
}
sysException::SetAnnotation("active_players", activePlayers);
// Ped Entity Count
sysException::SetAnnotation("entity_count_peds", CPed::GetPool() ? CPed::GetPool()->GetNoOfUsedSpaces() : 0);
// Vehicles Entity Count
s32 totalVehicles = 0;
CVehicle::Pool* pVehiclePool = CVehicle::GetPool();
if (pVehiclePool)
{
s32 i = (s32)pVehiclePool->GetSize();
while (i--)
{
if (pVehiclePool->GetSlot(i))
{
++totalVehicles;
}
}
}
sysException::SetAnnotation("entity_count_vehicles", totalVehicles);
// Objects Entity Count
sysException::SetAnnotation("entity_count_objects", CObject::GetPool() ? CObject::GetPool()->GetNoOfUsedSpaces() : 0);
#if RSG_SCARLETT
const char* gfxModes[] = {
"Performance",
"Quality",
"Performance RT 1440",
"Performance RT",
};
sysException::SetAnnotation("rendering_mode", gfxModes[CSettingsManager::GetInstance().GetPriority()]);
#endif // RSG_SCARLETT
}
#endif //BACKTRACE_ENABLED && (RSG_PC || RSG_XBOX)