uid issue
This commit is contained in:
672
engine/sv_precache.cpp
Normal file
672
engine/sv_precache.cpp
Normal file
@ -0,0 +1,672 @@
|
||||
//========= Copyright <20> 1996-2005, 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 DEDICATED
|
||||
#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_ForceExactFile( const char *name )
|
||||
{
|
||||
DownloadListGenerator().ForceExactFile( name, CONSISTENCY_EXACT );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
// Input : *name -
|
||||
//-----------------------------------------------------------------------------
|
||||
void SV_ForceSimpleMaterial( const char *name )
|
||||
{
|
||||
DownloadListGenerator().ForceExactFile( name, CONSISTENCY_SIMPLE_MATERIAL );
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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 ) || IsGameConsole() ) );
|
||||
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 DEDICATED
|
||||
EngineVGui()->UpdateProgressBar(PROGRESS_DEFAULT);
|
||||
#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 );
|
||||
|
||||
// just precache particle files now
|
||||
if ( ( flags & RES_PRELOAD ) && serverGameDLL && !V_stricmp( "pcf", V_GetFileExtensionSafe( name ) ) )
|
||||
{
|
||||
serverGameDLL->PrecacheParticleSystemFile( 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() );
|
||||
}
|
Reference in New Issue
Block a user