Team Fortress 2 Source Code as on 22/4/2020
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.

573 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Defines a group of app systems that all have the same lifetime
  4. // that need to be connected/initialized, etc. in a well-defined order
  5. //
  6. // $Revision: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "appframework/IAppSystemGroup.h"
  10. #include "appframework/IAppSystem.h"
  11. #include "interface.h"
  12. #include "filesystem.h"
  13. #include "filesystem_init.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. //-----------------------------------------------------------------------------
  17. // constructor, destructor
  18. //-----------------------------------------------------------------------------
  19. //extern ILoggingListener *g_pDefaultLoggingListener;
  20. //-----------------------------------------------------------------------------
  21. // constructor, destructor
  22. //-----------------------------------------------------------------------------
  23. CAppSystemGroup::CAppSystemGroup( CAppSystemGroup *pAppSystemParent ) : m_SystemDict(false, 0, 16)
  24. {
  25. m_pParentAppSystem = pAppSystemParent;
  26. }
  27. //-----------------------------------------------------------------------------
  28. // Actually loads a DLL
  29. //-----------------------------------------------------------------------------
  30. CSysModule *CAppSystemGroup::LoadModuleDLL( const char *pDLLName )
  31. {
  32. return Sys_LoadModule( pDLLName );
  33. }
  34. //-----------------------------------------------------------------------------
  35. // Methods to load + unload DLLs
  36. //-----------------------------------------------------------------------------
  37. AppModule_t CAppSystemGroup::LoadModule( const char *pDLLName )
  38. {
  39. // Remove the extension when creating the name.
  40. int nLen = Q_strlen( pDLLName ) + 1;
  41. char *pModuleName = (char*)stackalloc( nLen );
  42. Q_StripExtension( pDLLName, pModuleName, nLen );
  43. // See if we already loaded it...
  44. for ( int i = m_Modules.Count(); --i >= 0; )
  45. {
  46. if ( m_Modules[i].m_pModuleName )
  47. {
  48. if ( !Q_stricmp( pModuleName, m_Modules[i].m_pModuleName ) )
  49. return i;
  50. }
  51. }
  52. CSysModule *pSysModule = LoadModuleDLL( pDLLName );
  53. if (!pSysModule)
  54. {
  55. Warning("AppFramework : Unable to load module %s!\n", pDLLName );
  56. return APP_MODULE_INVALID;
  57. }
  58. int nIndex = m_Modules.AddToTail();
  59. m_Modules[nIndex].m_pModule = pSysModule;
  60. m_Modules[nIndex].m_Factory = 0;
  61. m_Modules[nIndex].m_pModuleName = (char*)malloc( nLen );
  62. Q_strncpy( m_Modules[nIndex].m_pModuleName, pModuleName, nLen );
  63. return nIndex;
  64. }
  65. AppModule_t CAppSystemGroup::LoadModule( CreateInterfaceFn factory )
  66. {
  67. if (!factory)
  68. {
  69. Warning("AppFramework : Unable to load module %p!\n", factory );
  70. return APP_MODULE_INVALID;
  71. }
  72. // See if we already loaded it...
  73. for ( int i = m_Modules.Count(); --i >= 0; )
  74. {
  75. if ( m_Modules[i].m_Factory )
  76. {
  77. if ( m_Modules[i].m_Factory == factory )
  78. return i;
  79. }
  80. }
  81. int nIndex = m_Modules.AddToTail();
  82. m_Modules[nIndex].m_pModule = NULL;
  83. m_Modules[nIndex].m_Factory = factory;
  84. m_Modules[nIndex].m_pModuleName = NULL;
  85. return nIndex;
  86. }
  87. void CAppSystemGroup::UnloadAllModules()
  88. {
  89. // NOTE: Iterate in reverse order so they are unloaded in opposite order
  90. // from loading
  91. for (int i = m_Modules.Count(); --i >= 0; )
  92. {
  93. if ( m_Modules[i].m_pModule )
  94. {
  95. Sys_UnloadModule( m_Modules[i].m_pModule );
  96. }
  97. if ( m_Modules[i].m_pModuleName )
  98. {
  99. free( m_Modules[i].m_pModuleName );
  100. }
  101. }
  102. m_Modules.RemoveAll();
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Methods to add/remove various global singleton systems
  106. //-----------------------------------------------------------------------------
  107. IAppSystem *CAppSystemGroup::AddSystem( AppModule_t module, const char *pInterfaceName )
  108. {
  109. if (module == APP_MODULE_INVALID)
  110. return NULL;
  111. Assert( (module >= 0) && (module < m_Modules.Count()) );
  112. CreateInterfaceFn pFactory = m_Modules[module].m_pModule ? Sys_GetFactory( m_Modules[module].m_pModule ) : m_Modules[module].m_Factory;
  113. int retval;
  114. void *pSystem = pFactory( pInterfaceName, &retval );
  115. if ((retval != IFACE_OK) || (!pSystem))
  116. {
  117. Warning("AppFramework : Unable to create system %s!\n", pInterfaceName );
  118. return NULL;
  119. }
  120. IAppSystem *pAppSystem = static_cast<IAppSystem*>(pSystem);
  121. int sysIndex = m_Systems.AddToTail( pAppSystem );
  122. // Inserting into the dict will help us do named lookup later
  123. MEM_ALLOC_CREDIT();
  124. m_SystemDict.Insert( pInterfaceName, sysIndex );
  125. return pAppSystem;
  126. }
  127. static char const *g_StageLookup[] =
  128. {
  129. "CREATION",
  130. "CONNECTION",
  131. "PREINITIALIZATION",
  132. "INITIALIZATION",
  133. "SHUTDOWN",
  134. "POSTSHUTDOWN",
  135. "DISCONNECTION",
  136. "DESTRUCTION",
  137. "NONE",
  138. };
  139. void CAppSystemGroup::ReportStartupFailure( int nErrorStage, int nSysIndex )
  140. {
  141. char const *pszStageDesc = "Unknown";
  142. if ( nErrorStage >= 0 && nErrorStage < ARRAYSIZE( g_StageLookup ) )
  143. {
  144. pszStageDesc = g_StageLookup[ nErrorStage ];
  145. }
  146. char const *pszSystemName = "(Unknown)";
  147. for ( int i = m_SystemDict.First(); i != m_SystemDict.InvalidIndex(); i = m_SystemDict.Next( i ) )
  148. {
  149. if ( m_SystemDict[ i ] != nSysIndex )
  150. continue;
  151. pszSystemName = m_SystemDict.GetElementName( i );
  152. break;
  153. }
  154. // Walk the dictionary
  155. Warning( "System (%s) failed during stage %s\n", pszSystemName, pszStageDesc );
  156. }
  157. void CAppSystemGroup::AddSystem( IAppSystem *pAppSystem, const char *pInterfaceName )
  158. {
  159. if ( !pAppSystem )
  160. return;
  161. int sysIndex = m_Systems.AddToTail( pAppSystem );
  162. // Inserting into the dict will help us do named lookup later
  163. MEM_ALLOC_CREDIT();
  164. m_SystemDict.Insert( pInterfaceName, sysIndex );
  165. }
  166. void CAppSystemGroup::RemoveAllSystems()
  167. {
  168. // NOTE: There's no deallcation here since we don't really know
  169. // how the allocation has happened. We could add a deallocation method
  170. // to the code in interface.h; although when the modules are unloaded
  171. // the deallocation will happen anyways
  172. m_Systems.RemoveAll();
  173. m_SystemDict.RemoveAll();
  174. }
  175. //-----------------------------------------------------------------------------
  176. // Simpler method of doing the LoadModule/AddSystem thing.
  177. //-----------------------------------------------------------------------------
  178. bool CAppSystemGroup::AddSystems( AppSystemInfo_t *pSystemList )
  179. {
  180. while ( pSystemList->m_pModuleName[0] )
  181. {
  182. AppModule_t module = LoadModule( pSystemList->m_pModuleName );
  183. IAppSystem *pSystem = AddSystem( module, pSystemList->m_pInterfaceName );
  184. if ( !pSystem )
  185. {
  186. Warning( "Unable to load interface %s from %s\n", pSystemList->m_pInterfaceName, pSystemList->m_pModuleName );
  187. return false;
  188. }
  189. ++pSystemList;
  190. }
  191. return true;
  192. }
  193. //-----------------------------------------------------------------------------
  194. // Methods to find various global singleton systems
  195. //-----------------------------------------------------------------------------
  196. void *CAppSystemGroup::FindSystem( const char *pSystemName )
  197. {
  198. unsigned short i = m_SystemDict.Find( pSystemName );
  199. if (i != m_SystemDict.InvalidIndex())
  200. return m_Systems[m_SystemDict[i]];
  201. // If it's not an interface we know about, it could be an older
  202. // version of an interface, or maybe something implemented by
  203. // one of the instantiated interfaces...
  204. // QUESTION: What order should we iterate this in?
  205. // It controls who wins if multiple ones implement the same interface
  206. for ( i = 0; i < m_Systems.Count(); ++i )
  207. {
  208. void *pInterface = m_Systems[i]->QueryInterface( pSystemName );
  209. if (pInterface)
  210. return pInterface;
  211. }
  212. if ( m_pParentAppSystem )
  213. {
  214. void* pInterface = m_pParentAppSystem->FindSystem( pSystemName );
  215. if ( pInterface )
  216. return pInterface;
  217. }
  218. // No dice..
  219. return NULL;
  220. }
  221. //-----------------------------------------------------------------------------
  222. // Gets at the parent appsystem group
  223. //-----------------------------------------------------------------------------
  224. CAppSystemGroup *CAppSystemGroup::GetParent()
  225. {
  226. return m_pParentAppSystem;
  227. }
  228. //-----------------------------------------------------------------------------
  229. // Method to connect/disconnect all systems
  230. //-----------------------------------------------------------------------------
  231. bool CAppSystemGroup::ConnectSystems()
  232. {
  233. for (int i = 0; i < m_Systems.Count(); ++i )
  234. {
  235. IAppSystem *sys = m_Systems[i];
  236. if (!sys->Connect( GetFactory() ))
  237. {
  238. ReportStartupFailure( CONNECTION, i );
  239. return false;
  240. }
  241. }
  242. return true;
  243. }
  244. void CAppSystemGroup::DisconnectSystems()
  245. {
  246. // Disconnect in reverse order of connection
  247. for (int i = m_Systems.Count(); --i >= 0; )
  248. {
  249. m_Systems[i]->Disconnect();
  250. }
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Method to initialize/shutdown all systems
  254. //-----------------------------------------------------------------------------
  255. InitReturnVal_t CAppSystemGroup::InitSystems()
  256. {
  257. for (int i = 0; i < m_Systems.Count(); ++i )
  258. {
  259. InitReturnVal_t nRetVal = m_Systems[i]->Init();
  260. if ( nRetVal != INIT_OK )
  261. {
  262. ReportStartupFailure( INITIALIZATION, i );
  263. return nRetVal;
  264. }
  265. }
  266. return INIT_OK;
  267. }
  268. void CAppSystemGroup::ShutdownSystems()
  269. {
  270. // Shutdown in reverse order of initialization
  271. for (int i = m_Systems.Count(); --i >= 0; )
  272. {
  273. m_Systems[i]->Shutdown();
  274. }
  275. }
  276. //-----------------------------------------------------------------------------
  277. // Returns the stage at which the app system group ran into an error
  278. //-----------------------------------------------------------------------------
  279. CAppSystemGroup::AppSystemGroupStage_t CAppSystemGroup::GetErrorStage() const
  280. {
  281. return m_nErrorStage;
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Gets at a factory that works just like FindSystem
  285. //-----------------------------------------------------------------------------
  286. // This function is used to make this system appear to the outside world to
  287. // function exactly like the currently existing factory system
  288. CAppSystemGroup *s_pCurrentAppSystem;
  289. void *AppSystemCreateInterfaceFn(const char *pName, int *pReturnCode)
  290. {
  291. void *pInterface = s_pCurrentAppSystem->FindSystem( pName );
  292. if ( pReturnCode )
  293. {
  294. *pReturnCode = pInterface ? IFACE_OK : IFACE_FAILED;
  295. }
  296. return pInterface;
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Gets at a class factory for the topmost appsystem group in an appsystem stack
  300. //-----------------------------------------------------------------------------
  301. CreateInterfaceFn CAppSystemGroup::GetFactory()
  302. {
  303. return AppSystemCreateInterfaceFn;
  304. }
  305. //-----------------------------------------------------------------------------
  306. // Main application loop
  307. //-----------------------------------------------------------------------------
  308. int CAppSystemGroup::Run()
  309. {
  310. // The factory now uses this app system group
  311. s_pCurrentAppSystem = this;
  312. // Load, connect, init
  313. int nRetVal = OnStartup();
  314. if ( m_nErrorStage != NONE )
  315. return nRetVal;
  316. // Main loop implemented by the application
  317. // FIXME: HACK workaround to avoid vgui porting
  318. nRetVal = Main();
  319. // Shutdown, disconnect, unload
  320. OnShutdown();
  321. // The factory now uses the parent's app system group
  322. s_pCurrentAppSystem = GetParent();
  323. return nRetVal;
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Virtual methods for override
  327. //-----------------------------------------------------------------------------
  328. int CAppSystemGroup::Startup()
  329. {
  330. return OnStartup();
  331. }
  332. void CAppSystemGroup::Shutdown()
  333. {
  334. return OnShutdown();
  335. }
  336. //-----------------------------------------------------------------------------
  337. // Use this version in cases where you can't control the main loop and
  338. // expect to be ticked
  339. //-----------------------------------------------------------------------------
  340. int CAppSystemGroup::OnStartup()
  341. {
  342. // The factory now uses this app system group
  343. s_pCurrentAppSystem = this;
  344. m_nErrorStage = NONE;
  345. // Call an installed application creation function
  346. if ( !Create() )
  347. {
  348. m_nErrorStage = CREATION;
  349. return -1;
  350. }
  351. // Let all systems know about each other
  352. if ( !ConnectSystems() )
  353. {
  354. m_nErrorStage = CONNECTION;
  355. return -1;
  356. }
  357. // Allow the application to do some work before init
  358. if ( !PreInit() )
  359. {
  360. m_nErrorStage = PREINITIALIZATION;
  361. return -1;
  362. }
  363. // Call Init on all App Systems
  364. int nRetVal = InitSystems();
  365. if ( nRetVal != INIT_OK )
  366. {
  367. m_nErrorStage = INITIALIZATION;
  368. return -1;
  369. }
  370. return nRetVal;
  371. }
  372. void CAppSystemGroup::OnShutdown()
  373. {
  374. // The factory now uses this app system group
  375. s_pCurrentAppSystem = this;
  376. switch( m_nErrorStage )
  377. {
  378. case NONE:
  379. break;
  380. case PREINITIALIZATION:
  381. case INITIALIZATION:
  382. goto disconnect;
  383. case CREATION:
  384. case CONNECTION:
  385. goto destroy;
  386. }
  387. // Cal Shutdown on all App Systems
  388. ShutdownSystems();
  389. // Allow the application to do some work after shutdown
  390. PostShutdown();
  391. disconnect:
  392. // Systems should disconnect from each other
  393. DisconnectSystems();
  394. destroy:
  395. // Unload all DLLs loaded in the AppCreate block
  396. RemoveAllSystems();
  397. // Have to do this because the logging listeners & response policies may live in modules which are being unloaded
  398. // @TODO: this seems like a bad legacy practice... app systems should unload their spew handlers gracefully.
  399. // LoggingSystem_ResetCurrentLoggingState();
  400. // Assert( g_pDefaultLoggingListener != NULL );
  401. // LoggingSystem_RegisterLoggingListener( g_pDefaultLoggingListener );
  402. UnloadAllModules();
  403. // Call an installed application destroy function
  404. Destroy();
  405. }
  406. //-----------------------------------------------------------------------------
  407. //
  408. // This class represents a group of app systems that are loaded through steam
  409. //
  410. //-----------------------------------------------------------------------------
  411. //-----------------------------------------------------------------------------
  412. // Constructor
  413. //-----------------------------------------------------------------------------
  414. CSteamAppSystemGroup::CSteamAppSystemGroup( IFileSystem *pFileSystem, CAppSystemGroup *pAppSystemParent )
  415. {
  416. m_pFileSystem = pFileSystem;
  417. m_pGameInfoPath[0] = 0;
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Used by CSteamApplication to set up necessary pointers if we can't do it in the constructor
  421. //-----------------------------------------------------------------------------
  422. void CSteamAppSystemGroup::Setup( IFileSystem *pFileSystem, CAppSystemGroup *pParentAppSystem )
  423. {
  424. m_pFileSystem = pFileSystem;
  425. m_pParentAppSystem = pParentAppSystem;
  426. }
  427. //-----------------------------------------------------------------------------
  428. // Loads the module from Steam
  429. //-----------------------------------------------------------------------------
  430. CSysModule *CSteamAppSystemGroup::LoadModuleDLL( const char *pDLLName )
  431. {
  432. return m_pFileSystem->LoadModule( pDLLName );
  433. }
  434. //-----------------------------------------------------------------------------
  435. // Returns the game info path
  436. //-----------------------------------------------------------------------------
  437. const char *CSteamAppSystemGroup::GetGameInfoPath() const
  438. {
  439. return m_pGameInfoPath;
  440. }
  441. //-----------------------------------------------------------------------------
  442. // Sets up the search paths
  443. //-----------------------------------------------------------------------------
  444. bool CSteamAppSystemGroup::SetupSearchPaths( const char *pStartingDir, bool bOnlyUseStartingDir, bool bIsTool )
  445. {
  446. CFSSteamSetupInfo steamInfo;
  447. steamInfo.m_pDirectoryName = pStartingDir;
  448. steamInfo.m_bOnlyUseDirectoryName = bOnlyUseStartingDir;
  449. steamInfo.m_bToolsMode = bIsTool;
  450. steamInfo.m_bSetSteamDLLPath = true;
  451. steamInfo.m_bSteam = m_pFileSystem->IsSteam();
  452. if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK )
  453. return false;
  454. CFSMountContentInfo fsInfo;
  455. fsInfo.m_pFileSystem = m_pFileSystem;
  456. fsInfo.m_bToolsMode = bIsTool;
  457. fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath;
  458. if ( FileSystem_MountContent( fsInfo ) != FS_OK )
  459. return false;
  460. // Finally, load the search paths for the "GAME" path.
  461. CFSSearchPathsInit searchPathsInit;
  462. searchPathsInit.m_pDirectoryName = steamInfo.m_GameInfoPath;
  463. searchPathsInit.m_pFileSystem = fsInfo.m_pFileSystem;
  464. if ( FileSystem_LoadSearchPaths( searchPathsInit ) != FS_OK )
  465. return false;
  466. FileSystem_AddSearchPath_Platform( fsInfo.m_pFileSystem, steamInfo.m_GameInfoPath );
  467. Q_strncpy( m_pGameInfoPath, steamInfo.m_GameInfoPath, sizeof(m_pGameInfoPath) );
  468. return true;
  469. }