summaryrefslogtreecommitdiff
path: root/game/shared/econ/econ_item_system.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/econ/econ_item_system.cpp')
-rw-r--r--game/shared/econ/econ_item_system.cpp694
1 files changed, 694 insertions, 0 deletions
diff --git a/game/shared/econ/econ_item_system.cpp b/game/shared/econ/econ_item_system.cpp
new file mode 100644
index 0000000..29026b9
--- /dev/null
+++ b/game/shared/econ/econ_item_system.cpp
@@ -0,0 +1,694 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "tier1/KeyValues.h"
+#include "econ_gcmessages.h"
+#include "econ_item_system.h"
+#include "econ_item_inventory.h"
+#include "game_item_schema.h"
+#include "gc_clientsystem.h"
+
+#include "utldict.h"
+#include "filesystem.h"
+#include "steam/isteamhttp.h"
+
+
+#if defined(CLIENT_DLL) || defined(GAME_DLL)
+#include "gamestringpool.h"
+#include "ihasattributes.h"
+#include "tier0/icommandline.h"
+#endif
+
+#if defined(CLIENT_DLL)
+#include "igameevents.h"
+#endif
+
+// FIXME FIXME FIXME
+#if defined(TF_DLL) || defined(TF_CLIENT_DLL)
+ #include "tf_item_system.h"
+#endif // defined(TF_DLL) || defined(TF_CLIENT_DLL)
+
+#if defined (DOTA_CLIENT_DLL) || defined (DOTA_DLL)
+ #include "econ/dota_item_system.h"
+#endif // defined (DOTA_CLIENT_DLL) || defined (DOTA_DLL)
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#if ( defined( GAME_DLL ) || defined( CLIENT_DLL ) ) && ( defined( _DEBUG ) || defined( STAGING_ONLY ) )
+ConVar item_debug( "item_debug", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
+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." );
+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." );
+#endif
+
+static ConVar item_quality_chance_unique( "item_quality_chance_unique", "0.1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Percentage chance that a random item is unique." );
+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." );
+static ConVar item_quality_chance_common( "item_quality_chance_common", "1.0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Percentage chance that a random item is common." );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Get at the global item system
+//-----------------------------------------------------------------------------
+CEconItemSystem *ItemSystem( void )
+{
+ static GameItemSystem_t *pSystem = NULL;
+ if ( !pSystem )
+ {
+ pSystem = new GameItemSystem_t();
+ }
+
+ return pSystem;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Global schema access, declared in game_item_schema.h
+//-----------------------------------------------------------------------------
+GameItemSchema_t *GetItemSchema()
+{
+ return ItemSystem()->GetItemSchema();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemSystem::CEconItemSystem( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEconItemSystem::~CEconItemSystem( void )
+{
+ Shutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse in our data files.
+//-----------------------------------------------------------------------------
+void CEconItemSystem::Init( void )
+{
+#if defined(USES_ECON_ITEMS)
+ ParseItemSchemaFile( "scripts/items/items_game.txt" );
+#endif
+
+#ifdef CLIENT_DLL
+ IGameEvent *event = gameeventmanager->CreateEvent( "item_schema_initialized" );
+ if ( event )
+ {
+ gameeventmanager->FireEventClientSide( event );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemSystem::Shutdown( void )
+{
+}
+
+extern ConVar mp_tournament;
+
+#ifdef GAME_DLL
+ConVar mp_tournament_whitelist( "mp_tournament_whitelist", "item_whitelist.txt", FCVAR_NONE, "Specifies the item whitelist file to use." );
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemSystem::ReloadWhitelist( void )
+{
+ // Default state of items depends on whether we're in tourney mode, and whether there's a whitelist
+ bool bDefault = true;
+ bool bFoundWhitelist = false;
+
+ KeyValues *pWhitelistKV = new KeyValues( "item_whitelist" );
+
+#ifdef GAME_DLL
+ if ( mp_tournament.GetBool() && mp_tournament_whitelist.GetString() )
+ {
+ const char *pszWhitelistFile = mp_tournament_whitelist.GetString();
+ if ( pWhitelistKV->LoadFromFile( filesystem, pszWhitelistFile ) )
+ {
+ // Allow the whitelist to override the default, so they can turn it into a blacklist if they want to
+ bDefault = pWhitelistKV->GetBool( "unlisted_items_default_to" );
+ bFoundWhitelist = true;
+ }
+ else if ( pszWhitelistFile && pszWhitelistFile[0] )
+ {
+ Msg("Item Whitelist file '%s' could not be found. All items will be allowed.\n", pszWhitelistFile );
+ }
+ }
+#endif
+
+ const CEconItemSchema::ItemDefinitionMap_t& mapItemDefs = m_itemSchema.GetItemDefinitionMap();
+ FOR_EACH_MAP_FAST( mapItemDefs, i )
+ {
+ mapItemDefs[i]->SetAllowedInMatch( bDefault );
+ }
+
+ // If we didn't find a file, we're done.
+ if ( !bFoundWhitelist )
+ return;
+
+ // Otherwise, go through the KVs and turn on the matching items.
+ Msg("Parsing item whitelist (default: %s)\n", bDefault ? "allowed" : "disallowed" );
+ pWhitelistKV = pWhitelistKV->GetFirstSubKey();
+ while ( pWhitelistKV )
+ {
+ bool bAllow = pWhitelistKV->GetBool();
+
+ const char *pszItemName = pWhitelistKV->GetName();
+ if ( pszItemName && pszItemName[0] && !FStrEq("unlisted_items_default_to", pszItemName) )
+ {
+ CEconItemDefinition *pItemDef = m_itemSchema.GetItemDefinitionByName( pszItemName );
+ if ( pItemDef )
+ {
+ pItemDef->SetAllowedInMatch( bAllow );
+ Msg(" -> %s '%s'\n", bAllow ? "Allowing" : "Removing", pszItemName );
+ }
+ else
+ {
+ Warning(" -> Could not find an item definition named '%s'\n", pszItemName );
+ }
+ }
+
+ pWhitelistKV = pWhitelistKV->GetNextKey();
+ }
+ Msg("Finished.\n");
+}
+
+#ifdef GAME_DLL
+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 )
+{
+ Msg("Available item definitions for whitelisting:\n");
+ const CEconItemSchema::SortedItemDefinitionMap_t& mapItemDefs = ItemSystem()->GetItemSchema()->GetSortedItemDefinitionMap();
+ FOR_EACH_MAP( mapItemDefs, i )
+ {
+ const CEconItemDefinition *pItemDef = mapItemDefs[i];
+ if ( pItemDef && pItemDef->GetQuality() != AE_NORMAL && !pItemDef->IsHidden() )
+ {
+ Msg(" '%s'\n", pItemDef->GetDefinitionName() );
+ }
+ }
+}
+#endif // GAME_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEconItemSystem::ResetAttribStringCache( void )
+{
+ const CUtlMap<int, CEconItemAttributeDefinition, int> &mapDefs = m_itemSchema.GetAttributeDefinitionMap();
+ FOR_EACH_MAP_FAST( mapDefs, i )
+ {
+ mapDefs[i].ClearStringCache();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEconItemSystem::DecryptItemFiles( KeyValues *pKV, const char *pName )
+{
+ char szFullName[512];
+ Q_snprintf(szFullName,sizeof(szFullName), "%s.ctx", pName );
+
+ FileHandle_t f = filesystem->Open( szFullName, "rb", "MOD" );
+
+ if (!f)
+ {
+#if !defined(CSTRIKE_DLL)
+ Warning("No %s file found. May be unable to create items.\n", pName );
+#endif // CSTRIKE_DLL
+ return false;
+ }
+
+ int fileSize = filesystem->Size(f);
+ char *buffer = (char*)MemAllocScratch(fileSize + 1);
+
+ Assert(buffer);
+
+ filesystem->Read(buffer, fileSize, f); // read into local buffer
+ buffer[fileSize] = 0; // null terminate file as EOF
+ filesystem->Close( f ); // close file after reading
+
+ UTIL_DecodeICE( (unsigned char*)buffer, fileSize, GetEncryptionKey() );
+
+ bool retOK = pKV->LoadFromBuffer( szFullName, buffer, filesystem );
+
+ MemFreeScratch();
+
+ if ( !retOK )
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Read the specified item schema file. Init the item schema with the contents
+//-----------------------------------------------------------------------------
+void CEconItemSystem::ParseItemSchemaFile( const char *pFilename )
+{
+ CUtlVector< CUtlString > vecErrors;
+ bool bSuccess = m_itemSchema.BInit( pFilename, "MOD", &vecErrors );
+
+ if( !bSuccess )
+ {
+ FOR_EACH_VEC( vecErrors, nError )
+ {
+ // we want this to be an Error because several
+ // places rely on loading a valid item schema
+ Error( "%s\n", vecErrors[nError].String() );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate a random item matching the specified criteria
+//-----------------------------------------------------------------------------
+item_definition_index_t CEconItemSystem::GenerateRandomItem( CItemSelectionCriteria *pCriteria, entityquality_t *outEntityQuality )
+{
+ // First, pick a random item quality (use the one passed in first)
+ if ( !pCriteria->BQualitySet() )
+ {
+ pCriteria->SetQuality( GetRandomQualityForItem() );
+ }
+
+ pCriteria->SetIgnoreEnabledFlag( true );
+
+ // Determine which item templates match the criteria
+ CUtlVector<item_definition_index_t> vecMatches;
+ const CEconItemSchema::ItemDefinitionMap_t &mapDefs = m_itemSchema.GetItemDefinitionMap();
+
+HackMakeValidList:
+ FOR_EACH_MAP_FAST( mapDefs, i )
+ {
+ if ( pCriteria->BEvaluate( mapDefs[i] ) )
+ {
+ vecMatches.AddToTail( mapDefs.Key( i ) );
+ }
+ }
+
+ // No valid items?
+ int iValidItems = vecMatches.Count();
+ if ( !iValidItems )
+ {
+ // If we were searching for a unique item, drop back to a non-unique
+ if ( pCriteria->GetQuality() == AE_UNIQUE )
+ {
+ pCriteria->SetQuality( GetRandomQualityForItem( true ) );
+ goto HackMakeValidList;
+ }
+ return INVALID_ITEM_DEF_INDEX;
+ }
+
+ // Choose a random match
+ int iChosenIdx = RandomInt( 0, (iValidItems-1) );
+ item_definition_index_t iChosenItem = vecMatches[iChosenIdx];
+
+ const CEconItemDefinition *pItemDef = m_itemSchema.GetItemDefinition( iChosenItem );
+ if ( !pItemDef )
+ return INVALID_ITEM_DEF_INDEX;
+
+ // If we haven't specified an entity quality, we want to use the item's specified one
+ if ( pCriteria->GetQuality() == AE_USE_SCRIPT_VALUE )
+ {
+ int32 iScriptQuality = pItemDef->GetQuality();
+ pCriteria->SetQuality( iScriptQuality == AE_UNDEFINED ? GetRandomQualityForItem( true ) : iScriptQuality );
+ }
+
+ // If we haven't specified an item level, we want to use the item's specified one.
+ if ( !pCriteria->BItemLevelSet() )
+ {
+ pCriteria->SetItemLevel( RandomInt( pItemDef->GetMinLevel(), pItemDef->GetMaxLevel() ) );
+ }
+
+ if ( outEntityQuality )
+ {
+ *outEntityQuality = pCriteria->GetQuality();
+ }
+ return iChosenItem;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return a random quality for the item specified
+//-----------------------------------------------------------------------------
+entityquality_t CEconItemSystem::GetRandomQualityForItem( bool bPreventUnique )
+{
+ // Start on the rarest, and work backwards
+ if ( !bPreventUnique )
+ {
+ if ( RandomFloat(0,1) < item_quality_chance_unique.GetFloat() )
+ return AE_UNIQUE;
+ }
+
+ if ( RandomFloat(0,1) < item_quality_chance_rare.GetFloat() )
+ return AE_RARITY2;
+
+ if ( RandomFloat(0,1) < item_quality_chance_common.GetFloat() )
+ return AE_RARITY1;
+
+ return AE_NORMAL;
+}
+
+static ISteamHTTP *GetISteamHTTP()
+{
+ if ( steamapicontext != NULL && steamapicontext->SteamHTTP() )
+ {
+ return steamapicontext->SteamHTTP();
+ }
+ #ifndef CLIENT_DLL
+ if ( steamgameserverapicontext != NULL )
+ {
+ return steamgameserverapicontext->SteamHTTP();
+ }
+ #endif
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Common functionality for using our raw buffer data to initialize
+// the schema when safe.
+//-----------------------------------------------------------------------------
+bool IDelayedSchemaData::InitializeSchemaInternal( CEconItemSchema *pItemSchema, CUtlBuffer& bufRawData, bool bInitAsBinary, uint32 nExpectedVersion )
+{
+ Msg( "Applying new item schema, version %08X\n", nExpectedVersion );
+
+ CUtlVector<CUtlString> vecErrors;
+ bool bSuccess = bInitAsBinary
+ ? pItemSchema->BInitBinaryBuffer( bufRawData, &vecErrors )
+ : pItemSchema->BInitTextBuffer( bufRawData, &vecErrors );
+ if( bSuccess )
+ {
+ // Sanity-check that we received the version that they sent us
+ uint32 nOurVersion = pItemSchema->GetVersion();
+ if ( nExpectedVersion != 0 && nOurVersion != nExpectedVersion )
+ {
+ Warning( "**WARNING** Item schema mismatch after update!\n" );
+ Warning( "GC told us to expect %08X, we got %08X\n", nExpectedVersion, nOurVersion );
+ }
+ }
+ else
+ {
+ Warning( "**WARNING** Failed to apply item schema!\n" );
+ FOR_EACH_VEC( vecErrors, nError )
+ {
+ Warning( "%s\n", vecErrors[nError].Get() );
+ }
+ }
+ return bSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The GC sent us a single block of binary data.
+//-----------------------------------------------------------------------------
+class DelayedSchemaData_GCDirectData : public IDelayedSchemaData
+{
+public:
+ DelayedSchemaData_GCDirectData( const std::string& strBuffer )
+ : m_bufRawData( strBuffer.data(), strBuffer.size(), CUtlBuffer::READ_ONLY )
+ {
+ //
+ }
+
+ virtual bool InitializeSchema( CEconItemSchema *pItemSchema )
+ {
+ return InitializeSchemaInternal( pItemSchema, m_bufRawData, true, 0 );
+ }
+
+private:
+ CUtlBuffer m_bufRawData;
+};
+
+extern bool CheckValveSignature( const void *data, uint32 nDataSize, const void *signature, uint32 nSignatureSize );
+
+//-----------------------------------------------------------------------------
+// Purpose: We received a text file from an HTML request.
+//-----------------------------------------------------------------------------
+class DelayedSchemaData_HTTPResponseData : public IDelayedSchemaData
+{
+public:
+ DelayedSchemaData_HTTPResponseData( ISteamHTTP *pHTTP, HTTPRequestHandle handleHTTPRequest, uint32 unBodySize, uint32 nExpectedVersion, const std::string &sSignature )
+ : m_nExpectedVersion( nExpectedVersion )
+ {
+ Assert( pHTTP );
+
+ m_bufRawData.SetBufferType( true, true );
+ m_bufRawData.SeekPut( CUtlBuffer::SEEK_HEAD, unBodySize );
+
+ m_bValid = pHTTP->GetHTTPResponseBodyData( handleHTTPRequest, (uint8*)m_bufRawData.Base(), m_bufRawData.TellPut() );
+ if ( m_bValid )
+ m_bValid = CheckValveSignature( m_bufRawData.Base(), m_bufRawData.TellPut(), sSignature.c_str(), sSignature.length() );
+ }
+
+ virtual bool InitializeSchema( CEconItemSchema *pItemSchema )
+ {
+ if ( !m_bValid )
+ return false;
+
+ return InitializeSchemaInternal( pItemSchema, m_bufRawData, false, m_nExpectedVersion );
+ }
+
+private:
+ bool m_bValid;
+ CUtlBuffer m_bufRawData;
+ uint32 m_nExpectedVersion;
+};
+
+#define GC_ITEM_SCHEMA_UPDATE_APPLIED "Applied updated item schema from GC. %d bytes, version %08X.\n"
+#define GC_ITEM_SCHEMA_UPDATE_QUEUED "Received %d bytes item schema version %08X direct data; update is queued.\n"
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the item schema from the GC
+//-----------------------------------------------------------------------------
+class CGCUpdateItemSchema : public GCSDK::CGCClientJob
+{
+public:
+ CGCUpdateItemSchema( GCSDK::CGCClient *pClient ) : GCSDK::CGCClientJob( pClient ) {
+ m_szUrl[0] = '\0';
+ m_nExpectedVersion = 0;
+ bHTTPCompleted = false;
+ }
+
+ char m_szUrl[512];
+ uint32 m_nExpectedVersion;
+ bool bHTTPCompleted;
+ CCallResult< CGCUpdateItemSchema, HTTPRequestCompleted_t > callback;
+ std::string m_sSignature;
+
+ virtual bool BYieldingRunGCJob( GCSDK::IMsgNetPacket *pNetPacket )
+ {
+ GCSDK::CProtoBufMsg< CMsgUpdateItemSchema > msg( pNetPacket );
+
+#if ( defined( GAME_DLL ) || defined( CLIENT_DLL ) ) && ( defined( _DEBUG ) || defined( STAGING_ONLY ) )
+ const bool bUseGCCopy = items_game_use_gc_copy.GetBool();
+#else
+ const bool bUseGCCopy = true;
+#endif
+
+ if ( bUseGCCopy == false && k_EUniversePublic != GetUniverse() )
+ {
+ Msg( "Loading item schema from local file.\n" );
+ KeyValuesAD pItemsGameKV( "ItemsGameFile" );
+ if ( pItemsGameKV->LoadFromFile( g_pFullFileSystem, "scripts/items/items_game.txt", "GAME" ) )
+ {
+ CUtlBuffer buffer;
+ pItemsGameKV->WriteAsBinary( buffer );
+
+ CUtlVector< CUtlString > vecErrors;
+ bool bSuccess = ItemSystem()->GetItemSchema()->BInitBinaryBuffer( buffer, &vecErrors );
+ if( !bSuccess )
+ {
+ FOR_EACH_VEC( vecErrors, nError )
+ {
+ Warning( "%s\n", vecErrors[nError].Get() );
+ }
+ }
+ }
+
+ return true;
+ }
+
+ // Check if we're already up-to-date
+ m_nExpectedVersion = msg.Body().item_schema_version();
+ uint32 nCurrentSchemaVersion = ItemSystem()->GetItemSchema()->GetVersion();
+ if ( m_nExpectedVersion != 0 && m_nExpectedVersion == nCurrentSchemaVersion )
+ {
+ Msg( "Current item schema is up-to-date with version %08X.\n", nCurrentSchemaVersion );
+ return true;
+ }
+
+ m_sSignature = msg.Body().signature();
+
+ // !TEST!
+ //const char *szURL = "http://cdn.beta.steampowered.com/apps/440/scripts/items/items_game.b8b7a85b4dd98b139957004b86ec0bc070a59d18.txt";
+ if ( msg.Body().has_items_game() )
+ {
+ bool bDidInit = ItemSystem()->GetItemSchema()->MaybeInitFromBuffer( new DelayedSchemaData_GCDirectData( msg.Body().items_game() ) );
+ Msg( bDidInit ? GC_ITEM_SCHEMA_UPDATE_APPLIED : GC_ITEM_SCHEMA_UPDATE_QUEUED, (int)msg.Body().items_game().size(), m_nExpectedVersion );
+ }
+ else
+ {
+ // Remember URL
+ const char *szURL = msg.Body().items_game_url().c_str();
+ if ( !szURL || !szURL[0] )
+ {
+ Warning( "GC sent malformed CGCUpdateItemSchema message: No schema data, no URL\n" );
+ }
+ else
+ {
+ Q_strncpy( m_szUrl, szURL, sizeof( m_szUrl ) );
+ //Msg( "Fetching %s to update item schema\n", m_szUrl );
+
+ // Send an HTTP request for the file
+ ISteamHTTP *pHTTP = GetISteamHTTP();
+ if ( !pHTTP )
+ {
+ //Warning( "Can't get ISteamHTTP to update item schema\n");
+ return true;
+ }
+ HTTPRequestHandle hReq = pHTTP->CreateHTTPRequest( k_EHTTPMethodGET, m_szUrl );
+ pHTTP->SetHTTPRequestNetworkActivityTimeout( hReq, 10 );
+ SteamAPICall_t hCall;
+ if ( !pHTTP->SendHTTPRequest( hReq, &hCall ) )
+ {
+ Warning( "Failed to update item schema: couldn't fetch %s\n", m_szUrl );
+ return true;
+ }
+
+ //
+ // *Wait* for completion.
+ //
+ // This is important. The GC needs to be able to safely assume that
+ // we will not process the next message until we have finished
+ // dealing with this one.
+ //
+ bHTTPCompleted = false;
+ #ifndef CLIENT_DLL
+ if ( steamgameserverapicontext != NULL && pHTTP == steamgameserverapicontext->SteamHTTP() )
+ {
+ callback.SetGameserverFlag();
+ }
+ #endif
+ callback.Set( hCall, this, &CGCUpdateItemSchema::OnHTTPCompleted );
+
+ // Wait for it to finish.
+ while ( !bHTTPCompleted )
+ {
+ BYieldingWaitOneFrame();
+ }
+ }
+ }
+
+ return true;
+ }
+
+ void OnHTTPCompleted( HTTPRequestCompleted_t *arg, bool bFailed )
+ {
+ // Clear flag, no matter what else, so we can stop yielding
+ bHTTPCompleted = true;
+
+ ISteamHTTP *pHTTP = GetISteamHTTP();
+ Assert( pHTTP );
+ if ( !pHTTP ) return;
+
+ if ( arg->m_eStatusCode != k_EHTTPStatusCode200OK )
+ {
+ Warning( "Failed to update item schema: HTTP status %d fetching %s\n", arg->m_eStatusCode, m_szUrl );
+ }
+ else
+ {
+ if ( !arg->m_bRequestSuccessful )
+ {
+ bFailed = true;
+ }
+ if ( !bFailed )
+ {
+ uint32 unBodySize;
+ if ( !pHTTP->GetHTTPResponseBodySize( arg->m_hRequest, &unBodySize ) )
+ {
+ Assert( false );
+ bFailed = true;
+ }
+ else
+ {
+ bool bDidInit = ItemSystem()->GetItemSchema()->MaybeInitFromBuffer( new DelayedSchemaData_HTTPResponseData( pHTTP, arg->m_hRequest, unBodySize, m_nExpectedVersion, m_sSignature ) );
+ Msg( bDidInit ? GC_ITEM_SCHEMA_UPDATE_APPLIED : GC_ITEM_SCHEMA_UPDATE_QUEUED, unBodySize, m_nExpectedVersion );
+ }
+ }
+
+ if ( bFailed )
+ {
+ Warning( "Failed to update item schema from %s\n", m_szUrl );
+ }
+ }
+
+ pHTTP->ReleaseHTTPRequest( arg->m_hRequest );
+ }
+};
+GC_REG_JOB( GCSDK::CGCClient, CGCUpdateItemSchema, "CGCUpdateItemSchema", k_EMsgGCUpdateItemSchema, GCSDK::k_EServerTypeGCClient );
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Update the item schema from the GC
+//-----------------------------------------------------------------------------
+CON_COMMAND_F( econ_show_items_with_tag, "Lists the item definitions that have a specified tag.", FCVAR_CLIENTDLL )
+{
+ if ( args.ArgC() != 2 )
+ return;
+
+ econ_tag_handle_t tagHandle = GetItemSchema()->GetHandleForTag( args.Arg( 1 ) );
+ FOR_EACH_MAP( GetItemSchema()->GetSortedItemDefinitionMap(), i )
+ {
+ const CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionMap()[i];
+
+ if ( pItemDef->HasEconTag( tagHandle ) )
+ {
+ Msg(" '%s'\n", pItemDef->GetDefinitionName() );
+ }
+ }
+}
+#endif // CLIENT_DLL
+
+#ifdef STAGING_ONLY
+//-----------------------------------------------------------------------------
+// Purpose: Update the item schema from the GC
+//-----------------------------------------------------------------------------
+#ifdef CLIENT_DLL
+CON_COMMAND_F( cl_reload_local_item_schema, "Reloads the local item schema copy.", FCVAR_CLIENTDLL )
+#else
+CON_COMMAND_F( sv_reload_local_item_schema, "Reloads the local item schema copy.", FCVAR_GAMEDLL )
+#endif
+{
+#ifdef CLIENT_DLL
+ engine->ClientCmd_Unrestricted( "cmd sv_reload_local_item_schema" );
+#endif
+
+ Msg( "Loading item schema from local file.\n" );
+ KeyValuesAD pItemsGameKV( "ItemsGameFile" );
+ if ( pItemsGameKV->LoadFromFile( g_pFullFileSystem, "scripts/items/items_game.txt", "GAME" ) )
+ {
+ CUtlBuffer buffer;
+ pItemsGameKV->WriteAsBinary( buffer );
+
+ CUtlVector< CUtlString > vecErrors;
+ bool bSuccess = ItemSystem()->GetItemSchema()->BInitBinaryBuffer( buffer, &vecErrors );
+ if( !bSuccess )
+ {
+ FOR_EACH_VEC( vecErrors, nError )
+ {
+ Warning( "%s\n", vecErrors[nError].Get() );
+ }
+ }
+ }
+}
+#endif