//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "server_pch.h" #include "sv_precache.h" #include "host.h" #include "tier0/icommandline.h" #include "MapReslistGenerator.h" #include "DownloadListGenerator.h" #include "soundchars.h" #ifndef SWDS #include "vgui_baseui_interface.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" static ConVar sv_forcepreload( "sv_forcepreload", "0", FCVAR_ARCHIVE, "Force server side preloading."); //----------------------------------------------------------------------------- // Purpose: // Input : *name - // Output : int SV_ModelIndex //----------------------------------------------------------------------------- int SV_ModelIndex (const char *name) { return sv.LookupModelIndex( name ); } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // preload - // Output : int //----------------------------------------------------------------------------- int SV_FindOrAddModel(const char *name, bool preload ) { // Look for a match or an empty slot... int flags = RES_FATALIFMISSING; if ( preload ) { flags |= RES_PRELOAD; } return sv.PrecacheModel( name, flags ); } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // Output : int //----------------------------------------------------------------------------- int SV_SoundIndex(const char *name) { return sv.LookupSoundIndex( name ); } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // preload - // Output : int //----------------------------------------------------------------------------- int SV_FindOrAddSound(const char *name, bool preload ) { // Look for a match or an empty slot... int flags = RES_FATALIFMISSING; if ( preload ) { flags |= RES_PRELOAD; } return sv.PrecacheSound( name, flags ); } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // Output : int //----------------------------------------------------------------------------- int SV_GenericIndex(const char *name) { return sv.LookupGenericIndex( name ); } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // preload - // Output : int //----------------------------------------------------------------------------- int SV_FindOrAddGeneric(const char *name, bool preload ) { // Look for a match or an empty slot... int flags = RES_FATALIFMISSING; if ( preload ) { flags |= RES_PRELOAD; } return sv.PrecacheGeneric( name, flags ); } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // Output : int //----------------------------------------------------------------------------- int SV_DecalIndex(const char *name) { return sv.LookupDecalIndex( name ); } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // preload - // Output : int //----------------------------------------------------------------------------- int SV_FindOrAddDecal(const char *name, bool preload ) { // Look for a match or an empty slot... int flags = RES_FATALIFMISSING; if ( preload ) { flags |= RES_PRELOAD; } return sv.PrecacheDecal( name, flags ); } //----------------------------------------------------------------------------- // Purpose: // Input : *name - //----------------------------------------------------------------------------- void SV_ForceSimpleMaterial( const char *name ) { DownloadListGenerator().ForceSimpleMaterial( name ); } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // &mins - // &maxs - //----------------------------------------------------------------------------- void SV_ForceModelBounds( const char *name, const Vector &mins, const Vector &maxs ) { DownloadListGenerator().ForceModelBounds( name, mins, maxs ); } //----------------------------------------------------------------------------- // Purpose: // Output : TABLEID //----------------------------------------------------------------------------- INetworkStringTable *CGameServer::GetModelPrecacheTable( void ) const { return m_pModelPrecacheTable; } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // flags - // *model - // Output : int //----------------------------------------------------------------------------- int CGameServer::PrecacheModel( char const *name, int flags, model_t *model /*=NULL*/ ) { if ( !m_pModelPrecacheTable ) return -1; int idx = m_pModelPrecacheTable->AddString( true, name ); if ( idx == INVALID_STRING_INDEX ) { return -1; } CPrecacheUserData p; // first time, set file size & flags CPrecacheUserData const *pExisting = (CPrecacheUserData const *)m_pModelPrecacheTable->GetStringUserData( idx, NULL ); if ( !pExisting ) { p.flags = flags; } else { // Just or in any new flags p = *pExisting; p.flags |= flags; } m_pModelPrecacheTable->SetStringUserData( idx, sizeof( p ), &p ); CPrecacheItem *slot = &model_precache[ idx ]; if ( model ) { slot->SetModel( model ); } bool bLoadNow; bLoadNow = ( !slot->GetModel() && ( ( flags & RES_PRELOAD ) || IsX360() ) ); if ( CommandLine()->FindParm( "-nopreload" ) || CommandLine()->FindParm( "-nopreloadmodels" )) { bLoadNow = false; } else if ( sv_forcepreload.GetInt() || CommandLine()->FindParm( "-preload" ) ) { bLoadNow = true; } if ( idx != 0 ) { if ( bLoadNow ) { slot->SetModel( modelloader->GetModelForName( name, IModelLoader::FMODELLOADER_SERVER ) ); #ifndef SWDS EngineVGui()->UpdateProgressBar(PROGRESS_PRECACHE); #endif MapReslistGenerator().OnModelPrecached(name); } else { modelloader->ReferenceModel( name, IModelLoader::FMODELLOADER_SERVER ); slot->SetModel( NULL ); } } return idx; } //----------------------------------------------------------------------------- // Purpose: // Input : index - // Output : model_t //----------------------------------------------------------------------------- model_t *CGameServer::GetModel( int index ) { if ( index <= 0 || !m_pModelPrecacheTable ) return NULL; if ( index >= m_pModelPrecacheTable->GetNumStrings() ) { return NULL; } CPrecacheItem *slot = &model_precache[ index ]; model_t *m = slot->GetModel(); if ( m ) { return m; } char const *modelname = m_pModelPrecacheTable->GetString( index ); Assert( modelname ); if ( host_showcachemiss.GetBool() ) { ConDMsg( "server model cache miss on %s\n", modelname ); } m = modelloader->GetModelForName( modelname, IModelLoader::FMODELLOADER_SERVER ); slot->SetModel( m ); return m; } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // Output : int //----------------------------------------------------------------------------- int CGameServer::LookupModelIndex( char const *name ) { if ( !m_pModelPrecacheTable ) return -1; int idx = m_pModelPrecacheTable->FindStringIndex( name ); return ( idx == INVALID_STRING_INDEX ) ? -1 : idx; } //----------------------------------------------------------------------------- // Purpose: // Output : TABLEID //----------------------------------------------------------------------------- INetworkStringTable *CGameServer::GetSoundPrecacheTable( void ) const { return m_pSoundPrecacheTable; } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // flags - // Output : int //----------------------------------------------------------------------------- int CGameServer::PrecacheSound( char const *name, int flags ) { if ( !m_pSoundPrecacheTable ) return -1; int idx = m_pSoundPrecacheTable->AddString( true, name ); if ( idx == INVALID_STRING_INDEX ) { return -1; } // mark the sound as being precached, but check first that reslist generation is enabled to save on the va() call if (MapReslistGenerator().IsEnabled() && name[0]) { MapReslistGenerator().OnResourcePrecached( va( "sound/%s", PSkipSoundChars( name ) ) ); } // first time, set file size & flags CPrecacheUserData p; CPrecacheUserData const *pExisting = (CPrecacheUserData const *)m_pSoundPrecacheTable->GetStringUserData( idx, NULL ); if ( !pExisting ) { p.flags = flags; } else { // Just or in any new flags p = *pExisting; p.flags |= flags; } m_pSoundPrecacheTable->SetStringUserData( idx, sizeof( p ), &p ); CPrecacheItem *slot = &sound_precache[ idx ]; slot->SetName( name ); return idx; } //----------------------------------------------------------------------------- // Purpose: // Input : index - // Output : char const //----------------------------------------------------------------------------- char const *CGameServer::GetSound( int index ) { if ( index <= 0 || !m_pSoundPrecacheTable ) { return NULL; } if ( index >= m_pSoundPrecacheTable->GetNumStrings() ) { return NULL; } CPrecacheItem *slot = &sound_precache[ index ]; return slot->GetName(); } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // Output : int //----------------------------------------------------------------------------- int CGameServer::LookupSoundIndex( char const *name ) { if ( !m_pSoundPrecacheTable ) return 0; int idx = m_pSoundPrecacheTable->FindStringIndex( name ); return ( idx == INVALID_STRING_INDEX ) ? 0 : idx; } //----------------------------------------------------------------------------- // Purpose: // Output : TABLEID //----------------------------------------------------------------------------- INetworkStringTable *CGameServer::GetGenericPrecacheTable( void ) const { return m_pGenericPrecacheTable; } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // flags - // Output : int //----------------------------------------------------------------------------- int CGameServer::PrecacheGeneric( char const *name, int flags ) { if ( !m_pGenericPrecacheTable ) return -1; int idx = m_pGenericPrecacheTable->AddString( true, name ); if ( idx == INVALID_STRING_INDEX ) { return -1; } MapReslistGenerator().OnResourcePrecached( name ); CPrecacheUserData p; // first time, set file size & flags CPrecacheUserData const *pExisting = (CPrecacheUserData const *)m_pGenericPrecacheTable->GetStringUserData( idx, NULL ); if ( !pExisting ) { p.flags = flags; } else { // Just or in any new flags p = *pExisting; p.flags |= flags; } m_pGenericPrecacheTable->SetStringUserData( idx, sizeof( p ), &p ); CPrecacheItem *slot = &generic_precache[ idx ]; slot->SetGeneric( name ); return idx; } //----------------------------------------------------------------------------- // Purpose: // Input : index - // Output : char const //----------------------------------------------------------------------------- char const *CGameServer::GetGeneric( int index ) { // Bogus index if ( index < 0 || !m_pGenericPrecacheTable ) return ""; if ( index >= m_pGenericPrecacheTable->GetNumStrings() ) { return ""; } CPrecacheItem *slot = &generic_precache[ index ]; return slot->GetGeneric(); } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // Output : int //----------------------------------------------------------------------------- int CGameServer::LookupGenericIndex( char const *name ) { if ( !m_pGenericPrecacheTable ) return 0; int idx = m_pGenericPrecacheTable->FindStringIndex( name ); return ( idx == INVALID_STRING_INDEX ) ? 0 : idx; } //----------------------------------------------------------------------------- // Purpose: // Output : TABLEID //----------------------------------------------------------------------------- INetworkStringTable *CGameServer::GetDecalPrecacheTable( void ) const { return m_pDecalPrecacheTable; } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // flags - // Output : int //----------------------------------------------------------------------------- int CGameServer::PrecacheDecal( char const *name, int flags ) { if ( !m_pDecalPrecacheTable ) return -1; int idx = m_pDecalPrecacheTable->AddString( true, name ); if ( idx == INVALID_STRING_INDEX ) { return -1; } MapReslistGenerator().OnResourcePrecached(name); CPrecacheUserData p; // first time, set file size & flags CPrecacheUserData const *pExisting = (CPrecacheUserData const *)m_pDecalPrecacheTable->GetStringUserData( idx, NULL ); if ( !pExisting ) { p.flags = flags; } else { // Just or in any new flags p = *pExisting; p.flags |= flags; } m_pDecalPrecacheTable->SetStringUserData( idx, sizeof( p ), &p ); CPrecacheItem *slot = &decal_precache[ idx ]; slot->SetDecal( name ); return idx; } //----------------------------------------------------------------------------- // Purpose: // Input : *name - // Output : int //----------------------------------------------------------------------------- int CGameServer::LookupDecalIndex( char const *name ) { if ( !m_pDecalPrecacheTable ) return -1; int idx = m_pDecalPrecacheTable->FindStringIndex( name ); return ( idx == INVALID_STRING_INDEX ) ? -1 : idx; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CGameServer::DumpPrecacheStats( INetworkStringTable *table ) { if ( table == NULL ) { ConMsg( "Can only dump stats when active in a level\n" ); return; } CPrecacheItem *items = NULL; if ( table == m_pModelPrecacheTable ) { items = model_precache; } else if ( table == m_pGenericPrecacheTable ) { items = generic_precache; } else if ( table == m_pSoundPrecacheTable ) { items = sound_precache; } else if ( table == m_pDecalPrecacheTable ) { items = decal_precache; } if ( !items ) return; int count = table->GetNumStrings(); int maxcount = table->GetMaxStrings(); ConMsg( "\n" ); ConMsg( "Precache table %s: %i of %i slots used\n", table->GetTableName(), count, maxcount ); for ( int i = 0; i < count; i++ ) { char const *name = table->GetString( i ); CPrecacheItem *slot = &items[ i ]; int testLength; const CPrecacheUserData *p = ( const CPrecacheUserData * )table->GetStringUserData( i, &testLength ); ErrorIfNot( testLength == sizeof( *p ), ("CGameServer::DumpPrecacheStats: invalid CPrecacheUserData length (%d)", testLength) ); if ( !name || !slot || !p ) continue; ConMsg( "%03i: %s (%s): ", i, name, GetFlagString( p->flags ) ); if ( slot->GetReferenceCount() == 0 ) { ConMsg( " never used\n" ); } else { ConMsg( " %i refs, first %.2f mru %.2f\n", slot->GetReferenceCount(), slot->GetFirstReference(), slot->GetMostRecentReference() ); } } ConMsg( "\n" ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CON_COMMAND( sv_precacheinfo, "Show precache info." ) { if ( args.ArgC() == 2 ) { char const *table = args[ 1 ]; bool dumped = true; if ( !Q_strcasecmp( table, "generic" ) ) { sv.DumpPrecacheStats( sv.GetGenericPrecacheTable() ); } else if ( !Q_strcasecmp( table, "sound" ) ) { sv.DumpPrecacheStats( sv.GetSoundPrecacheTable() ); } else if ( !Q_strcasecmp( table, "decal" ) ) { sv.DumpPrecacheStats( sv.GetDecalPrecacheTable() ); } else if ( !Q_strcasecmp( table, "model" ) ) { sv.DumpPrecacheStats( sv.GetModelPrecacheTable() ); } else { dumped = false; } if ( dumped ) { return; } } // Show all data sv.DumpPrecacheStats( sv.GetGenericPrecacheTable() ); sv.DumpPrecacheStats( sv.GetDecalPrecacheTable() ); sv.DumpPrecacheStats( sv.GetSoundPrecacheTable() ); sv.DumpPrecacheStats( sv.GetModelPrecacheTable() ); }