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
694 lines
22 KiB
//========= 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
|
|
|