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.

694 lines
22 KiB

  1. //========= Copyright Valve Corporation, 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 "gc_clientsystem.h"
  13. #include "utldict.h"
  14. #include "filesystem.h"
  15. #include "steam/isteamhttp.h"
  16. #if defined(CLIENT_DLL) || defined(GAME_DLL)
  17. #include "gamestringpool.h"
  18. #include "ihasattributes.h"
  19. #include "tier0/icommandline.h"
  20. #endif
  21. #if defined(CLIENT_DLL)
  22. #include "igameevents.h"
  23. #endif
  24. // FIXME FIXME FIXME
  25. #if defined(TF_DLL) || defined(TF_CLIENT_DLL)
  26. #include "tf_item_system.h"
  27. #endif // defined(TF_DLL) || defined(TF_CLIENT_DLL)
  28. #if defined (DOTA_CLIENT_DLL) || defined (DOTA_DLL)
  29. #include "econ/dota_item_system.h"
  30. #endif // defined (DOTA_CLIENT_DLL) || defined (DOTA_DLL)
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. #if ( defined( GAME_DLL ) || defined( CLIENT_DLL ) ) && ( defined( _DEBUG ) || defined( STAGING_ONLY ) )
  34. ConVar item_debug( "item_debug", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  35. ConVar items_game_use_gc_copy( "items_game_use_gc_copy", "1", FCVAR_CHEAT | FCVAR_REPLICATED | FCVAR_ARCHIVE, "If set, items_game.txt will be stomped by the GC." );
  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. #endif
  38. static ConVar item_quality_chance_unique( "item_quality_chance_unique", "0.1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Percentage chance that a random item is unique." );
  39. 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." );
  40. static ConVar item_quality_chance_common( "item_quality_chance_common", "1.0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Percentage chance that a random item is common." );
  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. #if defined(USES_ECON_ITEMS)
  79. ParseItemSchemaFile( "scripts/items/items_game.txt" );
  80. #endif
  81. #ifdef CLIENT_DLL
  82. IGameEvent *event = gameeventmanager->CreateEvent( "item_schema_initialized" );
  83. if ( event )
  84. {
  85. gameeventmanager->FireEventClientSide( event );
  86. }
  87. #endif
  88. }
  89. //-----------------------------------------------------------------------------
  90. // Purpose:
  91. //-----------------------------------------------------------------------------
  92. void CEconItemSystem::Shutdown( void )
  93. {
  94. }
  95. extern ConVar mp_tournament;
  96. #ifdef GAME_DLL
  97. ConVar mp_tournament_whitelist( "mp_tournament_whitelist", "item_whitelist.txt", FCVAR_NONE, "Specifies the item whitelist file to use." );
  98. #endif
  99. //-----------------------------------------------------------------------------
  100. // Purpose:
  101. //-----------------------------------------------------------------------------
  102. void CEconItemSystem::ReloadWhitelist( void )
  103. {
  104. // Default state of items depends on whether we're in tourney mode, and whether there's a whitelist
  105. bool bDefault = true;
  106. bool bFoundWhitelist = false;
  107. KeyValues *pWhitelistKV = new KeyValues( "item_whitelist" );
  108. #ifdef GAME_DLL
  109. if ( mp_tournament.GetBool() && mp_tournament_whitelist.GetString() )
  110. {
  111. const char *pszWhitelistFile = mp_tournament_whitelist.GetString();
  112. if ( pWhitelistKV->LoadFromFile( filesystem, pszWhitelistFile ) )
  113. {
  114. // Allow the whitelist to override the default, so they can turn it into a blacklist if they want to
  115. bDefault = pWhitelistKV->GetBool( "unlisted_items_default_to" );
  116. bFoundWhitelist = true;
  117. }
  118. else if ( pszWhitelistFile && pszWhitelistFile[0] )
  119. {
  120. Msg("Item Whitelist file '%s' could not be found. All items will be allowed.\n", pszWhitelistFile );
  121. }
  122. }
  123. #endif
  124. const CEconItemSchema::ItemDefinitionMap_t& mapItemDefs = m_itemSchema.GetItemDefinitionMap();
  125. FOR_EACH_MAP_FAST( mapItemDefs, i )
  126. {
  127. mapItemDefs[i]->SetAllowedInMatch( bDefault );
  128. }
  129. // If we didn't find a file, we're done.
  130. if ( !bFoundWhitelist )
  131. return;
  132. // Otherwise, go through the KVs and turn on the matching items.
  133. Msg("Parsing item whitelist (default: %s)\n", bDefault ? "allowed" : "disallowed" );
  134. pWhitelistKV = pWhitelistKV->GetFirstSubKey();
  135. while ( pWhitelistKV )
  136. {
  137. bool bAllow = pWhitelistKV->GetBool();
  138. const char *pszItemName = pWhitelistKV->GetName();
  139. if ( pszItemName && pszItemName[0] && !FStrEq("unlisted_items_default_to", pszItemName) )
  140. {
  141. CEconItemDefinition *pItemDef = m_itemSchema.GetItemDefinitionByName( pszItemName );
  142. if ( pItemDef )
  143. {
  144. pItemDef->SetAllowedInMatch( bAllow );
  145. Msg(" -> %s '%s'\n", bAllow ? "Allowing" : "Removing", pszItemName );
  146. }
  147. else
  148. {
  149. Warning(" -> Could not find an item definition named '%s'\n", pszItemName );
  150. }
  151. }
  152. pWhitelistKV = pWhitelistKV->GetNextKey();
  153. }
  154. Msg("Finished.\n");
  155. }
  156. #ifdef GAME_DLL
  157. CON_COMMAND_F( item_show_whitelistable_definitions, "Lists the item definitions that can be whitelisted in the item_whitelist.txt file in tournament mode.", FCVAR_CHEAT )
  158. {
  159. Msg("Available item definitions for whitelisting:\n");
  160. const CEconItemSchema::SortedItemDefinitionMap_t& mapItemDefs = ItemSystem()->GetItemSchema()->GetSortedItemDefinitionMap();
  161. FOR_EACH_MAP( mapItemDefs, i )
  162. {
  163. const CEconItemDefinition *pItemDef = mapItemDefs[i];
  164. if ( pItemDef && pItemDef->GetQuality() != AE_NORMAL && !pItemDef->IsHidden() )
  165. {
  166. Msg(" '%s'\n", pItemDef->GetDefinitionName() );
  167. }
  168. }
  169. }
  170. #endif // GAME_DLL
  171. //-----------------------------------------------------------------------------
  172. // Purpose:
  173. //-----------------------------------------------------------------------------
  174. void CEconItemSystem::ResetAttribStringCache( void )
  175. {
  176. const CUtlMap<int, CEconItemAttributeDefinition, int> &mapDefs = m_itemSchema.GetAttributeDefinitionMap();
  177. FOR_EACH_MAP_FAST( mapDefs, i )
  178. {
  179. mapDefs[i].ClearStringCache();
  180. }
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Purpose:
  184. //-----------------------------------------------------------------------------
  185. bool CEconItemSystem::DecryptItemFiles( KeyValues *pKV, const char *pName )
  186. {
  187. char szFullName[512];
  188. Q_snprintf(szFullName,sizeof(szFullName), "%s.ctx", pName );
  189. FileHandle_t f = filesystem->Open( szFullName, "rb", "MOD" );
  190. if (!f)
  191. {
  192. #if !defined(CSTRIKE_DLL)
  193. Warning("No %s file found. May be unable to create items.\n", pName );
  194. #endif // CSTRIKE_DLL
  195. return false;
  196. }
  197. int fileSize = filesystem->Size(f);
  198. char *buffer = (char*)MemAllocScratch(fileSize + 1);
  199. Assert(buffer);
  200. filesystem->Read(buffer, fileSize, f); // read into local buffer
  201. buffer[fileSize] = 0; // null terminate file as EOF
  202. filesystem->Close( f ); // close file after reading
  203. UTIL_DecodeICE( (unsigned char*)buffer, fileSize, GetEncryptionKey() );
  204. bool retOK = pKV->LoadFromBuffer( szFullName, buffer, filesystem );
  205. MemFreeScratch();
  206. if ( !retOK )
  207. return false;
  208. return true;
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Purpose: Read the specified item schema file. Init the item schema with the contents
  212. //-----------------------------------------------------------------------------
  213. void CEconItemSystem::ParseItemSchemaFile( const char *pFilename )
  214. {
  215. CUtlVector< CUtlString > vecErrors;
  216. bool bSuccess = m_itemSchema.BInit( pFilename, "MOD", &vecErrors );
  217. if( !bSuccess )
  218. {
  219. FOR_EACH_VEC( vecErrors, nError )
  220. {
  221. // we want this to be an Error because several
  222. // places rely on loading a valid item schema
  223. Error( "%s\n", vecErrors[nError].String() );
  224. }
  225. }
  226. }
  227. //-----------------------------------------------------------------------------
  228. // Purpose: Generate a random item matching the specified criteria
  229. //-----------------------------------------------------------------------------
  230. item_definition_index_t CEconItemSystem::GenerateRandomItem( CItemSelectionCriteria *pCriteria, entityquality_t *outEntityQuality )
  231. {
  232. // First, pick a random item quality (use the one passed in first)
  233. if ( !pCriteria->BQualitySet() )
  234. {
  235. pCriteria->SetQuality( GetRandomQualityForItem() );
  236. }
  237. pCriteria->SetIgnoreEnabledFlag( true );
  238. // Determine which item templates match the criteria
  239. CUtlVector<item_definition_index_t> vecMatches;
  240. const CEconItemSchema::ItemDefinitionMap_t &mapDefs = m_itemSchema.GetItemDefinitionMap();
  241. HackMakeValidList:
  242. FOR_EACH_MAP_FAST( mapDefs, i )
  243. {
  244. if ( pCriteria->BEvaluate( mapDefs[i] ) )
  245. {
  246. vecMatches.AddToTail( mapDefs.Key( i ) );
  247. }
  248. }
  249. // No valid items?
  250. int iValidItems = vecMatches.Count();
  251. if ( !iValidItems )
  252. {
  253. // If we were searching for a unique item, drop back to a non-unique
  254. if ( pCriteria->GetQuality() == AE_UNIQUE )
  255. {
  256. pCriteria->SetQuality( GetRandomQualityForItem( true ) );
  257. goto HackMakeValidList;
  258. }
  259. return INVALID_ITEM_DEF_INDEX;
  260. }
  261. // Choose a random match
  262. int iChosenIdx = RandomInt( 0, (iValidItems-1) );
  263. item_definition_index_t iChosenItem = vecMatches[iChosenIdx];
  264. const CEconItemDefinition *pItemDef = m_itemSchema.GetItemDefinition( iChosenItem );
  265. if ( !pItemDef )
  266. return INVALID_ITEM_DEF_INDEX;
  267. // If we haven't specified an entity quality, we want to use the item's specified one
  268. if ( pCriteria->GetQuality() == AE_USE_SCRIPT_VALUE )
  269. {
  270. int32 iScriptQuality = pItemDef->GetQuality();
  271. pCriteria->SetQuality( iScriptQuality == AE_UNDEFINED ? GetRandomQualityForItem( true ) : iScriptQuality );
  272. }
  273. // If we haven't specified an item level, we want to use the item's specified one.
  274. if ( !pCriteria->BItemLevelSet() )
  275. {
  276. pCriteria->SetItemLevel( RandomInt( pItemDef->GetMinLevel(), pItemDef->GetMaxLevel() ) );
  277. }
  278. if ( outEntityQuality )
  279. {
  280. *outEntityQuality = pCriteria->GetQuality();
  281. }
  282. return iChosenItem;
  283. }
  284. //-----------------------------------------------------------------------------
  285. // Purpose: Return a random quality for the item specified
  286. //-----------------------------------------------------------------------------
  287. entityquality_t CEconItemSystem::GetRandomQualityForItem( bool bPreventUnique )
  288. {
  289. // Start on the rarest, and work backwards
  290. if ( !bPreventUnique )
  291. {
  292. if ( RandomFloat(0,1) < item_quality_chance_unique.GetFloat() )
  293. return AE_UNIQUE;
  294. }
  295. if ( RandomFloat(0,1) < item_quality_chance_rare.GetFloat() )
  296. return AE_RARITY2;
  297. if ( RandomFloat(0,1) < item_quality_chance_common.GetFloat() )
  298. return AE_RARITY1;
  299. return AE_NORMAL;
  300. }
  301. static ISteamHTTP *GetISteamHTTP()
  302. {
  303. if ( steamapicontext != NULL && steamapicontext->SteamHTTP() )
  304. {
  305. return steamapicontext->SteamHTTP();
  306. }
  307. #ifndef CLIENT_DLL
  308. if ( steamgameserverapicontext != NULL )
  309. {
  310. return steamgameserverapicontext->SteamHTTP();
  311. }
  312. #endif
  313. return NULL;
  314. }
  315. //-----------------------------------------------------------------------------
  316. // Purpose: Common functionality for using our raw buffer data to initialize
  317. // the schema when safe.
  318. //-----------------------------------------------------------------------------
  319. bool IDelayedSchemaData::InitializeSchemaInternal( CEconItemSchema *pItemSchema, CUtlBuffer& bufRawData, bool bInitAsBinary, uint32 nExpectedVersion )
  320. {
  321. Msg( "Applying new item schema, version %08X\n", nExpectedVersion );
  322. CUtlVector<CUtlString> vecErrors;
  323. bool bSuccess = bInitAsBinary
  324. ? pItemSchema->BInitBinaryBuffer( bufRawData, &vecErrors )
  325. : pItemSchema->BInitTextBuffer( bufRawData, &vecErrors );
  326. if( bSuccess )
  327. {
  328. // Sanity-check that we received the version that they sent us
  329. uint32 nOurVersion = pItemSchema->GetVersion();
  330. if ( nExpectedVersion != 0 && nOurVersion != nExpectedVersion )
  331. {
  332. Warning( "**WARNING** Item schema mismatch after update!\n" );
  333. Warning( "GC told us to expect %08X, we got %08X\n", nExpectedVersion, nOurVersion );
  334. }
  335. }
  336. else
  337. {
  338. Warning( "**WARNING** Failed to apply item schema!\n" );
  339. FOR_EACH_VEC( vecErrors, nError )
  340. {
  341. Warning( "%s\n", vecErrors[nError].Get() );
  342. }
  343. }
  344. return bSuccess;
  345. }
  346. //-----------------------------------------------------------------------------
  347. // Purpose: The GC sent us a single block of binary data.
  348. //-----------------------------------------------------------------------------
  349. class DelayedSchemaData_GCDirectData : public IDelayedSchemaData
  350. {
  351. public:
  352. DelayedSchemaData_GCDirectData( const std::string& strBuffer )
  353. : m_bufRawData( strBuffer.data(), strBuffer.size(), CUtlBuffer::READ_ONLY )
  354. {
  355. //
  356. }
  357. virtual bool InitializeSchema( CEconItemSchema *pItemSchema )
  358. {
  359. return InitializeSchemaInternal( pItemSchema, m_bufRawData, true, 0 );
  360. }
  361. private:
  362. CUtlBuffer m_bufRawData;
  363. };
  364. extern bool CheckValveSignature( const void *data, uint32 nDataSize, const void *signature, uint32 nSignatureSize );
  365. //-----------------------------------------------------------------------------
  366. // Purpose: We received a text file from an HTML request.
  367. //-----------------------------------------------------------------------------
  368. class DelayedSchemaData_HTTPResponseData : public IDelayedSchemaData
  369. {
  370. public:
  371. DelayedSchemaData_HTTPResponseData( ISteamHTTP *pHTTP, HTTPRequestHandle handleHTTPRequest, uint32 unBodySize, uint32 nExpectedVersion, const std::string &sSignature )
  372. : m_nExpectedVersion( nExpectedVersion )
  373. {
  374. Assert( pHTTP );
  375. m_bufRawData.SetBufferType( true, true );
  376. m_bufRawData.SeekPut( CUtlBuffer::SEEK_HEAD, unBodySize );
  377. m_bValid = pHTTP->GetHTTPResponseBodyData( handleHTTPRequest, (uint8*)m_bufRawData.Base(), m_bufRawData.TellPut() );
  378. if ( m_bValid )
  379. m_bValid = CheckValveSignature( m_bufRawData.Base(), m_bufRawData.TellPut(), sSignature.c_str(), sSignature.length() );
  380. }
  381. virtual bool InitializeSchema( CEconItemSchema *pItemSchema )
  382. {
  383. if ( !m_bValid )
  384. return false;
  385. return InitializeSchemaInternal( pItemSchema, m_bufRawData, false, m_nExpectedVersion );
  386. }
  387. private:
  388. bool m_bValid;
  389. CUtlBuffer m_bufRawData;
  390. uint32 m_nExpectedVersion;
  391. };
  392. #define GC_ITEM_SCHEMA_UPDATE_APPLIED "Applied updated item schema from GC. %d bytes, version %08X.\n"
  393. #define GC_ITEM_SCHEMA_UPDATE_QUEUED "Received %d bytes item schema version %08X direct data; update is queued.\n"
  394. //-----------------------------------------------------------------------------
  395. // Purpose: Update the item schema from the GC
  396. //-----------------------------------------------------------------------------
  397. class CGCUpdateItemSchema : public GCSDK::CGCClientJob
  398. {
  399. public:
  400. CGCUpdateItemSchema( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {
  401. m_szUrl[0] = '\0';
  402. m_nExpectedVersion = 0;
  403. bHTTPCompleted = false;
  404. }
  405. char m_szUrl[512];
  406. uint32 m_nExpectedVersion;
  407. bool bHTTPCompleted;
  408. CCallResult< CGCUpdateItemSchema, HTTPRequestCompleted_t > callback;
  409. std::string m_sSignature;
  410. virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
  411. {
  412. GCSDK::CProtoBufMsg< CMsgUpdateItemSchema > msg( pNetPacket );
  413. #if ( defined( GAME_DLL ) || defined( CLIENT_DLL ) ) && ( defined( _DEBUG ) || defined( STAGING_ONLY ) )
  414. const bool bUseGCCopy = items_game_use_gc_copy.GetBool();
  415. #else
  416. const bool bUseGCCopy = true;
  417. #endif
  418. if ( bUseGCCopy == false && k_EUniversePublic != GetUniverse() )
  419. {
  420. Msg( "Loading item schema from local file.\n" );
  421. KeyValuesAD pItemsGameKV( "ItemsGameFile" );
  422. if ( pItemsGameKV->LoadFromFile( g_pFullFileSystem, "scripts/items/items_game.txt", "GAME" ) )
  423. {
  424. CUtlBuffer buffer;
  425. pItemsGameKV->WriteAsBinary( buffer );
  426. CUtlVector< CUtlString > vecErrors;
  427. bool bSuccess = ItemSystem()->GetItemSchema()->BInitBinaryBuffer( buffer, &vecErrors );
  428. if( !bSuccess )
  429. {
  430. FOR_EACH_VEC( vecErrors, nError )
  431. {
  432. Warning( "%s\n", vecErrors[nError].Get() );
  433. }
  434. }
  435. }
  436. return true;
  437. }
  438. // Check if we're already up-to-date
  439. m_nExpectedVersion = msg.Body().item_schema_version();
  440. uint32 nCurrentSchemaVersion = ItemSystem()->GetItemSchema()->GetVersion();
  441. if ( m_nExpectedVersion != 0 && m_nExpectedVersion == nCurrentSchemaVersion )
  442. {
  443. Msg( "Current item schema is up-to-date with version %08X.\n", nCurrentSchemaVersion );
  444. return true;
  445. }
  446. m_sSignature = msg.Body().signature();
  447. // !TEST!
  448. //const char *szURL = "http://cdn.beta.steampowered.com/apps/440/scripts/items/items_game.b8b7a85b4dd98b139957004b86ec0bc070a59d18.txt";
  449. if ( msg.Body().has_items_game() )
  450. {
  451. bool bDidInit = ItemSystem()->GetItemSchema()->MaybeInitFromBuffer( new DelayedSchemaData_GCDirectData( msg.Body().items_game() ) );
  452. Msg( bDidInit ? GC_ITEM_SCHEMA_UPDATE_APPLIED : GC_ITEM_SCHEMA_UPDATE_QUEUED, (int)msg.Body().items_game().size(), m_nExpectedVersion );
  453. }
  454. else
  455. {
  456. // Remember URL
  457. const char *szURL = msg.Body().items_game_url().c_str();
  458. if ( !szURL || !szURL[0] )
  459. {
  460. Warning( "GC sent malformed CGCUpdateItemSchema message: No schema data, no URL\n" );
  461. }
  462. else
  463. {
  464. Q_strncpy( m_szUrl, szURL, sizeof( m_szUrl ) );
  465. //Msg( "Fetching %s to update item schema\n", m_szUrl );
  466. // Send an HTTP request for the file
  467. ISteamHTTP *pHTTP = GetISteamHTTP();
  468. if ( !pHTTP )
  469. {
  470. //Warning( "Can't get ISteamHTTP to update item schema\n");
  471. return true;
  472. }
  473. HTTPRequestHandle hReq = pHTTP->CreateHTTPRequest( k_EHTTPMethodGET, m_szUrl );
  474. pHTTP->SetHTTPRequestNetworkActivityTimeout( hReq, 10 );
  475. SteamAPICall_t hCall;
  476. if ( !pHTTP->SendHTTPRequest( hReq, &hCall ) )
  477. {
  478. Warning( "Failed to update item schema: couldn't fetch %s\n", m_szUrl );
  479. return true;
  480. }
  481. //
  482. // *Wait* for completion.
  483. //
  484. // This is important. The GC needs to be able to safely assume that
  485. // we will not process the next message until we have finished
  486. // dealing with this one.
  487. //
  488. bHTTPCompleted = false;
  489. #ifndef CLIENT_DLL
  490. if ( steamgameserverapicontext != NULL && pHTTP == steamgameserverapicontext->SteamHTTP() )
  491. {
  492. callback.SetGameserverFlag();
  493. }
  494. #endif
  495. callback.Set( hCall, this, &CGCUpdateItemSchema::OnHTTPCompleted );
  496. // Wait for it to finish.
  497. while ( !bHTTPCompleted )
  498. {
  499. BYieldingWaitOneFrame();
  500. }
  501. }
  502. }
  503. return true;
  504. }
  505. void OnHTTPCompleted( HTTPRequestCompleted_t *arg, bool bFailed )
  506. {
  507. // Clear flag, no matter what else, so we can stop yielding
  508. bHTTPCompleted = true;
  509. ISteamHTTP *pHTTP = GetISteamHTTP();
  510. Assert( pHTTP );
  511. if ( !pHTTP ) return;
  512. if ( arg->m_eStatusCode != k_EHTTPStatusCode200OK )
  513. {
  514. Warning( "Failed to update item schema: HTTP status %d fetching %s\n", arg->m_eStatusCode, m_szUrl );
  515. }
  516. else
  517. {
  518. if ( !arg->m_bRequestSuccessful )
  519. {
  520. bFailed = true;
  521. }
  522. if ( !bFailed )
  523. {
  524. uint32 unBodySize;
  525. if ( !pHTTP->GetHTTPResponseBodySize( arg->m_hRequest, &unBodySize ) )
  526. {
  527. Assert( false );
  528. bFailed = true;
  529. }
  530. else
  531. {
  532. bool bDidInit = ItemSystem()->GetItemSchema()->MaybeInitFromBuffer( new DelayedSchemaData_HTTPResponseData( pHTTP, arg->m_hRequest, unBodySize, m_nExpectedVersion, m_sSignature ) );
  533. Msg( bDidInit ? GC_ITEM_SCHEMA_UPDATE_APPLIED : GC_ITEM_SCHEMA_UPDATE_QUEUED, unBodySize, m_nExpectedVersion );
  534. }
  535. }
  536. if ( bFailed )
  537. {
  538. Warning( "Failed to update item schema from %s\n", m_szUrl );
  539. }
  540. }
  541. pHTTP->ReleaseHTTPRequest( arg->m_hRequest );
  542. }
  543. };
  544. GC_REG_JOB( GCSDK::CGCClient, CGCUpdateItemSchema, "CGCUpdateItemSchema", k_EMsgGCUpdateItemSchema, GCSDK::k_EServerTypeGCClient );
  545. #ifdef CLIENT_DLL
  546. //-----------------------------------------------------------------------------
  547. // Purpose: Update the item schema from the GC
  548. //-----------------------------------------------------------------------------
  549. CON_COMMAND_F( econ_show_items_with_tag, "Lists the item definitions that have a specified tag.", FCVAR_CLIENTDLL )
  550. {
  551. if ( args.ArgC() != 2 )
  552. return;
  553. econ_tag_handle_t tagHandle = GetItemSchema()->GetHandleForTag( args.Arg( 1 ) );
  554. FOR_EACH_MAP( GetItemSchema()->GetSortedItemDefinitionMap(), i )
  555. {
  556. const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionMap()[i];
  557. if ( pItemDef->HasEconTag( tagHandle ) )
  558. {
  559. Msg(" '%s'\n", pItemDef->GetDefinitionName() );
  560. }
  561. }
  562. }
  563. #endif // CLIENT_DLL
  564. #ifdef STAGING_ONLY
  565. //-----------------------------------------------------------------------------
  566. // Purpose: Update the item schema from the GC
  567. //-----------------------------------------------------------------------------
  568. #ifdef CLIENT_DLL
  569. CON_COMMAND_F( cl_reload_local_item_schema, "Reloads the local item schema copy.", FCVAR_CLIENTDLL )
  570. #else
  571. CON_COMMAND_F( sv_reload_local_item_schema, "Reloads the local item schema copy.", FCVAR_GAMEDLL )
  572. #endif
  573. {
  574. #ifdef CLIENT_DLL
  575. engine->ClientCmd_Unrestricted( "cmd sv_reload_local_item_schema" );
  576. #endif
  577. Msg( "Loading item schema from local file.\n" );
  578. KeyValuesAD pItemsGameKV( "ItemsGameFile" );
  579. if ( pItemsGameKV->LoadFromFile( g_pFullFileSystem, "scripts/items/items_game.txt", "GAME" ) )
  580. {
  581. CUtlBuffer buffer;
  582. pItemsGameKV->WriteAsBinary( buffer );
  583. CUtlVector< CUtlString > vecErrors;
  584. bool bSuccess = ItemSystem()->GetItemSchema()->BInitBinaryBuffer( buffer, &vecErrors );
  585. if( !bSuccess )
  586. {
  587. FOR_EACH_VEC( vecErrors, nError )
  588. {
  589. Warning( "%s\n", vecErrors[nError].Get() );
  590. }
  591. }
  592. }
  593. }
  594. #endif