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

544 lines
20 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Manager for handling UGC file requests
  4. //
  5. //========================================================================//
  6. #include "cbase.h"
  7. #include "ugc_request_manager.h"
  8. #if !defined(NO_STEAM) && !defined(_PS3)
  9. static uint64 g_TimeStampIncr = 0;
  10. //-----------------------------------------------------------------------------
  11. // LessFunc for UGC operation (priority / timestamp)
  12. //-----------------------------------------------------------------------------
  13. bool UGCOperationsLessFunc( UGCFileRequest_t * const &lhs, UGCFileRequest_t * const &rhs )
  14. {
  15. // If the priorities are equal, then we tie-break on the time they were submitted (rhs wins if another tie occurs)
  16. if ( lhs->GetPriority() == rhs->GetPriority() )
  17. return ( lhs->GetTimestamp() >= rhs->GetTimestamp() );
  18. return ( lhs->GetPriority() < rhs->GetPriority() );
  19. }
  20. //-----------------------------------------------------------------------------
  21. // Constructor
  22. //-----------------------------------------------------------------------------
  23. CUGCFileRequestManager::CUGCFileRequestManager( void )
  24. {
  25. m_PendingFileOperations.SetLessFunc( UGCOperationsLessFunc );
  26. m_FileRequests.SetLessFunc( DefLessFunc( UGCHandle_t ) );
  27. }
  28. //-----------------------------------------------------------------------------
  29. // Purpose:
  30. //-----------------------------------------------------------------------------
  31. bool CUGCFileRequestManager::DeleteFileRequest( UGCHandle_t handle, bool bRemoveFromDisk /*= false*/ )
  32. {
  33. if ( handle == k_UGCHandleInvalid )
  34. return false;
  35. const UGCFileRequest_t *pRequest = GetFileRequestByHandle( handle );
  36. if ( pRequest == NULL )
  37. return false;
  38. // Clear it from our pending work
  39. for ( int i=0; i < m_PendingFileOperations.Count(); i++ )
  40. {
  41. UGCFileRequest_t *pQueueRequest = m_PendingFileOperations.Element( i );
  42. if ( pQueueRequest && pQueueRequest->fileHandle == handle )
  43. {
  44. m_PendingFileOperations.RemoveAt( i );
  45. break;
  46. }
  47. }
  48. // Remove it from our library
  49. if ( m_FileRequests.Remove( handle ) == false )
  50. return false;
  51. // Clean it off disk as well
  52. if ( bRemoveFromDisk )
  53. {
  54. char szLocalFilename[MAX_PATH];
  55. pRequest->fileRequest.GetFullPath( szLocalFilename, sizeof(szLocalFilename) );
  56. g_pFullFileSystem->RemoveFile( szLocalFilename );
  57. }
  58. delete pRequest;
  59. return true;
  60. }
  61. //-----------------------------------------------------------------------------
  62. // Purpose:
  63. //-----------------------------------------------------------------------------
  64. void CUGCFileRequestManager::Update( void )
  65. {
  66. while ( m_PendingFileOperations.Count() )
  67. {
  68. UGCFileRequest_t *pFileRequest = m_PendingFileOperations.ElementAtHead();
  69. Assert( pFileRequest != NULL );
  70. if ( pFileRequest == NULL )
  71. {
  72. // FIXME: Throw a warning
  73. m_PendingFileOperations.RemoveAtHead();
  74. continue;
  75. }
  76. UGCFileRequestStatus_t ugcStatus = pFileRequest->fileRequest.Update();
  77. switch ( ugcStatus )
  78. {
  79. case UGCFILEREQUEST_ERROR:
  80. {
  81. Warning("An error occurred while attempting to download a file from the UGC server!\n");
  82. // Msg("- Deleted: %llu\tPriority:%u\tTimestamp:%u\n", pFileRequest->fileHandle, pFileRequest->unPriority, pFileRequest->unTimestamp );
  83. m_PendingFileOperations.RemoveAtHead();
  84. }
  85. break;
  86. case UGCFILEREQUEST_FINISHED:
  87. {
  88. // If we finished an upload, we need to move the file over into the main library
  89. // FIXME: The library is usually downloaded files ready on disk. Now it means things in the clouds or things on disk...
  90. if ( pFileRequest->nType == UGC_REQUEST_UPLOAD )
  91. {
  92. // If this is invalid, we didn't capture our final file handle properly
  93. Assert( pFileRequest->fileRequest.GetCloudHandle() != k_UGCHandleInvalid );
  94. if ( pFileRequest->fileRequest.GetCloudHandle() != k_UGCHandleInvalid )
  95. {
  96. pFileRequest->fileHandle = pFileRequest->fileRequest.GetCloudHandle();
  97. // Add this into the main list now that it's completed
  98. m_FileRequests.Insert( pFileRequest->fileHandle, pFileRequest );
  99. Log_Msg( LOG_WORKSHOP, "[CUGCRequestManager] Finished uploading %llu\n", pFileRequest->fileHandle );
  100. }
  101. }
  102. else
  103. {
  104. Log_Msg( LOG_WORKSHOP, "[CUGCRequestManager] Finished downloading %llu\n", pFileRequest->fileHandle );
  105. IGameEvent *pEvent = gameeventmanager->CreateEvent( "ugc_file_download_finished" );
  106. if ( pEvent )
  107. {
  108. pEvent->SetUint64( "hcontent", pFileRequest->GetFileHandle() );
  109. gameeventmanager->FireEventClientSide( pEvent );
  110. }
  111. }
  112. // We're done, continue on!
  113. // Msg("- Deleted: %llu\tPriority:%u\tTimestamp:%u\n", pFileRequest->fileHandle, pFileRequest->unPriority, pFileRequest->unTimestamp );
  114. m_PendingFileOperations.RemoveAtHead();
  115. }
  116. break;
  117. case UGCFILEREQUEST_READY:
  118. {
  119. if ( pFileRequest->nType == UGC_REQUEST_DOWNLOAD )
  120. {
  121. // Pass along target directory and filename unless they're not set
  122. const char *lpszTargetDirectory = ( pFileRequest->szTargetDirectory[0] != '\0' ) ? pFileRequest->szTargetDirectory : NULL;
  123. const char *lpszTargetFilename = ( pFileRequest->szTargetFilename[0] != '\0' ) ? pFileRequest->szTargetFilename : NULL;
  124. // We're ready to download, so start us off
  125. UGCFileRequestStatus_t status = pFileRequest->fileRequest.StartDownload( pFileRequest->fileHandle, lpszTargetDirectory, lpszTargetFilename, pFileRequest->unLastUpdateTime, pFileRequest->bForceUpdate );
  126. if ( status == UGCFILEREQUEST_FINISHED )
  127. {
  128. // We're already done (file was on disk)
  129. // FIXME: Roll this into the function call above!
  130. // Msg("- Deleted: %llu\tPriority:%u\tTimestamp:%u\n", pFileRequest->fileHandle, pFileRequest->unPriority, pFileRequest->unTimestamp );
  131. m_PendingFileOperations.RemoveAtHead();
  132. }
  133. if ( status == UGCFILEREQUEST_DOWNLOADING )
  134. {
  135. IGameEvent *pEvent = gameeventmanager->CreateEvent( "ugc_file_download_start" );
  136. if ( pEvent )
  137. {
  138. pEvent->SetUint64( "hcontent", pFileRequest->GetFileHandle() );
  139. pEvent->SetUint64( "published_file_id", pFileRequest->GetPublishedFileID() );
  140. gameeventmanager->FireEventClientSide( pEvent );
  141. }
  142. }
  143. Log_Msg( LOG_WORKSHOP, "[CUGCRequestManager] Beginning download of %llu\n", pFileRequest->fileHandle );
  144. return;
  145. }
  146. else if ( pFileRequest->nType == UGC_REQUEST_UPLOAD )
  147. {
  148. const char *lpszTargetDirectory = ( pFileRequest->szTargetDirectory[0] != '\0' ) ? pFileRequest->szTargetDirectory : NULL;
  149. const char *lpszTargetFilename = ( pFileRequest->szTargetFilename[0] != '\0' ) ? pFileRequest->szTargetFilename : NULL;
  150. char szFullPath[MAX_PATH];
  151. V_SafeComposeFilename( lpszTargetDirectory, lpszTargetFilename, szFullPath, ARRAYSIZE(szFullPath) );
  152. // FIXME: Bleh, this makes all kinds of contracts we don't like!
  153. CUtlBuffer buffer;
  154. // FIXME: Swap for an async read!
  155. if ( !g_pFullFileSystem->ReadFile( pFileRequest->szSourceFilename, "GAME", buffer ) )
  156. {
  157. // We failed to read this off the disk
  158. buffer.Purge();
  159. pFileRequest->fileRequest.ThrowError( "Unable to read file: %s\n", pFileRequest->szSourceFilename );
  160. return;
  161. }
  162. // We're ready to download, so start us off
  163. UGCFileRequestStatus_t status = pFileRequest->fileRequest.StartUpload( buffer, szFullPath );
  164. if ( status == UGCFILEREQUEST_ERROR )
  165. {
  166. // FIXME: Now what?
  167. // m_PendingFileOperations.RemoveAtHead();
  168. Assert( 0 );
  169. }
  170. Log_Msg( LOG_WORKSHOP, "[CUGCRequestManager] Beginning upload of %s\n", szFullPath );
  171. // Done with the memory
  172. buffer.Purge();
  173. return;
  174. }
  175. }
  176. break;
  177. default:
  178. // Working, continue to wait...
  179. return;
  180. break;
  181. }
  182. // The request is complete, continue to the next!
  183. }
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Purpose:
  187. //-----------------------------------------------------------------------------
  188. bool CUGCFileRequestManager::CreateFileDownloadRequest( UGCHandle_t unFileHandle, PublishedFileId_t fileID, const char *lpszTargetDirectory, const char *lpszTargetFilename, uint32 unPriority, uint32 unLastUpdateTime /*=0*/, bool bForceUpdate /*= false*/ )
  189. {
  190. // Must pass in a valid handle if we're downloading
  191. if ( unFileHandle == k_UGCHandleInvalid )
  192. return false;
  193. // Make sure we don't already have a request by this handle
  194. if ( FileRequestExists( unFileHandle ) )
  195. return true;
  196. UGCFileRequest_t *pRequest = new UGCFileRequest_t;
  197. pRequest->nType = UGC_REQUEST_DOWNLOAD;
  198. pRequest->fileHandle = unFileHandle;
  199. pRequest->publishedFileID = fileID;
  200. if ( lpszTargetDirectory != NULL )
  201. {
  202. V_strncpy( pRequest->szTargetDirectory, lpszTargetDirectory, ARRAYSIZE(pRequest->szTargetDirectory) );
  203. V_FixSlashes( pRequest->szTargetDirectory );
  204. }
  205. if ( lpszTargetFilename != NULL )
  206. {
  207. V_strncpy( pRequest->szTargetFilename, lpszTargetFilename, ARRAYSIZE(pRequest->szTargetFilename) );
  208. }
  209. pRequest->unLastUpdateTime = unLastUpdateTime;
  210. pRequest->bForceUpdate = bForceUpdate;
  211. pRequest->unTimestamp = g_TimeStampIncr++; // FIXME: This is to get around some timestamping, in essence larger numbers = newer additions
  212. pRequest->unPriority = unPriority;
  213. // This insert will sort the request into the list properly
  214. m_PendingFileOperations.Insert( pRequest );
  215. // For debugging insertion into priority queue
  216. Log_Msg( LOG_WORKSHOP, "[CUGCFileRequestManager] Inserted Download: %llu\tPriority:%u\tTimestamp:%u\n", pRequest->fileHandle, pRequest->unPriority, pRequest->unTimestamp );
  217. Debug_LogPendingOperations();
  218. // Keep this in our records now
  219. m_FileRequests.Insert( unFileHandle, pRequest );
  220. return true;
  221. }
  222. //-----------------------------------------------------------------------------
  223. // Purpose:
  224. //-----------------------------------------------------------------------------
  225. bool CUGCFileRequestManager::CreateFileUploadRequest( const char *lpszSourceFilename, const char *lpszTargetDirectory, const char *lpszTargetFilename, uint32 unPriority )
  226. {
  227. if ( lpszSourceFilename == NULL )
  228. return false;
  229. // Make sure we don't already have a request for this
  230. char szFullPath[MAX_PATH];
  231. V_SafeComposeFilename( lpszTargetDirectory, lpszTargetFilename, szFullPath, ARRAYSIZE(szFullPath) );
  232. // Make sure we don't have another upload by the same filename in progress
  233. const UGCFileRequest_t *pDuplicateRequest = GetFileRequestByFilename( szFullPath );
  234. if ( pDuplicateRequest != NULL && pDuplicateRequest->nType == UGC_REQUEST_UPLOAD )
  235. return true;
  236. UGCFileRequest_t *pRequest = new UGCFileRequest_t;
  237. pRequest->nType = UGC_REQUEST_UPLOAD;
  238. pRequest->fileHandle = k_UGCHandleInvalid;
  239. if ( lpszTargetDirectory != NULL )
  240. {
  241. V_strncpy( pRequest->szTargetDirectory, lpszTargetDirectory, ARRAYSIZE(pRequest->szTargetDirectory) );
  242. V_FixSlashes( pRequest->szTargetDirectory );
  243. }
  244. if ( lpszTargetFilename != NULL )
  245. {
  246. V_strncpy( pRequest->szTargetFilename, lpszTargetFilename, ARRAYSIZE(pRequest->szTargetFilename) );
  247. }
  248. // Save where we're going to read from
  249. V_strncpy( pRequest->szSourceFilename, lpszSourceFilename, ARRAYSIZE(pRequest->szSourceFilename) );
  250. V_FixSlashes( pRequest->szSourceFilename );
  251. pRequest->unLastUpdateTime = 0;
  252. pRequest->bForceUpdate = false;
  253. pRequest->unTimestamp = g_TimeStampIncr++; // FIXME: This is to get around some timestamping, in essence larger numbers = newer additions
  254. pRequest->unPriority = unPriority;
  255. // This insert will sort the request into the list properly
  256. m_PendingFileOperations.Insert( pRequest );
  257. // For debugging insertion into priority queue
  258. Log_Msg( LOG_WORKSHOP, "[CUGCFileRequestManager] Inserted Upload: %llu\tPriority:%u\tTimestamp:%u\n", pRequest->fileHandle, pRequest->unPriority, pRequest->unTimestamp );
  259. Debug_LogPendingOperations();
  260. return true;
  261. }
  262. //-----------------------------------------------------------------------------
  263. // Purpose:
  264. //-----------------------------------------------------------------------------
  265. const UGCFileRequest_t *CUGCFileRequestManager::GetFileRequestByHandle( UGCHandle_t unFileHandle ) const
  266. {
  267. int nIndex = m_FileRequests.Find( unFileHandle );
  268. if ( nIndex == m_FileRequests.InvalidIndex() )
  269. return NULL;
  270. return m_FileRequests[nIndex];
  271. }
  272. //-----------------------------------------------------------------------------
  273. // Purpose:
  274. //-----------------------------------------------------------------------------
  275. bool CUGCFileRequestManager::FileRequestExists( UGCHandle_t handle ) const
  276. {
  277. int nIndex = m_FileRequests.Find( handle );
  278. return ( nIndex != m_FileRequests.InvalidIndex() );
  279. }
  280. //-----------------------------------------------------------------------------
  281. // Purpose:
  282. //-----------------------------------------------------------------------------
  283. void CUGCFileRequestManager::GetFullPath( UGCHandle_t unFileHandle, char *pDest, size_t nSize ) const
  284. {
  285. const UGCFileRequest_t *pRequest = GetFileRequestByHandle( unFileHandle );
  286. if ( pRequest == NULL )
  287. {
  288. // Clear the return so it's obvious it failed
  289. V_memset( pDest, 0, nSize );
  290. return;
  291. }
  292. pRequest->fileRequest.GetFullPath( pDest, nSize );
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Purpose:
  296. //-----------------------------------------------------------------------------
  297. const char *CUGCFileRequestManager::GetDirectory( UGCHandle_t unFileHandle ) const
  298. {
  299. const UGCFileRequest_t *pRequest = GetFileRequestByHandle( unFileHandle );
  300. if ( pRequest == NULL )
  301. return NULL;
  302. return pRequest->fileRequest.GetDirectory();
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Purpose:
  306. //-----------------------------------------------------------------------------
  307. const char *CUGCFileRequestManager::GetFilename( UGCHandle_t unFileHandle ) const
  308. {
  309. const UGCFileRequest_t *pRequest = GetFileRequestByHandle( unFileHandle );
  310. if ( pRequest == NULL )
  311. return NULL;
  312. return pRequest->fileRequest.GetFilename();
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose:
  316. //-----------------------------------------------------------------------------
  317. UGCFileRequestStatus_t CUGCFileRequestManager::GetStatus( UGCHandle_t unFileHandle ) const
  318. {
  319. const UGCFileRequest_t *pRequest = GetFileRequestByHandle( unFileHandle );
  320. if ( pRequest == NULL )
  321. return UGCFILEREQUEST_INVALID;
  322. return pRequest->fileRequest.GetStatus();
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose: Get the file handle for a request by its target filename
  326. //-----------------------------------------------------------------------------
  327. UGCHandle_t CUGCFileRequestManager::GetFileRequestHandleByFilename( const char *lpszFilename ) const
  328. {
  329. // Get the request by name
  330. const UGCFileRequest_t *pRequest = GetFileRequestByFilename( lpszFilename );
  331. if ( pRequest != NULL )
  332. {
  333. return pRequest->fileHandle;
  334. }
  335. return k_UGCHandleInvalid;
  336. }
  337. //-----------------------------------------------------------------------------
  338. // Purpose: Get the file handle for a request by its target filename
  339. //-----------------------------------------------------------------------------
  340. const UGCFileRequest_t *CUGCFileRequestManager::GetFileRequestByFilename( const char *lpszFilename ) const
  341. {
  342. // FIXME: This is a slow crawl through a list doing stricmps :(
  343. char szFullPath[MAX_PATH];
  344. for ( unsigned int i=0; i < m_FileRequests.Count(); i++ )
  345. {
  346. const UGCFileRequest_t *pRequest = m_FileRequests[i];
  347. pRequest->fileRequest.GetFullPath( szFullPath, ARRAYSIZE(szFullPath) );
  348. if ( !V_stricmp( szFullPath, lpszFilename ) )
  349. return pRequest;
  350. }
  351. // Now move through all the pending operations to see if it's living in there
  352. // We need to do this because uploads don't live in the normal system until they're done uploading
  353. // FIXME: This is going to be doing duplicate work since items can straddle both the known requests and the pending ones
  354. for ( int i=0; i < m_PendingFileOperations.Count(); i++ )
  355. {
  356. const UGCFileRequest_t *pRequest = m_PendingFileOperations.Element(i);
  357. pRequest->fileRequest.GetFullPath( szFullPath, ARRAYSIZE(szFullPath) );
  358. if ( !V_stricmp( szFullPath, lpszFilename ) )
  359. return pRequest;
  360. }
  361. return NULL;
  362. }
  363. //-----------------------------------------------------------------------------
  364. // Purpose:
  365. //-----------------------------------------------------------------------------
  366. UGCFileRequestStatus_t CUGCFileRequestManager::GetStatus( const char *lpszFilename ) const
  367. {
  368. const UGCFileRequest_t *pRequest = GetFileRequestByFilename( lpszFilename );
  369. if ( pRequest == NULL )
  370. return UGCFILEREQUEST_INVALID;
  371. return pRequest->fileRequest.GetStatus();
  372. }
  373. //
  374. //-----------------------------------------------------------------------------
  375. // Purpose: Returns the progress of a file being downloaded from the Steam cloud.
  376. // Return: Always 0 if nothing has begun, or 1 if past the point of downloading, otherwise, the percentage downloaded
  377. //-----------------------------------------------------------------------------
  378. float CUGCFileRequestManager::GetDownloadProgress( UGCHandle_t handle ) const
  379. {
  380. const UGCFileRequest_t *pRequest = GetFileRequestByHandle( handle );
  381. if ( pRequest == NULL )
  382. return 0.0f;
  383. return pRequest->GetProgress();
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose: Promote the specified handle to the top of the priority list
  387. //-----------------------------------------------------------------------------
  388. bool CUGCFileRequestManager::PromoteRequestToTop( UGCHandle_t handle )
  389. {
  390. // The request must be in the system
  391. const UGCFileRequest_t *pRequest = GetFileRequestByHandle( handle );
  392. if ( pRequest == NULL )
  393. return false;
  394. // There must be pending operations to bother continuing
  395. if ( m_PendingFileOperations.Count() == 0 )
  396. return false;
  397. // If we're already at the top, don't bother
  398. const UGCFileRequest_t *pTopRequest = m_PendingFileOperations.ElementAtHead();
  399. if ( pTopRequest == pRequest )
  400. return true;
  401. // This is the top priority currently
  402. uint32 unTopPriority = pTopRequest->unPriority;
  403. // Now we need to find this request in the pending operations
  404. for ( int i = 0; i < m_PendingFileOperations.Count(); i++ )
  405. {
  406. const UGCFileRequest_t *pFoundRequest = m_PendingFileOperations.Element(i);
  407. if ( pRequest == pFoundRequest )
  408. {
  409. // Bump our priority up
  410. // We cast away the const reference because we're the controlling class for this type
  411. ((UGCFileRequest_t *)pRequest)->unPriority = unTopPriority+1;
  412. m_PendingFileOperations.RemoveAt(i);
  413. m_PendingFileOperations.Insert( ((UGCFileRequest_t *)pRequest) );
  414. Log_Msg( LOG_WORKSHOP, "[CUGCFileRequestManager] Promoted %llu to top of queue\n", pRequest->fileHandle );
  415. Debug_LogPendingOperations();
  416. return true;
  417. }
  418. }
  419. return false;
  420. }
  421. //-----------------------------------------------------------------------------
  422. // Purpose: Dump our priority queue so we can debug it
  423. //-----------------------------------------------------------------------------
  424. void CUGCFileRequestManager::Debug_LogPendingOperations( void )
  425. {
  426. #if 0
  427. // Must have something to operate on
  428. if ( m_PendingFileOperations.Count() == 0 )
  429. return;
  430. // For debugging insertion into priority queue
  431. Log_Msg( LOG_WORKSHOP, "\n==[Pending UGC Operations]==\n");
  432. // The queue cannot be walked through trivially, so we need to actually pop each member off the top, the reinsert at the end
  433. CUtlVector< UGCFileRequest_t * > vecOverflow;
  434. while ( m_PendingFileOperations.Count() )
  435. {
  436. UGCFileRequest_t *pQueuedRequest = m_PendingFileOperations.ElementAtHead();
  437. Log_Msg( LOG_WORKSHOP, "o File: %llu\tPriority:%u\tTimestamp:%u\n", pQueuedRequest->fileHandle, pQueuedRequest->unPriority, pQueuedRequest->unTimestamp );
  438. vecOverflow.AddToTail( pQueuedRequest );
  439. m_PendingFileOperations.RemoveAtHead();
  440. }
  441. // Put them all back
  442. for ( int i=0; i < vecOverflow.Count(); i++ )
  443. {
  444. m_PendingFileOperations.Insert( vecOverflow[i] );
  445. }
  446. Log_Msg( LOG_WORKSHOP, "==============================\n\n");
  447. #endif //
  448. }
  449. bool CUGCFileRequestManager::HasPendingDownloads( void ) const
  450. {
  451. return m_PendingFileOperations.Count() > 0;
  452. }
  453. #endif // ! NO_STEAM