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.

795 lines
24 KiB

  1. //========= Copyright � 1996-2003, Valve LLC, All rights reserved. ============
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tier1/keyvalues.h"
  8. #include "econ_gcmessages.h"
  9. #include "econ_item_system.h"
  10. #include "econ_item_inventory.h"
  11. #include "game_item_schema.h"
  12. #include "utldict.h"
  13. #include "filesystem.h"
  14. #include "steam/isteamhttp.h"
  15. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  16. #include "ihasattributes.h"
  17. #include "tier0/icommandline.h"
  18. #endif
  19. #if defined(CLIENT_DLL)
  20. #include "igameevents.h"
  21. #endif
  22. // FIXME FIXME FIXME
  23. #if defined(TF_DLL) || defined(TF_CLIENT_DLL)
  24. #include "tf_item_system.h"
  25. #endif // defined(TF_DLL) || defined(TF_CLIENT_DLL)
  26. #if defined (DOTA_CLIENT_DLL) || defined (DOTA_DLL)
  27. #include "econ/dota_item_system.h"
  28. #endif // defined (DOTA_CLIENT_DLL) || defined (DOTA_DLL)
  29. #if defined( CSTRIKE_CLIENT_DLL ) || defined( CSTRIKE15 )
  30. #include "cstrike15_item_system.h"
  31. #endif
  32. // memdbgon must be the last include file in a .cpp file!!!
  33. #include "tier0/memdbgon.h"
  34. #if (defined(GAME_DLL) || defined(CLIENT_DLL)) && defined(_DEBUG)
  35. ConVar item_debug( "item_debug", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  36. ConVar item_debug_validation( "item_debug_validation", "1", FCVAR_REPLICATED | FCVAR_ARCHIVE, "If set, CEconEntity::ValidateEntityAttachedToPlayer behaves as it would in release builds and also allows bot players to take the same code path as real players." );
  37. static ConVar item_quality_chance_unique( "item_quality_chance_unique", "0.1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Percentage chance that a random item is unique." );
  38. static ConVar item_quality_chance_rare( "item_quality_chance_rare", "0.5", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Percentage chance that a random item is a rare." );
  39. static ConVar item_quality_chance_common( "item_quality_chance_common", "1.0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Percentage chance that a random item is common." );
  40. #endif
  41. //-----------------------------------------------------------------------------
  42. // Purpose: Get at the global item system
  43. //-----------------------------------------------------------------------------
  44. CEconItemSystem *ItemSystem( void )
  45. {
  46. static GameItemSystem_t *pSystem = NULL;
  47. if ( !pSystem )
  48. {
  49. pSystem = new GameItemSystem_t();
  50. }
  51. return pSystem;
  52. }
  53. //-----------------------------------------------------------------------------
  54. // Purpose: Global schema access, declared in game_item_schema.h
  55. //-----------------------------------------------------------------------------
  56. GameItemSchema_t *GetItemSchema()
  57. {
  58. return ItemSystem()->GetItemSchema();
  59. }
  60. //-----------------------------------------------------------------------------
  61. // Purpose:
  62. //-----------------------------------------------------------------------------
  63. CEconItemSystem::CEconItemSystem( void )
  64. {
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose:
  68. //-----------------------------------------------------------------------------
  69. CEconItemSystem::~CEconItemSystem( void )
  70. {
  71. Shutdown();
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose: Parse in our data files.
  75. //-----------------------------------------------------------------------------
  76. void CEconItemSystem::Init( void )
  77. {
  78. #ifdef CLIENT_DLL
  79. IGameEvent *event = gameeventmanager->CreateEvent( "item_schema_initialized" );
  80. if ( event )
  81. {
  82. gameeventmanager->FireEventClientSide( event );
  83. }
  84. #endif
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose:
  88. //-----------------------------------------------------------------------------
  89. void CEconItemSystem::Shutdown( void )
  90. {
  91. }
  92. extern ConVar mp_tournament;
  93. #ifdef GAME_DLL
  94. ConVar mp_tournament_whitelist( "mp_tournament_whitelist", "item_whitelist.txt", FCVAR_NONE, "Specifies the item whitelist file to use." );
  95. #endif
  96. //-----------------------------------------------------------------------------
  97. // Purpose:
  98. //-----------------------------------------------------------------------------
  99. void CEconItemSystem::ReloadWhitelist( void )
  100. {
  101. // Default state of items depends on whether we're in tourney mode, and whether there's a whitelist
  102. bool bDefault = true;
  103. bool bFoundWhitelist = false;
  104. KeyValues::AutoDelete pWhitelistKV( "item_whitelist" );
  105. #ifdef GAME_DLL
  106. if ( mp_tournament.GetBool() && mp_tournament_whitelist.GetString() )
  107. {
  108. const char *pszWhitelistFile = mp_tournament_whitelist.GetString();
  109. if ( pWhitelistKV->LoadFromFile( filesystem, pszWhitelistFile ) )
  110. {
  111. // Allow the whitelist to override the default, so they can turn it into a blacklist if they want to
  112. bDefault = pWhitelistKV->GetBool( "unlisted_items_default_to" );
  113. bFoundWhitelist = true;
  114. }
  115. else if ( pszWhitelistFile && pszWhitelistFile[0] )
  116. {
  117. Msg("Item Whitelist file '%s' could not be found. All items will be allowed.\n", pszWhitelistFile );
  118. }
  119. }
  120. #endif
  121. const CEconItemSchema::ItemDefinitionMap_t& mapItemDefs = m_itemSchema.GetItemDefinitionMap();
  122. FOR_EACH_MAP_FAST( mapItemDefs, i )
  123. {
  124. mapItemDefs[i]->SetAllowedInMatch( bDefault );
  125. }
  126. // If we didn't find a file, we're done.
  127. if ( !bFoundWhitelist )
  128. return;
  129. // Otherwise, go through the KVs and turn on the matching items.
  130. Msg("Parsing item whitelist (default: %s)\n", bDefault ? "allowed" : "disallowed" );
  131. for ( KeyValues *pKey = pWhitelistKV->GetFirstSubKey(); pKey != NULL; pKey = pKey->GetNextKey() )
  132. {
  133. bool bAllow = pKey->GetBool();
  134. const char *pszItemName = pKey->GetName();
  135. if ( pszItemName && pszItemName[0] && !FStrEq("unlisted_items_default_to", pszItemName) )
  136. {
  137. CEconItemDefinition *pItemDef = m_itemSchema.GetItemDefinitionByName( pszItemName );
  138. if ( pItemDef )
  139. {
  140. pItemDef->SetAllowedInMatch( bAllow );
  141. Msg(" -> %s '%s'\n", bAllow ? "Allowing" : "Removing", pszItemName );
  142. }
  143. else
  144. {
  145. Warning(" -> Could not find an item definition named '%s'\n", pszItemName );
  146. }
  147. }
  148. }
  149. Msg("Finished.\n");
  150. }
  151. //-----------------------------------------------------------------------------
  152. // Purpose:
  153. //-----------------------------------------------------------------------------
  154. void CEconItemSystem::ResetAttribStringCache( void )
  155. {
  156. CEconItemSchema::EconAttrDefsContainer_t &mapDefs = m_itemSchema.GetAttributeDefinitionContainer();
  157. FOR_EACH_VEC( mapDefs, i )
  158. {
  159. if ( mapDefs[i] )
  160. mapDefs[i]->ClearStringCache();
  161. }
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Purpose:
  165. //-----------------------------------------------------------------------------
  166. bool CEconItemSystem::DecryptItemFiles( KeyValues *pKV, const char *pName )
  167. {
  168. char szFullName[512];
  169. Q_snprintf(szFullName,sizeof(szFullName), "%s.ctx", pName );
  170. FileHandle_t f = filesystem->Open( szFullName, "rb", "MOD" );
  171. if (!f)
  172. {
  173. #if !defined(CSTRIKE_DLL)
  174. Warning("No %s file found. May be unable to create items.\n", pName );
  175. #endif // CSTRIKE_DLL
  176. return false;
  177. }
  178. int fileSize = filesystem->Size(f);
  179. char *buffer = (char*)MemAllocScratch(fileSize + 1);
  180. Assert(buffer);
  181. filesystem->Read(buffer, fileSize, f); // read into local buffer
  182. buffer[fileSize] = 0; // null terminate file as EOF
  183. filesystem->Close( f ); // close file after reading
  184. UTIL_DecodeICE( (unsigned char*)buffer, fileSize, GetEncryptionKey() );
  185. bool retOK = pKV->LoadFromBuffer( szFullName, buffer, filesystem );
  186. MemFreeScratch();
  187. if ( !retOK )
  188. return false;
  189. return true;
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Purpose: Read the specified item schema file. Init the item schema with the contents
  193. //-----------------------------------------------------------------------------
  194. void CEconItemSystem::ParseItemSchemaFile( const char *pFilename )
  195. {
  196. CUtlVector< CUtlString > vecErrors;
  197. bool bSuccess = m_itemSchema.BInit( pFilename, "MOD", &vecErrors );
  198. if( !bSuccess )
  199. {
  200. FOR_EACH_VEC( vecErrors, nError )
  201. {
  202. Msg( "%s", vecErrors[nError].String() );
  203. }
  204. }
  205. }
  206. //-----------------------------------------------------------------------------
  207. // Purpose: Generate a random item matching the specified criteria
  208. //-----------------------------------------------------------------------------
  209. int CEconItemSystem::GenerateRandomItem( CItemSelectionCriteria *pCriteria, entityquality_t *outEntityQuality )
  210. {
  211. // First, pick a random item quality (use the one passed in first)
  212. if ( !pCriteria->BQualitySet() )
  213. {
  214. pCriteria->SetQuality( GetRandomQualityForItem() );
  215. }
  216. pCriteria->SetIgnoreEnabledFlag( true );
  217. // Determine which item templates match the criteria
  218. CUtlVector<int> vecMatches;
  219. const CEconItemSchema::ItemDefinitionMap_t &mapDefs = m_itemSchema.GetItemDefinitionMap();
  220. HackMakeValidList:
  221. FOR_EACH_MAP_FAST( mapDefs, i )
  222. {
  223. if ( pCriteria->BEvaluate( mapDefs[i], m_itemSchema ) )
  224. {
  225. vecMatches.AddToTail( mapDefs.Key( i ) );
  226. }
  227. }
  228. // No valid items?
  229. int iValidItems = vecMatches.Count();
  230. if ( !iValidItems )
  231. {
  232. // If we were searching for a unique item, drop back to a non-unique
  233. if ( pCriteria->GetQuality() == AE_UNIQUE )
  234. {
  235. pCriteria->SetQuality( GetRandomQualityForItem( true ) );
  236. goto HackMakeValidList;
  237. }
  238. return INVALID_ITEM_INDEX;
  239. }
  240. // Choose a random match
  241. int iChosenIdx = RandomInt( 0, (iValidItems-1) );
  242. int iChosenItem = vecMatches[iChosenIdx];
  243. const CEconItemDefinition *pItemDef = m_itemSchema.GetItemDefinition( iChosenItem );
  244. if ( !pItemDef )
  245. return INVALID_ITEM_INDEX;
  246. // If we haven't specified an entity quality, we want to use the item's specified one
  247. if ( pCriteria->GetQuality() == AE_USE_SCRIPT_VALUE )
  248. {
  249. int32 iScriptQuality = pItemDef->GetQuality();
  250. pCriteria->SetQuality( iScriptQuality == AE_UNDEFINED ? GetRandomQualityForItem( true ) : iScriptQuality );
  251. }
  252. // If we haven't specified an item level, we want to use the item's specified one.
  253. if ( !pCriteria->BItemLevelSet() )
  254. {
  255. pCriteria->SetItemLevel( RandomInt( pItemDef->GetMinLevel(), pItemDef->GetMaxLevel() ) );
  256. }
  257. if ( outEntityQuality )
  258. {
  259. *outEntityQuality = pCriteria->GetQuality();
  260. }
  261. return iChosenItem;
  262. }
  263. //-----------------------------------------------------------------------------
  264. // Purpose: Return a random quality for the item specified
  265. //-----------------------------------------------------------------------------
  266. entityquality_t CEconItemSystem::GetRandomQualityForItem( bool bPreventUnique )
  267. {
  268. /*
  269. // Start on the rarest, and work backwards
  270. if ( !bPreventUnique )
  271. {
  272. if ( RandomFloat(0,1) < item_quality_chance_unique.GetFloat() )
  273. return AE_UNIQUE;
  274. }
  275. if ( RandomFloat(0,1) < item_quality_chance_rare.GetFloat() )
  276. return AE_RARITY2;
  277. if ( RandomFloat(0,1) < item_quality_chance_common.GetFloat() )
  278. return AE_RARITY1;
  279. */
  280. return AE_NORMAL;
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Purpose:
  284. //-----------------------------------------------------------------------------
  285. CEconItemAttribute *CEconItemSystem::GenerateAttribute( attrib_definition_index_t iAttributeDefinition, float flValue )
  286. {
  287. // Create a new instance of the chosen attribute
  288. CEconItemAttribute *pAttribute = new CEconItemAttribute( iAttributeDefinition, flValue );
  289. return pAttribute;
  290. }
  291. //-----------------------------------------------------------------------------
  292. // Purpose: Generate an attribute by name. Used for debugging.
  293. //-----------------------------------------------------------------------------
  294. CEconItemAttribute *CEconItemSystem::GenerateAttribute( const char *pszName, float flValue )
  295. {
  296. // Find the attribute index matching the class
  297. CEconItemSchema::EconAttrDefsContainer_t &mapDefs = m_itemSchema.GetAttributeDefinitionContainer();
  298. FOR_EACH_VEC( mapDefs, i )
  299. {
  300. if ( !mapDefs[i] )
  301. continue;
  302. if ( Q_stricmp(pszName, mapDefs[i]->GetDefinitionName()) )
  303. continue;
  304. return GenerateAttribute( mapDefs[i]->GetDefinitionIndex(), flValue );
  305. }
  306. return NULL;
  307. }
  308. static ISteamHTTP *GetISteamHTTP()
  309. {
  310. if ( steamapicontext != NULL && steamapicontext->SteamHTTP() )
  311. {
  312. return steamapicontext->SteamHTTP();
  313. }
  314. #ifndef CLIENT_DLL
  315. if ( steamgameserverapicontext != NULL )
  316. {
  317. return steamgameserverapicontext->SteamHTTP();
  318. }
  319. #endif
  320. return NULL;
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose: Common functionality for using our raw buffer data to initialize
  324. // the schema when safe.
  325. //-----------------------------------------------------------------------------
  326. bool IDelayedSchemaData::InitializeSchemaInternal( CEconItemSchema *pItemSchema, CUtlBuffer& bufRawData, bool bInitAsBinary, uint32 nExpectedVersion )
  327. {
  328. DevMsg( 2, "Applying new item schema, version %08X\n", nExpectedVersion );
  329. CUtlVector<CUtlString> vecErrors;
  330. bool bSuccess = bInitAsBinary
  331. ? pItemSchema->BInitBinaryBuffer( bufRawData, &vecErrors )
  332. : pItemSchema->BInitTextBuffer( bufRawData, &vecErrors );
  333. if( bSuccess )
  334. {
  335. // Sanity-check that we received the version that they sent us
  336. uint32 nOurVersion = pItemSchema->GetVersion();
  337. if ( nExpectedVersion != 0 && nOurVersion != nExpectedVersion )
  338. {
  339. Warning( "**WARNING** Item schema mismatch after update!\n" );
  340. Warning( "GC told us to expect %08X, we got %08X\n", nExpectedVersion, nOurVersion );
  341. }
  342. }
  343. else
  344. {
  345. Warning( "**WARNING** Failed to apply item schema!\n" );
  346. FOR_EACH_VEC( vecErrors, nError )
  347. {
  348. Warning( "%s\n", vecErrors[nError].Get() );
  349. }
  350. }
  351. return bSuccess;
  352. }
  353. //-----------------------------------------------------------------------------
  354. // Purpose: The GC sent us a single block of binary data.
  355. //-----------------------------------------------------------------------------
  356. class DelayedSchemaData_GCDirectData : public IDelayedSchemaData
  357. {
  358. public:
  359. DelayedSchemaData_GCDirectData( const std::string& strBuffer )
  360. : m_bufRawData( strBuffer.data(), strBuffer.size(), CUtlBuffer::READ_ONLY )
  361. {
  362. //
  363. }
  364. virtual bool InitializeSchema( CEconItemSchema *pItemSchema )
  365. {
  366. return InitializeSchemaInternal( pItemSchema, m_bufRawData, true, 0 );
  367. }
  368. private:
  369. CUtlBuffer m_bufRawData;
  370. };
  371. //-----------------------------------------------------------------------------
  372. // Purpose: We received a text file from an HTML request.
  373. //-----------------------------------------------------------------------------
  374. class DelayedSchemaData_HTTPResponseData : public IDelayedSchemaData
  375. {
  376. public:
  377. DelayedSchemaData_HTTPResponseData( ISteamHTTP *pHTTP, HTTPRequestHandle handleHTTPRequest, uint32 unBodySize, uint32 nExpectedVersion )
  378. : m_nExpectedVersion( nExpectedVersion )
  379. {
  380. Assert( pHTTP );
  381. m_bufRawData.SetBufferType( true, true );
  382. m_bufRawData.SeekPut( CUtlBuffer::SEEK_HEAD, unBodySize );
  383. m_bValid = pHTTP->GetHTTPResponseBodyData( handleHTTPRequest, (uint8*)m_bufRawData.Base(), m_bufRawData.TellPut() );
  384. }
  385. virtual bool InitializeSchema( CEconItemSchema *pItemSchema )
  386. {
  387. if ( !m_bValid )
  388. return false;
  389. return InitializeSchemaInternal( pItemSchema, m_bufRawData, false, m_nExpectedVersion );
  390. }
  391. private:
  392. bool m_bValid;
  393. CUtlBuffer m_bufRawData;
  394. uint32 m_nExpectedVersion;
  395. };
  396. #if GC_UPDATE_ITEM_SCHEMA_SUPPORTED
  397. //-----------------------------------------------------------------------------
  398. // Purpose: Update the item schema from the GC
  399. //-----------------------------------------------------------------------------
  400. class CGCUpdateItemSchema : public GCSDK::CGCClientJob
  401. {
  402. public:
  403. CGCUpdateItemSchema( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {
  404. m_szUrl[0] = '\0';
  405. m_nExpectedVersion = 0;
  406. bHTTPCompleted = false;
  407. }
  408. char m_szUrl[512];
  409. uint32 m_nExpectedVersion;
  410. bool bHTTPCompleted;
  411. CCallResult< CGCUpdateItemSchema, HTTPRequestCompleted_t > callback;
  412. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  413. {
  414. GCSDK::CProtoBufMsg< CMsgUpdateItemSchema > msg( pNetPacket );
  415. #if (defined(GAME_DLL) || defined(CLIENT_DLL))
  416. bool bUseGCCopy = true;
  417. if ( steamapicontext && steamapicontext->SteamUtils() && steamapicontext->SteamUtils()->GetConnectedUniverse() != k_EUniversePublic )
  418. {
  419. bUseGCCopy = CommandLine()->FindParm( "-use_local_item_data" ) == 0;
  420. }
  421. #else
  422. bool bUseGCCopy = true;
  423. #endif
  424. if ( bUseGCCopy == false )
  425. {
  426. Msg( "Loading item schema from local file.\n" );
  427. KeyValuesAD pItemsGameKV( "ItemsGameFile" );
  428. if ( pItemsGameKV->LoadFromFile( g_pFullFileSystem, "scripts/items/items_game.txt", "GAME" ) )
  429. {
  430. CUtlBuffer buffer;
  431. pItemsGameKV->WriteAsBinary( buffer );
  432. CUtlVector< CUtlString > vecErrors;
  433. bool bSuccess = ItemSystem()->GetItemSchema()->BInitBinaryBuffer( buffer, &vecErrors );
  434. if( !bSuccess )
  435. {
  436. FOR_EACH_VEC( vecErrors, nError )
  437. {
  438. Warning( "%s\n", vecErrors[nError].Get() );
  439. }
  440. }
  441. }
  442. return true;
  443. }
  444. // Check if we're already up-to-date
  445. m_nExpectedVersion = msg.Body().item_schema_version();
  446. uint32 nCurrentSchemaVersion = ItemSystem()->GetItemSchema()->GetVersion();
  447. if ( m_nExpectedVersion != 0 && m_nExpectedVersion == nCurrentSchemaVersion )
  448. {
  449. Msg( "Current item schema is up-to-date with version %08X.\n", nCurrentSchemaVersion );
  450. return true;
  451. }
  452. // !TEST!
  453. //const char *szURL = "http://cdntest.steampowered.com/apps/440/scripts/items/items_game.b8b7a85b4dd98b139957004b86ec0bc070a59d18.txt";
  454. if ( msg.Body().has_items_game() )
  455. {
  456. DevMsg( 2, "Received %d bytes item schema version %08X direct data; update is queued.\n", (int)msg.Body().items_game().size(), m_nExpectedVersion );
  457. ItemSystem()->GetItemSchema()->MaybeInitFromBuffer( new DelayedSchemaData_GCDirectData( msg.Body().items_game() ) );
  458. }
  459. else
  460. {
  461. // Remember URL
  462. const char *szURL = msg.Body().items_game_url().c_str();
  463. if ( !szURL || !szURL[0] )
  464. {
  465. Warning( "GC sent malformed CGCUpdateItemSchema message: No schema data, no URL\n" );
  466. }
  467. else
  468. {
  469. Q_strncpy( m_szUrl, szURL, sizeof( m_szUrl ) );
  470. //Msg( "Fetching %s to update item schema\n", m_szUrl );
  471. // Send an HTTP request for the file
  472. ISteamHTTP *pHTTP = GetISteamHTTP();
  473. if ( !pHTTP )
  474. {
  475. // We must be a game server. Request the direct data.
  476. Msg( "ISteamHTTP not available to update item schema, requesting direct data for version %08X\n", m_nExpectedVersion );
  477. return true;
  478. }
  479. HTTPRequestHandle hReq = pHTTP->CreateHTTPRequest( k_EHTTPMethodGET, m_szUrl );
  480. pHTTP->SetHTTPRequestNetworkActivityTimeout( hReq, 10 );
  481. SteamAPICall_t hCall;
  482. if ( !pHTTP->SendHTTPRequest( hReq, &hCall ) )
  483. {
  484. Warning( "Failed to update item schema: couldn't fetch %s\n", m_szUrl );
  485. return true;
  486. }
  487. //
  488. // *Wait* for completion.
  489. //
  490. // This is important. The GC needs to be able to safely assume that
  491. // we will not process the next message until we have finished
  492. // dealing with this one.
  493. //
  494. bHTTPCompleted = false;
  495. #ifndef CLIENT_DLL
  496. if ( steamgameserverapicontext != NULL && pHTTP == steamgameserverapicontext->SteamHTTP() )
  497. {
  498. callback.SetGameserverFlag();
  499. }
  500. #endif
  501. callback.Set( hCall, this, &CGCUpdateItemSchema::OnHTTPCompleted );
  502. // Wait for it to finish.
  503. while ( !bHTTPCompleted )
  504. {
  505. BYieldingWaitOneFrame();
  506. }
  507. }
  508. }
  509. return true;
  510. }
  511. void OnHTTPCompleted( HTTPRequestCompleted_t *arg, bool bFailed )
  512. {
  513. // Clear flag, no matter what else, so we can stop yielding
  514. bHTTPCompleted = true;
  515. ISteamHTTP *pHTTP = GetISteamHTTP();
  516. Assert( pHTTP );
  517. if ( !pHTTP ) return;
  518. if ( arg->m_eStatusCode != k_EHTTPStatusCode200OK )
  519. {
  520. Warning( "Failed to update item schema: HTTP status %d fetching %s\n", arg->m_eStatusCode, m_szUrl );
  521. }
  522. else
  523. {
  524. if ( !arg->m_bRequestSuccessful )
  525. {
  526. bFailed = true;
  527. }
  528. if ( !bFailed )
  529. {
  530. uint32 unBodySize;
  531. if ( !pHTTP->GetHTTPResponseBodySize( arg->m_hRequest, &unBodySize ) )
  532. {
  533. Assert( false );
  534. bFailed = true;
  535. }
  536. else
  537. {
  538. DevMsg( 2, "Fetched %d bytes item schema version %08X via HTTP; update is queued.\n", unBodySize, m_nExpectedVersion );
  539. ItemSystem()->GetItemSchema()->MaybeInitFromBuffer( new DelayedSchemaData_HTTPResponseData( pHTTP, arg->m_hRequest, unBodySize, m_nExpectedVersion ) );
  540. }
  541. }
  542. if ( bFailed )
  543. {
  544. Warning( "Failed to update item schema from %s\n", m_szUrl );
  545. }
  546. }
  547. pHTTP->ReleaseHTTPRequest( arg->m_hRequest );
  548. }
  549. };
  550. GC_REG_CLIENT_JOB( CGCUpdateItemSchema, k_EMsgGCUpdateItemSchema );
  551. #endif
  552. #ifdef CLIENT_DLL
  553. //-----------------------------------------------------------------------------
  554. // Purpose: Update the item schema from the GC
  555. //-----------------------------------------------------------------------------
  556. CON_COMMAND_F( econ_show_items_with_tag, "Lists the item definitions that have a specified tag.", FCVAR_CLIENTDLL )
  557. {
  558. if ( args.ArgC() != 2 )
  559. return;
  560. econ_tag_handle_t tagHandle = GetItemSchema()->GetHandleForTag( args.Arg( 1 ) );
  561. FOR_EACH_MAP( GetItemSchema()->GetSortedItemDefinitionMap(), i )
  562. {
  563. const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionMap()[i];
  564. if ( pItemDef->HasEconTag( tagHandle ) )
  565. {
  566. Msg(" '%s'\n", pItemDef->GetDefinitionName() );
  567. }
  568. }
  569. }
  570. #endif // CLIENT_DLL
  571. #ifdef CLIENT_DLL
  572. //-----------------------------------------------------------------------------
  573. // Purpose: Get a file from a given URL.
  574. // Generalized version of the item schema update.
  575. //-----------------------------------------------------------------------------
  576. CGCFetchWebResource::CGCFetchWebResource( GCSDK::CGCClient *pClient, CUtlString strName, CUtlString strURL, bool bForceSkipCache/*=false*/, KeyValues *pkvGETParams/*=NULL*/ ) : GCSDK::CGCClientJob( pClient )
  577. {
  578. m_strName = strName;
  579. m_strURL = strURL;
  580. m_bHTTPCompleted = false;
  581. m_bForceSkipCache = bForceSkipCache;
  582. m_pkvGETParams = pkvGETParams->MakeCopy();
  583. }
  584. CGCFetchWebResource::~CGCFetchWebResource()
  585. {
  586. if ( m_pkvGETParams )
  587. {
  588. m_pkvGETParams->deleteThis();
  589. }
  590. }
  591. bool CGCFetchWebResource::BYieldingRunGCJob()
  592. {
  593. ISteamHTTP *pHTTP = GetISteamHTTP();
  594. if ( !pHTTP )
  595. {
  596. // We must be a game server.
  597. Msg( "ISteamHTTP not available to update web resource.\n" );
  598. return true;
  599. }
  600. // Send an HTTP request for the file.
  601. HTTPRequestHandle hReq = pHTTP->CreateHTTPRequest( k_EHTTPMethodGET, m_strURL.Get() );
  602. pHTTP->SetHTTPRequestNetworkActivityTimeout( hReq, 10 );
  603. // Skip any data that may be cached in the Steam client or elsewhere
  604. if ( m_bForceSkipCache )
  605. {
  606. pHTTP->SetHTTPRequestHeaderValue( hReq, "Cache-Control", "no-cache, no-store" );
  607. }
  608. // Add any GET params
  609. if ( m_pkvGETParams )
  610. {
  611. FOR_EACH_VALUE( m_pkvGETParams, kvSubKey )
  612. {
  613. pHTTP->SetHTTPRequestGetOrPostParameter( hReq, kvSubKey->GetName(), kvSubKey->GetString() );
  614. }
  615. }
  616. SteamAPICall_t hCall;
  617. if ( !pHTTP->SendHTTPRequest( hReq, &hCall ) )
  618. {
  619. Warning( "Failed to update web resource: couldn't fetch %s\n", m_strURL.Get() );
  620. return true;
  621. }
  622. // Wait for completion.
  623. m_bHTTPCompleted = false;
  624. callback.Set( hCall, this, &CGCFetchWebResource::OnHTTPCompleted );
  625. // Wait for it to finish.
  626. while ( !m_bHTTPCompleted )
  627. {
  628. BYieldingWaitOneFrame();
  629. }
  630. return true;
  631. }
  632. void CGCFetchWebResource::OnHTTPCompleted( HTTPRequestCompleted_t *arg, bool bFailed )
  633. {
  634. // Clear flag so we can stop yielding.
  635. m_bHTTPCompleted = true;
  636. ISteamHTTP *pHTTP = GetISteamHTTP();
  637. Assert( pHTTP );
  638. if ( !pHTTP )
  639. return;
  640. if ( arg->m_eStatusCode != k_EHTTPStatusCode200OK )
  641. {
  642. Warning( "Failed to update web resource: HTTP status %d fetching %s\n", arg->m_eStatusCode, m_strURL.Get() );
  643. }
  644. else
  645. {
  646. if ( !arg->m_bRequestSuccessful )
  647. {
  648. bFailed = true;
  649. }
  650. if ( !bFailed )
  651. {
  652. uint32 unBodySize;
  653. if ( !pHTTP->GetHTTPResponseBodySize( arg->m_hRequest, &unBodySize ) )
  654. {
  655. Assert( false );
  656. bFailed = true;
  657. }
  658. else
  659. {
  660. DevMsg( 2, "Fetched %d bytes for web resource via HTTP.\n", unBodySize );
  661. CUtlBuffer bufRawData;
  662. bufRawData.SetBufferType( true, true );
  663. bufRawData.SeekPut( CUtlBuffer::SEEK_HEAD, unBodySize );
  664. bool bValid = pHTTP->GetHTTPResponseBodyData( arg->m_hRequest, (uint8*) bufRawData.Base(), bufRawData.TellPut() );
  665. if ( bValid )
  666. {
  667. KeyValues* pResourceKV = new KeyValues( "" );
  668. pResourceKV->LoadFromBuffer( "", bufRawData );
  669. ItemSystem()->GetItemSchema()->SetWebResource( m_strName, pResourceKV );
  670. }
  671. }
  672. }
  673. if ( bFailed )
  674. {
  675. Warning( "Failed to update web resource from %s\n", m_strURL.Get() );
  676. }
  677. }
  678. pHTTP->ReleaseHTTPRequest( arg->m_hRequest );
  679. }
  680. #endif