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.

280 lines
12 KiB

  1. //========== Copyright � Valve Corporation, All rights reserved. ========
  2. //
  3. // Dedicated server's object for managing UGC file subscriptions.
  4. //
  5. //=============================================================================
  6. #ifndef DEDICATED_SERVER_UGC_MANAGER
  7. #define DEDICATED_SERVER_UGC_MANAGER
  8. #if defined( COMPILER_MSVC )
  9. #pragma once
  10. #endif
  11. #define AUTH_KEY_MAX_LEN 64
  12. #include "igamesystem.h" // autogamesystem base class
  13. #include "steam/isteamremotestorage.h" // ugc types/constants
  14. // Class for holding info about a UGC file the server is 'subscribed' to.
  15. struct DedicatedServerUGCFileInfo_t
  16. {
  17. // Pass in a 'publishfiledetails' kv blob returned by the 'GetPublishedFileDetails' web api.
  18. bool BuildFromKV( KeyValues *pPublishedFileDetails );
  19. char m_szFileName[k_cchFilenameMax]; // name_on_disk.extention
  20. uint32 m_unFileSizeInBytes; // size in bytes
  21. char m_szTitle[k_cchPublishedDocumentTitleMax]; // Display name
  22. char m_szUrl[k_cchPublishedFileURLMax]; // File url
  23. uint32 m_unTimeLastUpdated; // Last update to this content
  24. PublishedFileId_t fileId; // UGC id for this content package (file + preview + metadata). Always the same after updates.
  25. UGCHandle_t contentHandle; // UGC content handle to the file we're downloading. This can change if the author updates the file
  26. char m_szFilePath[MAX_PATH]; // Path to store UGC file relative to game directory
  27. bool m_bIsValid; // Did we get all the fields we need from the publish file details KV?
  28. double m_dblPlatFloatTimeReceived; // Plat_FloatTime of when we received this information
  29. EResult m_result; // Result for this entry we got back from steam (is 0 if no problems)
  30. };
  31. // Requests a file from steam over http one unChunkSize at a time, async writes to disk.
  32. class CStreamingUGCDownloader
  33. {
  34. public:
  35. virtual ~CStreamingUGCDownloader();
  36. CStreamingUGCDownloader();
  37. void StartFileDownload( const DedicatedServerUGCFileInfo_t *pFileInfo, uint32 unChunkSize );
  38. bool IsFinished( void ) { return m_bIsFinished; }
  39. void Update ( void );
  40. PublishedFileId_t GetPublishedFileId( void ) { return m_pFileInfo ? m_pFileInfo->fileId : 0; }
  41. const DedicatedServerUGCFileInfo_t* GetFileInfo( void ) { return m_pFileInfo; }
  42. private:
  43. CCallResult< CStreamingUGCDownloader, HTTPRequestCompleted_t > m_httpRequestCallback;
  44. void OnHTTPRequestComplete( HTTPRequestCompleted_t *arg, bool bFailed );
  45. void HTTPRequestPartialContent( uint32 rangeStart, uint32 rangeEnd );
  46. void Cleanup( void );
  47. uint32 m_unChunkSize;
  48. uint32 m_unBytesReceived;
  49. uint32 m_unFileSizeInBytes;
  50. char m_szTempFileName[k_cchFilenameMax]; // stream to a temp file as we download, copy to final dest once we get it all
  51. CUtlBuffer m_fileBuffer;
  52. FSAsyncControl_t m_ioAsyncControl;
  53. const DedicatedServerUGCFileInfo_t *m_pFileInfo;
  54. bool m_bIsFinished;
  55. bool m_bHTTPRequestPending;
  56. HTTPRequestHandle m_hReq;
  57. float m_flTimeLastMessage;
  58. char m_szMapTitle[k_cchPublishedDocumentTitleMax];
  59. // no copy, no assign.
  60. CStreamingUGCDownloader( const CStreamingUGCDownloader& src );
  61. CStreamingUGCDownloader& operator=( const CStreamingUGCDownloader& src );
  62. };
  63. // Wrapper for steam api file info requests.
  64. class CBaseWorkshopHTTPRequest
  65. {
  66. public:
  67. CBaseWorkshopHTTPRequest( const CUtlVector<PublishedFileId_t> &vecFileIDs );
  68. virtual ~CBaseWorkshopHTTPRequest();
  69. void OnHTTPRequestComplete( HTTPRequestCompleted_t *arg, bool bFailed );
  70. bool IsFinished( void ) const { return m_bFinished; }
  71. EHTTPStatusCode GetLastHTTPResult( void ) const { return m_lastHTTPResult; }
  72. const CUtlVector<PublishedFileId_t> & GetItemsQueried( void ) const { return m_vecItemsQueried; }
  73. protected:
  74. virtual void ProcessHTTPResponse( KeyValues *pResponseKV ) { Assert( 0 ); };
  75. CUtlVector< PublishedFileId_t > m_vecItemsQueried;
  76. HTTPRequestHandle m_handle;
  77. EHTTPStatusCode m_lastHTTPResult;
  78. bool m_bFinished;
  79. CCallResult< CBaseWorkshopHTTPRequest, HTTPRequestCompleted_t > m_httpCallback;
  80. CBaseWorkshopHTTPRequest( const CBaseWorkshopHTTPRequest& src );
  81. CBaseWorkshopHTTPRequest& operator=( const CBaseWorkshopHTTPRequest& src );
  82. };
  83. // Turns a list of file ids into filled out info structs
  84. class CPublishedFileInfoHTTPRequest : public CBaseWorkshopHTTPRequest
  85. {
  86. public:
  87. CPublishedFileInfoHTTPRequest( const CUtlVector<PublishedFileId_t>& vecFileIDs );
  88. virtual ~CPublishedFileInfoHTTPRequest();
  89. const CUtlVector<DedicatedServerUGCFileInfo_t*>& GetFileInfoList( void ) const { return m_vecFileInfos; }
  90. HTTPRequestHandle CreateHTTPRequest( const char* szAuthKey = NULL );
  91. virtual void ProcessHTTPResponse( KeyValues *pResponseKV ) OVERRIDE;
  92. protected:
  93. CUtlVector<DedicatedServerUGCFileInfo_t*> m_vecFileInfos;
  94. };
  95. // wrapper for collection info http requests
  96. class CCollectionInfoHTTPRequest : public CBaseWorkshopHTTPRequest
  97. {
  98. public:
  99. CCollectionInfoHTTPRequest( const CUtlVector<PublishedFileId_t>& vecFileIDs );
  100. virtual ~CCollectionInfoHTTPRequest();
  101. HTTPRequestHandle CreateHTTPRequest( const char* szAuthKey = NULL );
  102. virtual void ProcessHTTPResponse( KeyValues *pResponseKV ) OVERRIDE;
  103. KeyValues* GetResponseKV( void ) { return m_pResponseKV; }
  104. protected:
  105. KeyValues* m_pResponseKV;
  106. };
  107. // Keeps track of a map or collection of maps to turn into a map group in the GameTypes system once they've all been updated.
  108. class CWorkshopMapGroupBuilder
  109. {
  110. public:
  111. CWorkshopMapGroupBuilder( PublishedFileId_t id, const CUtlVector< PublishedFileId_t >& m_mapFileIDs );
  112. void MapOnDisk( PublishedFileId_t id, const char* szPath );
  113. void OnMapDownloaded( const DedicatedServerUGCFileInfo_t* pInfo );
  114. bool IsFinished ( void ) const { return m_pendingMapInfos.Count() == 0; }
  115. PublishedFileId_t GetId() const { return m_id; }
  116. const char* GetFirstMap( void ) const;
  117. const char* GetMapMatchingId( PublishedFileId_t id ) const;
  118. void CreateOrUpdateMapGroup( void );
  119. void RemoveRequiredMap( PublishedFileId_t id );
  120. private:
  121. CUtlVector< PublishedFileId_t > m_pendingMapInfos; // Maps we still need the latest version of. Removes entries when map is at latest version on disk.
  122. CUtlStringList m_Maps;
  123. PublishedFileId_t m_id; // collection id, or map id if a single map.
  124. CWorkshopMapGroupBuilder( const CWorkshopMapGroupBuilder& src );
  125. CWorkshopMapGroupBuilder& operator=( const CWorkshopMapGroupBuilder& src );
  126. };
  127. class CDedicatedServerWorkshopManager : public CAutoGameSystem
  128. {
  129. public:
  130. // Autogamesystem overrides.
  131. virtual bool Init( void ) OVERRIDE;
  132. virtual void LevelInitPreEntity( void ) OVERRIDE;
  133. virtual const char* Name( void ) OVERRIDE { return "CDedicatedServerMapWorkshop"; }
  134. virtual void Shutdown( void ) OVERRIDE;
  135. // Gathers all the file IDs this server needs to stay up to date with and sends file info queries to steam.
  136. // EXPENSIVE: does file io.
  137. void GetNewestSubscribedFiles( void );
  138. // To support code addressing maps by mapname and assuming the /maps/ directory,
  139. // this method will return a list of DedicatedServerUGCFileInfos whose mapname matches the given string.
  140. // returns true if any maps are filled out. Only returns map infos whose map is already on disk (no pending downloads).
  141. bool GetMapsMatchingName( const char* szMapName, CUtlVector<const DedicatedServerUGCFileInfo_t*>& outVec ) const;
  142. // Get the file ID of a UGC map. Returns 0 if non-ugc map or if not in subscription list.
  143. // Parameter is path to the map relative to the game directory.
  144. PublishedFileId_t GetUGCMapPublishedFileID( const char* szPathToUGCMap ) const;
  145. // Returns the path to the map file for a given file id, or NULL if map is not on disk.
  146. const char* GetUGCMapPath( PublishedFileId_t id ) const;
  147. // This ticks from ServerGameDll::Think... ideally this would be an event
  148. // driven system but we'd need callbacks from the async append for that to work.
  149. void Update( void );
  150. // List of file IDs for subscribed maps on disk.
  151. const CUtlVector< PublishedFileId_t >& GetWorkshopMapList( void ) const;
  152. // Get the latest version of a map or collection and switch to it once the latest version is downloaded.
  153. void HostWorkshopMap( PublishedFileId_t id );
  154. void HostWorkshopMapCollection( PublishedFileId_t id );
  155. bool HasPendingMapDownloads( void ) const;
  156. void CheckForNewVersion( PublishedFileId_t id );
  157. void CheckIfCurrentLevelNeedsUpdate( void );
  158. bool CurrentLevelNeedsUpdate( void ) const;
  159. void SetTargetStartMap( PublishedFileId_t id ) { m_unTargetStartMap = id; }
  160. // Get the maps for which we downloaded UGC information successfully
  161. void GetWorkshopMasWithValidUgcInformation( CUtlVector<const DedicatedServerUGCFileInfo_t *>& outVec ) const;
  162. protected:
  163. void UpdatePublishedFileInfoRequests( void ); // Empties pending url list, calls webapi requesting info for them
  164. // Returns the collection id if sucessful, 0 otherwise.
  165. PublishedFileId_t ParseCollectionInfo( KeyValues * pDetails );
  166. void UpdateUGCDownloadRequests( void ); // http get for the content's URL, stream to disk
  167. bool IsFileLatestVersion( const DedicatedServerUGCFileInfo_t* ugcInfo ); // Test file timestamp vs last update time.
  168. void UpdateFiles( const CUtlVector<PublishedFileId_t>& vecFileIDs );
  169. void UpdateFile( PublishedFileId_t id );
  170. void OnFileInfoReceived( const DedicatedServerUGCFileInfo_t *pInfo );
  171. void OnFileInfoRequestFailed( PublishedFileId_t id );
  172. void OnCollectionInfoReceived( PublishedFileId_t collectionId, const CUtlVector< PublishedFileId_t > & vecCollectionItems );
  173. void OnCollectionInfoRequestFailed( PublishedFileId_t id );
  174. void OnFileDownloaded( const DedicatedServerUGCFileInfo_t *pInfo );
  175. // Record we have this bsp on disk (may not be up to date)
  176. void NoteWorkshopMapOnDisk( PublishedFileId_t id, const char* szPath );
  177. bool ShouldUpdateCollection( PublishedFileId_t id, const CUtlVector<PublishedFileId_t>& vecMaps );
  178. void RemoveFileInfo ( PublishedFileId_t id );
  179. void Cleanup( void );
  180. void QueueDownloadFile( const DedicatedServerUGCFileInfo_t *pFileInfo );
  181. // List of downloads in progress
  182. CUtlVector< CStreamingUGCDownloader* > m_PendingFileDownloads;
  183. CUtlVector< PublishedFileId_t > m_FileInfoQueries; // info requests yet to be sent
  184. CUtlVector< CPublishedFileInfoHTTPRequest* > m_PendingFileInfoRequests; // info requests in flight
  185. CUtlVector< PublishedFileId_t > m_CollectionInfoQueries; // both use file ids, but if it's an id for a collection we need to call a different webapi
  186. CUtlVector< CCollectionInfoHTTPRequest* > m_PendingCollectionInfoRequests; // collection info requests in flight
  187. CUtlVector< PublishedFileId_t > m_vecMapsBeingUpdated; // IDs of maps either waiting for newest file info or in the process of downloading
  188. CUtlVector< PublishedFileId_t > m_vecWorkshopMapList; // Maps we have on disk
  189. CUtlMap< PublishedFileId_t, CUtlString > m_mapWorkshopIdsToMapNames;
  190. CUtlVector< PublishedFileId_t > m_vecFileQueryRetries;
  191. CUtlVector< PublishedFileId_t > m_vecCollectionQueryRetries;
  192. // Map of files we have queried info for.
  193. typedef CUtlMap< PublishedFileId_t, DedicatedServerUGCFileInfo_t* > MapFileIdToUgcFileInfo_t;
  194. MapFileIdToUgcFileInfo_t m_UGCFileInfos;
  195. // Helper class to keep track of our updating/downloading of a set of maps, then create a map group when we have them all.
  196. CWorkshopMapGroupBuilder* m_pMapGroupBuilder;
  197. PublishedFileId_t m_desiredHostCollection;
  198. PublishedFileId_t m_unTargetStartMap; // supports server ops specifying which map in a collection they wish to start on.
  199. char m_szWebAPIAuthKey[AUTH_KEY_MAX_LEN];
  200. bool m_bFoundAuthKey;
  201. bool m_bCurrentLevelNeedsUpdate; // Only gets updated if external caller requests it... only needed for a special case in cs_gamerules's restarting logic.
  202. PublishedFileId_t m_hackCurrentMapInfoCheck; // HACK: Will skip the next download for a file matching this publish ID, then zero this member.
  203. bool m_bHostedCollectionUpdatePending;
  204. double m_fTimeLastVersionCheck;
  205. CUtlMap< PublishedFileId_t, KeyValues* > m_mapPreviousCollectionQueryCache; // Old collection infos for use when we can't talk to steam.
  206. };
  207. CDedicatedServerWorkshopManager& DedicatedServerWorkshop( void );
  208. #endif // DEDICATED_SERVER_UGC_MANAGER