//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//===========================================================================//
# include "client_pch.h"
# include "sound.h"
# include <inetchannel.h>
# include "checksum_engine.h"
# include "con_nprint.h"
# include "r_local.h"
# include "gl_lightmap.h"
# include "console.h"
# include "traceinit.h"
# include "cl_demo.h"
# include "cdll_engine_int.h"
# include "debugoverlay.h"
# include "filesystem_engine.h"
# include "icliententity.h"
# include "dt_recv_eng.h"
# include "vgui_baseui_interface.h"
# include "testscriptmgr.h"
# include <tier0/vprof.h>
# include <proto_oob.h>
# include "materialsystem/imaterialsystemhardwareconfig.h"
# include "gl_matsysiface.h"
# include "staticpropmgr.h"
# include "ispatialpartitioninternal.h"
# include "cbenchmark.h"
# include "vox.h"
# include "LocalNetworkBackdoor.h"
# include <tier0/icommandline.h>
# include "GameEventManager.h"
# include "host_saverestore.h"
# include "ivideomode.h"
# include "host_phonehome.h"
# include "decal.h"
# include "sv_rcon.h"
# include "cl_rcon.h"
# include "vgui_baseui_interface.h"
# include "snd_audio_source.h"
# include "iregistry.h"
# include "sys.h"
# include <vstdlib/random.h>
# include "tier0/etwprof.h"
# include "tier0/vcrmode.h"
# include "sys_dll.h"
# include "video/ivideoservices.h"
# include "cl_steamauth.h"
# include "filesystem/IQueuedLoader.h"
# include "tier2/tier2.h"
# include "host_state.h"
# include "enginethreads.h"
# include "vgui/ISystem.h"
# include "pure_server.h"
# include "SoundEmitterSystem/isoundemittersystembase.h"
# include "LoadScreenUpdate.h"
# include "tier0/systeminformation.h"
# include "steam/steam_api.h"
# include "SourceAppInfo.h"
# include "cl_steamauth.h"
# include "sv_steamauth.h"
# include "engine/ivmodelinfo.h"
# ifdef _X360
# include "xbox/xbox_launch.h"
# endif
# if defined( REPLAY_ENABLED )
# include "replay_internal.h"
# endif
# include "language.h"
# include "igame.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
extern IVEngineClient * engineClient ;
void R_UnloadSkys ( void ) ;
void CL_ResetEntityBits ( void ) ;
void EngineTool_UpdateScreenshot ( ) ;
void WriteConfig_f ( ConVar * var , const char * pOldString ) ;
// If we get more than 250 messages in the incoming buffer queue, dump any above this #
# define MAX_INCOMING_MESSAGES 250
// Size of command send buffer
# define MAX_CMD_BUFFER 4000
CGlobalVarsBase g_ClientGlobalVariables ( true ) ;
IVideoRecorder * g_pVideoRecorder = NULL ;
extern ConVar rcon_password ;
extern ConVar host_framerate ;
extern ConVar cl_clanid ;
ConVar sv_unlockedchapters ( " sv_unlockedchapters " , " 1 " , FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX , " Highest unlocked game chapter. " ) ;
static ConVar tv_nochat ( " tv_nochat " , " 0 " , FCVAR_ARCHIVE | FCVAR_USERINFO , " Don't receive chat messages from other SourceTV spectators " ) ;
static ConVar cl_LocalNetworkBackdoor ( " cl_localnetworkbackdoor " , " 1 " , 0 , " Enable network optimizations for single player games. " ) ;
static ConVar cl_ignorepackets ( " cl_ignorepackets " , " 0 " , FCVAR_CHEAT , " Force client to ignore packets (for debugging) . " ) ;
static ConVar cl_playback_screenshots ( " cl_playback_screenshots " , " 0 " , 0 , " Allows the client to playback screenshot and jpeg commands in demos. " ) ;
# if defined( STAGING_ONLY ) || defined( _DEBUG )
static ConVar cl_block_usercommand ( " cl_block_usercommand " , " 0 " , FCVAR_CHEAT , " Force client to not send usercommand (for debugging) . " ) ;
# endif // STAGING_ONLY || _DEBUG
ConVar dev_loadtime_map_start ( " dev_loadtime_map_start " , " 0.0 " , FCVAR_HIDDEN ) ;
ConVar dev_loadtime_map_elapsed ( " dev_loadtime_map_elapsed " , " 0.0 " , FCVAR_HIDDEN ) ;
MovieInfo_t cl_movieinfo ;
// FIXME: put these on hunk?
dlight_t cl_dlights [ MAX_DLIGHTS ] ;
dlight_t cl_elights [ MAX_ELIGHTS ] ;
CFastPointLeafNum g_DLightLeafAccessors [ MAX_DLIGHTS ] ;
CFastPointLeafNum g_ELightLeafAccessors [ MAX_ELIGHTS ] ;
bool cl_takesnapshot = false ;
static bool cl_takejpeg = false ;
static bool cl_takesnapshot_internal = false ;
static int cl_jpegquality = DEFAULT_JPEG_QUALITY ;
static ConVar jpeg_quality ( " jpeg_quality " , " 90 " , 0 , " jpeg screenshot quality. " ) ;
static int cl_snapshotnum = 0 ;
static char cl_snapshotname [ MAX_OSPATH ] ;
static char cl_snapshot_subdirname [ MAX_OSPATH ] ;
// Must match game .dll definition
// HACK HACK FOR E3 -- Remove this after E3
# define HIDEHUD_ALL ( 1<<2 )
void PhonemeMP3Shutdown ( void ) ;
struct ResourceLocker
{
ResourceLocker ( )
{
g_pFileSystem - > AsyncFinishAll ( ) ;
g_pFileSystem - > AsyncSuspend ( ) ;
// Need to temporarily disable queued material system, then lock it
m_QMS = Host_AllowQueuedMaterialSystem ( false ) ;
m_MatLock = g_pMaterialSystem - > Lock ( ) ;
}
~ ResourceLocker ( )
{
// Restore QMS
materials - > Unlock ( m_MatLock ) ;
Host_AllowQueuedMaterialSystem ( m_QMS ) ;
g_pFileSystem - > AsyncResume ( ) ;
// ??? What? Why?
//// Need to purge cached materials due to a sv_pure change.
//g_pMaterialSystem->UncacheAllMaterials();
}
bool m_QMS ;
MaterialLock_t m_MatLock ;
} ;
// Reloads a list of files if they are still loaded
void CL_ReloadFilesInList ( IFileList * pFilesToReload )
{
if ( ! pFilesToReload )
{
return ;
}
ResourceLocker crashPreventer ;
// Handle materials..
materials - > ReloadFilesInList ( pFilesToReload ) ;
// Handle models.. NOTE: this MUST come after materials->ReloadFilesInList because the
// models need to know which materials got flushed.
modelloader - > ReloadFilesInList ( pFilesToReload ) ;
S_ReloadFilesInList ( pFilesToReload ) ;
// Let the client at it (for particles)
if ( g_ClientDLL )
{
g_ClientDLL - > ReloadFilesInList ( pFilesToReload ) ;
}
}
void CL_HandlePureServerWhitelist ( CPureServerWhitelist * pWhitelist , /* out */ IFileList * & pFilesToReload )
{
// Free the old whitelist and get the new one.
if ( cl . m_pPureServerWhitelist )
cl . m_pPureServerWhitelist - > Release ( ) ;
cl . m_pPureServerWhitelist = pWhitelist ;
if ( cl . m_pPureServerWhitelist )
cl . m_pPureServerWhitelist - > AddRef ( ) ;
g_pFileSystem - > RegisterFileWhitelist ( pWhitelist , & pFilesToReload ) ;
// Now that we've flushed any files that shouldn't have been on disk, we should have a CRC
// set that we can check with the server.
cl . m_bCheckCRCsWithServer = true ;
}
void PrintSvPureWhitelistClassification ( const CPureServerWhitelist * pWhiteList )
{
if ( pWhiteList = = NULL )
{
Msg ( " The server is using sv_pure -1 (no file checking). \n " ) ;
return ;
}
// Load up the default whitelist
CPureServerWhitelist * pStandardList = CPureServerWhitelist : : Create ( g_pFullFileSystem ) ;
pStandardList - > Load ( 0 ) ;
if ( * pStandardList = = * pWhiteList )
{
Msg ( " The server is using sv_pure 0. (Enforcing consistency for select files only) \n " ) ;
}
else
{
pStandardList - > Load ( 2 ) ;
if ( * pStandardList = = * pWhiteList )
{
Msg ( " The server is using sv_pure 2. (Fully pure) \n " ) ;
}
else
{
Msg ( " The server is using sv_pure 1. (Custom pure server rules.) \n " ) ;
}
}
pStandardList - > Release ( ) ;
}
void CL_PrintWhitelistInfo ( )
{
PrintSvPureWhitelistClassification ( cl . m_pPureServerWhitelist ) ;
if ( cl . m_pPureServerWhitelist )
{
cl . m_pPureServerWhitelist - > PrintWhitelistContents ( ) ;
}
}
// Console command to force a whitelist on the system.
# ifdef _DEBUG
void whitelist_f ( const CCommand & args )
{
int pureLevel = 2 ;
if ( args . ArgC ( ) = = 2 )
{
pureLevel = atoi ( args [ 1 ] ) ;
}
else
{
Warning ( " Whitelist 0, 1, or 2 \n " ) ;
}
if ( pureLevel = = 0 )
{
Warning ( " whitelist 0: CL_HandlePureServerWhitelist( NULL ) \n " ) ;
IFileList * pFilesToReload = NULL ;
CL_HandlePureServerWhitelist ( NULL , pFilesToReload ) ;
CL_ReloadFilesInList ( pFilesToReload ) ;
}
else
{
CPureServerWhitelist * pWhitelist = CPureServerWhitelist : : Create ( g_pFileSystem ) ;
pWhitelist - > Load ( pureLevel = = 1 ) ;
IFileList * pFilesToReload = NULL ;
CL_HandlePureServerWhitelist ( pWhitelist , pFilesToReload ) ;
CL_ReloadFilesInList ( pFilesToReload ) ;
pWhitelist - > Release ( ) ;
}
}
ConCommand whitelist ( " whitelist " , whitelist_f ) ;
# endif
const CPrecacheUserData * CL_GetPrecacheUserData ( INetworkStringTable * table , int index )
{
int testLength ;
const CPrecacheUserData * data = ( CPrecacheUserData * ) table - > GetStringUserData ( index , & testLength ) ;
if ( data )
{
ErrorIfNot (
testLength = = sizeof ( * data ) ,
( " CL_GetPrecacheUserData(%d,%d) - length (%d) invalid. " , table - > GetTableId ( ) , index , testLength )
) ;
}
return data ;
}
//-----------------------------------------------------------------------------
// Purpose: setup the demo flag, split from CL_IsHL2Demo so CL_IsHL2Demo can be inline
//-----------------------------------------------------------------------------
static bool s_bIsHL2Demo = false ;
void CL_InitHL2DemoFlag ( )
{
# if defined(_X360)
s_bIsHL2Demo = false ;
# else
static bool initialized = false ;
if ( ! initialized )
{
if ( Steam3Client ( ) . SteamApps ( ) & & ! Q_stricmp ( COM_GetModDirectory ( ) , " hl2 " ) )
{
initialized = true ;
// if user didn't buy HL2 yet, this must be the free demo
if ( VCRGetMode ( ) ! = VCR_Playback )
{
s_bIsHL2Demo = ! Steam3Client ( ) . SteamApps ( ) - > BIsSubscribedApp ( GetAppSteamAppId ( k_App_HL2 ) ) ;
}
# if !defined( NO_VCR )
VCRGenericValue ( " e " , & s_bIsHL2Demo , sizeof ( s_bIsHL2Demo ) ) ;
# endif
}
if ( ! Q_stricmp ( COM_GetModDirectory ( ) , " hl2 " ) & & CommandLine ( ) - > CheckParm ( " -demo " ) )
{
s_bIsHL2Demo = true ;
}
}
# endif
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the user is playing the HL2 Demo (rather than the full game)
//-----------------------------------------------------------------------------
bool CL_IsHL2Demo ( )
{
CL_InitHL2DemoFlag ( ) ;
return s_bIsHL2Demo ;
}
static bool s_bIsPortalDemo = false ;
void CL_InitPortalDemoFlag ( )
{
# if defined(_X360)
s_bIsPortalDemo = false ;
# else
static bool initialized = false ;
if ( ! initialized )
{
if ( Steam3Client ( ) . SteamApps ( ) & & ! Q_stricmp ( COM_GetModDirectory ( ) , " portal " ) )
{
initialized = true ;
// if user didn't buy Portal yet, this must be the free demo
if ( VCRGetMode ( ) ! = VCR_Playback )
{
s_bIsPortalDemo = ! Steam3Client ( ) . SteamApps ( ) - > BIsSubscribedApp ( GetAppSteamAppId ( k_App_PORTAL ) ) ;
}
# if !defined( NO_VCR )
VCRGenericValue ( " e " , & s_bIsPortalDemo , sizeof ( s_bIsPortalDemo ) ) ;
# endif
}
if ( ! Q_stricmp ( COM_GetModDirectory ( ) , " portal " ) & & CommandLine ( ) - > CheckParm ( " -demo " ) )
{
s_bIsPortalDemo = true ;
}
}
# endif
}
//-----------------------------------------------------------------------------
// Purpose: Returns true if the user is playing the Portal Demo (rather than the full game)
//-----------------------------------------------------------------------------
bool CL_IsPortalDemo ( )
{
CL_InitPortalDemoFlag ( ) ;
return s_bIsPortalDemo ;
}
# ifdef _XBOX
extern void Host_WriteConfiguration ( const char * dirname , const char * filename ) ;
//-----------------------------------------------------------------------------
// Convar callback to write the user configuration
//-----------------------------------------------------------------------------
void WriteConfig_f ( ConVar * var , const char * pOldString )
{
Host_WriteConfiguration ( " xboxuser.cfg " ) ;
}
# endif
//-----------------------------------------------------------------------------
// Purpose: If the client is in the process of connecting and the cl.signon hits
// is complete, make sure the client thinks its totally connected.
//-----------------------------------------------------------------------------
void CL_CheckClientState ( void )
{
// Setup the local network backdoor (we do this each frame so it can be toggled on and off).
bool useBackdoor = cl_LocalNetworkBackdoor . GetInt ( ) & &
( cl . m_NetChannel ? cl . m_NetChannel - > IsLoopback ( ) : false ) & &
sv . IsActive ( ) & &
! demorecorder - > IsRecording ( ) & &
! demoplayer - > IsPlayingBack ( ) & &
Host_IsSinglePlayerGame ( ) ;
CL_SetupLocalNetworkBackDoor ( useBackdoor ) ;
}
//-----------------------------------------------------------------------------
// bool CL_CheckCRCs( const char *pszMap )
//-----------------------------------------------------------------------------
bool CL_CheckCRCs ( const char * pszMap )
{
CRC32_t mapCRC ; // If this is the worldmap, CRC against server's map
MD5Value_t mapMD5 ;
V_memset ( mapMD5 . bits , 0 , MD5_DIGEST_LENGTH ) ;
// Don't verify CRC if we are running a local server (i.e., we are playing single player, or we are the server in multiplay
if ( sv . IsActive ( ) ) // Single player
return true ;
if ( IsX360 ( ) )
{
return true ;
}
bool couldHash = false ;
if ( g_ClientGlobalVariables . network_protocol > PROTOCOL_VERSION_17 )
{
couldHash = MD5_MapFile ( & mapMD5 , pszMap ) ;
}
else
{
CRC32_Init ( & mapCRC ) ;
couldHash = CRC_MapFile ( & mapCRC , pszMap ) ;
}
if ( ! couldHash )
{
// Does the file exist?
FileHandle_t fp = 0 ;
int nSize = - 1 ;
nSize = COM_OpenFile ( pszMap , & fp ) ;
if ( fp )
g_pFileSystem - > Close ( fp ) ;
if ( nSize ! = - 1 )
{
COM_ExplainDisconnection ( true , " Couldn't CRC map %s, disconnecting \n " , pszMap ) ;
Host_Error ( " Bad map " ) ;
}
else
{
COM_ExplainDisconnection ( true , " Missing map %s, disconnecting \n " , pszMap ) ;
Host_Error ( " Map is missing " ) ;
}
return false ;
}
bool hashValid = false ;
if ( g_ClientGlobalVariables . network_protocol > PROTOCOL_VERSION_17 )
{
hashValid = MD5_Compare ( cl . serverMD5 , mapMD5 ) ;
}
// Hacked map
if ( ! hashValid & & ! demoplayer - > IsPlayingBack ( ) )
{
if ( IsX360 ( ) )
{
Warning ( " Disconnect: BSP CRC failed! \n " ) ;
}
COM_ExplainDisconnection ( true , " Your map [%s] differs from the server's. \n " , pszMap ) ;
Host_Error ( " Client's map differs from the server's " ) ;
return false ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nMaxClients -
//-----------------------------------------------------------------------------
void CL_ReallocateDynamicData ( int maxclients )
{
Assert ( entitylist ) ;
if ( entitylist )
{
entitylist - > SetMaxEntities ( MAX_EDICTS ) ;
}
}
/*
= = = = = = = = = = = = = = = = =
CL_ReadPackets
Updates the local time and reads / handles messages on client net connection .
= = = = = = = = = = = = = = = = =
*/
void CL_ReadPackets ( bool bFinalTick )
{
VPROF_BUDGET ( " CL_ReadPackets " , VPROF_BUDGETGROUP_OTHER_NETWORKING ) ;
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
if ( ! Host_ShouldRun ( ) )
return ;
// update client times/tick
cl . oldtickcount = cl . GetServerTickCount ( ) ;
if ( ! cl . IsPaused ( ) )
{
cl . SetClientTickCount ( cl . GetClientTickCount ( ) + 1 ) ;
// While clock correction is off, we have the old behavior of matching the client and server clocks.
if ( ! CClockDriftMgr : : IsClockCorrectionEnabled ( ) )
cl . SetServerTickCount ( cl . GetClientTickCount ( ) ) ;
g_ClientGlobalVariables . tickcount = cl . GetClientTickCount ( ) ;
g_ClientGlobalVariables . curtime = cl . GetTime ( ) ;
}
// 0 or tick_rate if simulating
g_ClientGlobalVariables . frametime = cl . GetFrameTime ( ) ;
// read packets, if any in queue
if ( demoplayer - > IsPlayingBack ( ) & & cl . m_NetChannel )
{
tmZoneFiltered ( TELEMETRY_LEVEL0 , 50 , TMZF_NONE , " ReadPacket " ) ;
// process data from demo file
cl . m_NetChannel - > ProcessPlayback ( ) ;
}
else
{
if ( ! cl_ignorepackets . GetInt ( ) )
{
tmZoneFiltered ( TELEMETRY_LEVEL0 , 50 , TMZF_NONE , " ProcessSocket " ) ;
// process data from net socket
NET_ProcessSocket ( NS_CLIENT , & cl ) ;
}
}
// check timeout, but not if running _DEBUG engine
# if !defined( _DEBUG )
// Only check on final frame because that's when the server might send us a packet in single player. This avoids
// a bug where if you sit in the game code in the debugger then you get a timeout here on resuming the engine
// because the timestep is > 1 tick because of the debugging delay but the server hasn't sent the next packet yet. ywb 9/5/03
if ( ( cl . m_NetChannel ? cl . m_NetChannel - > IsTimedOut ( ) : false ) & &
bFinalTick & &
! demoplayer - > IsPlayingBack ( ) & &
cl . IsConnected ( ) )
{
ConMsg ( " \n Server connection timed out. \n " ) ;
// Show the vgui dialog on timeout
COM_ExplainDisconnection ( false , " Lost connection to server. " ) ;
if ( IsPC ( ) )
{
EngineVGui ( ) - > ShowErrorMessage ( ) ;
}
Host_Disconnect ( true , " Lost connection " ) ;
return ;
}
# endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CL_ClearState ( void )
{
// clear out the current whitelist
IFileList * pFilesToReload = NULL ;
CL_HandlePureServerWhitelist ( NULL , pFilesToReload ) ;
CL_ReloadFilesInList ( pFilesToReload ) ;
CL_ResetEntityBits ( ) ;
R_UnloadSkys ( ) ;
// clear decal index directories
Decal_Init ( ) ;
StaticPropMgr ( ) - > LevelShutdownClient ( ) ;
// shutdown this level in the client DLL
if ( g_ClientDLL )
{
if ( host_state . worldmodel )
{
char mapname [ 256 ] ;
CL_SetupMapName ( modelloader - > GetName ( host_state . worldmodel ) , mapname , sizeof ( mapname ) ) ;
phonehome - > Message ( IPhoneHome : : PHONE_MSG_MAPEND , mapname ) ;
}
audiosourcecache - > LevelShutdown ( ) ;
g_ClientDLL - > LevelShutdown ( ) ;
}
R_LevelShutdown ( ) ;
if ( IsX360 ( ) )
{
// Reset material system temporary memory (frees up memory for map loading)
bool bOnLevelShutdown = true ;
materials - > ResetTempHWMemory ( bOnLevelShutdown ) ;
}
if ( g_pLocalNetworkBackdoor )
g_pLocalNetworkBackdoor - > ClearState ( ) ;
// clear other arrays
memset ( cl_dlights , 0 , sizeof ( cl_dlights ) ) ;
memset ( cl_elights , 0 , sizeof ( cl_elights ) ) ;
// Wipe the hunk ( unless the server is active )
Host_FreeStateAndWorld ( false ) ;
Host_FreeToLowMark ( false ) ;
PhonemeMP3Shutdown ( ) ;
// Wipe the remainder of the structure.
cl . Clear ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Used for sorting sounds
// Input : &sound1 -
// &sound2 -
// Output : static bool
//-----------------------------------------------------------------------------
static bool CL_SoundMessageLessFunc ( SoundInfo_t const & sound1 , SoundInfo_t const & sound2 )
{
return sound1 . nSequenceNumber < sound2 . nSequenceNumber ;
}
static CUtlRBTree < SoundInfo_t , int > g_SoundMessages ( 0 , 0 , CL_SoundMessageLessFunc ) ;
extern ConVar snd_show ;
//-----------------------------------------------------------------------------
// Purpose: Add sound to queue
// Input : sound -
//-----------------------------------------------------------------------------
void CL_AddSound ( const SoundInfo_t & sound )
{
g_SoundMessages . Insert ( sound ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Play sound packet
// Input : sound -
//-----------------------------------------------------------------------------
void CL_DispatchSound ( const SoundInfo_t & sound )
{
int nSoundNum = sound . nSoundNum ;
CSfxTable * pSfx ;
char name [ MAX_QPATH ] ;
name [ 0 ] = 0 ;
if ( sound . bIsSentence )
{
// make dummy sfx for sentences
const char * pSentenceName = VOX_SentenceNameFromIndex ( sound . nSoundNum ) ;
if ( ! pSentenceName )
{
pSentenceName = " " ;
}
V_snprintf ( name , sizeof ( name ) , " %c%s " , CHAR_SENTENCE , pSentenceName ) ;
pSfx = S_DummySfx ( name ) ;
}
else
{
V_strncpy ( name , cl . GetSoundName ( sound . nSoundNum ) , sizeof ( name ) ) ;
const char * pchTranslatedName = g_ClientDLL - > TranslateEffectForVisionFilter ( " sounds " , name ) ;
if ( V_strcmp ( pchTranslatedName , name ) ! = 0 )
{
V_strncpy ( name , pchTranslatedName , sizeof ( name ) ) ;
nSoundNum = cl . LookupSoundIndex ( name ) ;
}
pSfx = cl . GetSound ( nSoundNum ) ;
}
if ( snd_show . GetInt ( ) > = 2 )
{
DevMsg ( " %i (seq %i) %s : src %d : ch %d : %d dB : vol %.2f : time %.3f (%.4f delay) @%.1f %.1f %.1f \n " ,
host_framecount ,
sound . nSequenceNumber ,
name ,
sound . nEntityIndex ,
sound . nChannel ,
sound . Soundlevel ,
sound . fVolume ,
cl . GetTime ( ) ,
sound . fDelay ,
sound . vOrigin . x ,
sound . vOrigin . y ,
sound . vOrigin . z ) ;
}
StartSoundParams_t params ;
params . staticsound = ( sound . nChannel = = CHAN_STATIC ) ? true : false ;
params . soundsource = sound . nEntityIndex ;
params . entchannel = params . staticsound ? CHAN_STATIC : sound . nChannel ;
params . pSfx = pSfx ;
params . origin = sound . vOrigin ;
params . fvol = sound . fVolume ;
params . soundlevel = sound . Soundlevel ;
params . flags = sound . nFlags ;
params . pitch = sound . nPitch ;
params . specialdsp = sound . nSpecialDSP ;
params . fromserver = true ;
params . delay = sound . fDelay ;
// we always want to do this when this flag is set - even if the delay is zero we need to precisely
// schedule this sound
if ( sound . nFlags & SND_DELAY )
{
// anything adjusted less than 100ms forward was probably scheduled this frame
if ( sound . fDelay > - 0.100f )
{
float soundtime = cl . m_flLastServerTickTime + sound . fDelay ;
// this adjusts for host_thread_mode or any other cases where we're running more than one
// tick at a time, but we get network updates on the first tick
soundtime - = ( ( g_ClientGlobalVariables . simTicksThisFrame - 1 ) * host_state . interval_per_tick ) ;
// this sound was networked over from the server, use server clock
params . delay = S_ComputeDelayForSoundtime ( soundtime , CLOCK_SYNC_SERVER ) ;
#if 0
static float lastSoundTime = 0 ;
Msg ( " [%.3f] Play %s at %.3f %.1fsms delay \n " , soundtime - lastSoundTime , name , soundtime , params . delay * 1000.0f ) ;
lastSoundTime = soundtime ;
# endif
if ( params . delay < = 0 )
{
// leave a little delay to flag the channel in the low-level sound system
params . delay = 1e-6 f ;
}
}
else
{
params . delay = sound . fDelay ;
}
}
params . speakerentity = sound . nSpeakerEntity ;
// Give the client DLL a chance to run arbitrary code to affect the sound parameters before we
// play.
g_ClientDLL - > ClientAdjustStartSoundParams ( params ) ;
if ( params . staticsound )
{
S_StartSound ( params ) ;
}
else
{
// Don't actually play non-static sounds if playing a demo and skipping ahead
// but always stop sounds
if ( demoplayer - > IsSkipping ( ) & & ! ( sound . nFlags & SND_STOP ) )
{
return ;
}
S_StartSound ( params ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Called after reading network messages to play sounds encoded in the network packet
//-----------------------------------------------------------------------------
void CL_DispatchSounds ( void )
{
int i ;
// Walk list in sequence order
i = g_SoundMessages . FirstInorder ( ) ;
while ( i ! = g_SoundMessages . InvalidIndex ( ) )
{
SoundInfo_t const * msg = & g_SoundMessages [ i ] ;
Assert ( msg ) ;
if ( msg )
{
// Play the sound
CL_DispatchSound ( * msg ) ;
}
i = g_SoundMessages . NextInorder ( i ) ;
}
// Reset the queue each time we empty it!!!
g_SoundMessages . RemoveAll ( ) ;
}
//-----------------------------------------------------------------------------
// Retry last connection (e.g., after we enter a password)
//-----------------------------------------------------------------------------
void CL_Retry ( )
{
if ( ! cl . m_szRetryAddress [ 0 ] )
{
ConMsg ( " Can't retry, no previous connection \n " ) ;
return ;
}
// Check that we can add the two execution markers
bool bCanAddExecutionMarkers = Cbuf_HasRoomForExecutionMarkers ( 2 ) ;
ConMsg ( " Commencing connection retry to %s \n " , cl . m_szRetryAddress ) ;
// We need to temporarily disable this execution marker so the connect command succeeds if it was executed by the server.
// We would still need this even if we called CL_Connect directly because the connect process may execute commands which we want to succeed.
const char * pszCommand = va ( " connect %s %s \n " , cl . m_szRetryAddress , cl . m_sRetrySourceTag . String ( ) ) ;
if ( cl . m_bRestrictServerCommands & & bCanAddExecutionMarkers )
Cbuf_AddTextWithMarkers ( eCmdExecutionMarker_Disable_FCVAR_SERVER_CAN_EXECUTE , pszCommand , eCmdExecutionMarker_Enable_FCVAR_SERVER_CAN_EXECUTE ) ;
else
Cbuf_AddText ( pszCommand ) ;
}
CON_COMMAND_F ( retry , " Retry connection to last server. " , FCVAR_DONTRECORD | FCVAR_SERVER_CAN_EXECUTE | FCVAR_CLIENTCMD_CAN_EXECUTE )
{
CL_Retry ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
CL_Connect_f
User command to connect to server
= = = = = = = = = = = = = = = = = = = = =
*/
void CL_Connect ( const char * address , const char * pszSourceTag )
{
// If it's not a single player connection to "localhost", initialize networking & stop listenserver
if ( Q_strncmp ( address , " localhost " , 9 ) )
{
Host_Disconnect ( false ) ;
// allow remote
NET_SetMutiplayer ( true ) ;
// start progress bar immediately for remote connection
EngineVGui ( ) - > EnabledProgressBarForNextLoad ( ) ;
SCR_BeginLoadingPlaque ( ) ;
EngineVGui ( ) - > UpdateProgressBar ( PROGRESS_BEGINCONNECT ) ;
}
else
{
// we are connecting/reconnecting to local game
// so don't stop listenserver
cl . Disconnect ( " Connecting to local host " , false ) ;
}
// This happens as part of the load process anyway, but on slower systems it causes the server to timeout the
// connection. Use the opportunity to flush anything before starting a new connection.
UpdateMaterialSystemConfig ( ) ;
cl . Connect ( address , pszSourceTag ) ;
// Reset error conditions
gfExtendedError = false ;
}
CON_COMMAND_F ( connect , " Connect to specified server. " , FCVAR_DONTRECORD )
{
// Default command processing considers ':' a command separator,
// and we donly want spaces to count. So we'll need to re-split the arg string
CUtlVector < char * > vecArgs ;
V_SplitString ( args . ArgS ( ) , " " , vecArgs ) ;
// How many arguments?
if ( vecArgs . Count ( ) = = 1 )
{
CL_Connect ( vecArgs [ 0 ] , " " ) ;
}
else if ( vecArgs . Count ( ) = = 2 )
{
CL_Connect ( vecArgs [ 0 ] , vecArgs [ 1 ] ) ;
}
else
{
ConMsg ( " Usage: connect <server> \n " ) ;
}
vecArgs . PurgeAndDeleteElementsArray ( ) ;
}
CON_COMMAND_F ( redirect , " Redirect client to specified server. " , FCVAR_DONTRECORD | FCVAR_SERVER_CAN_EXECUTE )
{
if ( ! CBaseClientState : : ConnectMethodAllowsRedirects ( ) )
{
ConMsg ( " redirect: Current connection method does not allow silent redirects. \n " ) ;
return ;
}
// Default command processing considers ':' a command separator,
// and we donly want spaces to count. So we'll need to re-split the arg string
CUtlVector < char * > vecArgs ;
V_SplitString ( args . ArgS ( ) , " " , vecArgs ) ;
if ( vecArgs . Count ( ) = = 1 )
{
CL_Connect ( vecArgs [ 0 ] , " redirect " ) ;
}
else
{
ConMsg ( " Usage: redirect <server> \n " ) ;
}
vecArgs . PurgeAndDeleteElements ( ) ;
}
//-----------------------------------------------------------------------------
// Takes the map name, strips path and extension
//-----------------------------------------------------------------------------
void CL_SetupMapName ( const char * pName , char * pFixedName , int maxlen )
{
const char * pSlash = strrchr ( pName , ' \\ ' ) ;
const char * pSlash2 = strrchr ( pName , ' / ' ) ;
if ( pSlash2 > pSlash )
pSlash = pSlash2 ;
if ( pSlash )
+ + pSlash ;
else
pSlash = pName ;
Q_strncpy ( pFixedName , pSlash , maxlen ) ;
char * pExt = strchr ( pFixedName , ' . ' ) ;
if ( pExt )
* pExt = 0 ;
}
CPureServerWhitelist * CL_LoadWhitelist ( INetworkStringTable * pTable , const char * pName )
{
// If there is no entry for the pure server whitelist, then sv_pure is off and the client can do whatever it wants.
int iString = pTable - > FindStringIndex ( pName ) ;
if ( iString = = INVALID_STRING_INDEX )
return NULL ;
int dataLen ;
const void * pData = pTable - > GetStringUserData ( iString , & dataLen ) ;
if ( pData )
{
CUtlBuffer buf ( pData , dataLen , CUtlBuffer : : READ_ONLY ) ;
CPureServerWhitelist * pWhitelist = CPureServerWhitelist : : Create ( g_pFullFileSystem ) ;
pWhitelist - > Decode ( buf ) ;
return pWhitelist ;
}
else
{
return NULL ;
}
}
void CL_CheckForPureServerWhitelist ( /* out */ IFileList * & pFilesToReload )
{
# ifdef DISABLE_PURE_SERVER_STUFF
return ;
# endif
// Don't do sv_pure stuff in SP games or HLTV/replay
if ( cl . m_nMaxClients < = 1 | | cl . ishltv | | demoplayer - > IsPlayingBack ( )
# ifdef REPLAY_ENABLED
| | cl . isreplay
# endif // ifdef REPLAY_ENABLED
)
return ;
CPureServerWhitelist * pWhitelist = NULL ;
if ( cl . m_pServerStartupTable )
pWhitelist = CL_LoadWhitelist ( cl . m_pServerStartupTable , " PureServerWhitelist " ) ;
PrintSvPureWhitelistClassification ( pWhitelist ) ;
CL_HandlePureServerWhitelist ( pWhitelist , pFilesToReload ) ;
if ( pWhitelist )
{
pWhitelist - > Release ( ) ;
}
}
int CL_GetServerQueryPort ( )
{
// Yes, this is ugly getting this data out of a string table. Would be better to have it in our network protocol,
// but we don't have a way to change the protocol without breaking things for people.
if ( ! cl . m_pServerStartupTable )
return 0 ;
int iString = cl . m_pServerStartupTable - > FindStringIndex ( " QueryPort " ) ;
if ( iString = = INVALID_STRING_INDEX )
return 0 ;
int dataLen ;
const void * pData = cl . m_pServerStartupTable - > GetStringUserData ( iString , & dataLen ) ;
if ( pData & & dataLen = = sizeof ( int ) )
return * ( ( const int * ) pData ) ;
else
return 0 ;
}
/*
= = = = = = = = = = = = = = = = = =
CL_RegisterResources
Clean up and move to next part of sequence .
= = = = = = = = = = = = = = = = = =
*/
void CL_RegisterResources ( void )
{
// All done precaching.
host_state . SetWorldModel ( cl . GetModel ( 1 ) ) ;
if ( ! host_state . worldmodel )
{
Host_Error ( " CL_RegisterResources: host_state.worldmodel/cl.GetModel( 1 )==NULL \n " ) ;
}
// Force main window to repaint... (only does something if running shaderapi
videomode - > InvalidateWindow ( ) ;
}
void CL_FullyConnected ( void )
{
CETWScope timer ( " CL_FullyConnected " ) ;
EngineVGui ( ) - > UpdateProgressBar ( PROGRESS_FULLYCONNECTED ) ;
// This has to happen here, in phase 3, because it is in this phase
// that raycasts against the world is supported (owing to the fact
// that the world entity has been created by this point)
StaticPropMgr ( ) - > LevelInitClient ( ) ;
if ( IsX360 ( ) )
{
// Notify the loader the end of the loading context, preloads are about to be purged
g_pQueuedLoader - > EndMapLoading ( false ) ;
}
// flush client-side dynamic models that have no refcount
modelloader - > FlushDynamicModels ( ) ;
// loading completed
// can NOW safely purge unused models and their data hierarchy (materials, shaders, etc)
modelloader - > PurgeUnusedModels ( ) ;
// Purge the preload stores, oreder is critical
g_pMDLCache - > ShutdownPreloadData ( ) ;
// NOTE: purposely disabling for singleplayer, memory spike causing issues, preload's stay in
// UNDONE: discard preload for TF to save memory
// g_pFileSystem->DiscardPreloadData();
// We initialize this list before the load, but don't perform reloads until after flushes have happened to avoid
// unnecessary reloads of items that wont be used on this map.
if ( cl . m_pPendingPureFileReloads )
{
CL_ReloadFilesInList ( cl . m_pPendingPureFileReloads ) ;
cl . m_pPendingPureFileReloads - > Release ( ) ;
cl . m_pPendingPureFileReloads = NULL ;
}
// ***************************************************************
// NO MORE PRELOAD DATA AVAILABLE PAST THIS POINT!!!
// ***************************************************************
g_ClientDLL - > LevelInitPostEntity ( ) ;
// communicate to tracker that we're in a game
int ip = cl . m_NetChannel - > GetRemoteAddress ( ) . GetIPNetworkByteOrder ( ) ;
short port = cl . m_NetChannel - > GetRemoteAddress ( ) . GetPort ( ) ;
if ( ! port )
{
ip = net_local_adr . GetIPNetworkByteOrder ( ) ;
port = net_local_adr . GetPort ( ) ;
}
int iQueryPort = CL_GetServerQueryPort ( ) ;
EngineVGui ( ) - > NotifyOfServerConnect ( com_gamedir , ip , port , iQueryPort ) ;
GetTestScriptMgr ( ) - > CheckPoint ( " FinishedMapLoad " ) ;
EngineVGui ( ) - > UpdateProgressBar ( PROGRESS_READYTOPLAY ) ;
if ( ! IsX360 ( ) | | cl . m_nMaxClients = = 1 )
{
// Need this to persist for multiplayer respawns, 360 can't reload
CM_DiscardEntityString ( ) ;
}
g_pMDLCache - > EndMapLoad ( ) ;
# if defined( _MEMTEST )
Cbuf_AddText ( " mem_dump \n " ) ;
# endif
if ( developer . GetInt ( ) > 0 )
{
ConDMsg ( " Signon traffic \" %s \" : incoming %s, outgoing %s \n " ,
cl . m_NetChannel - > GetName ( ) ,
Q_pretifymem ( cl . m_NetChannel - > GetTotalData ( FLOW_INCOMING ) , 3 ) ,
Q_pretifymem ( cl . m_NetChannel - > GetTotalData ( FLOW_OUTGOING ) , 3 ) ) ;
}
if ( IsX360 ( ) )
{
// Reset material system temporary memory (once loading is complete), ready for in-map use
bool bOnLevelShutdown = false ;
materials - > ResetTempHWMemory ( bOnLevelShutdown ) ;
}
// allow normal screen updates
SCR_EndLoadingPlaque ( ) ;
EndLoadingUpdates ( ) ;
// FIXME: Please oh please move this out of this spot...
// It so does not belong here. Instead, we want some phase of the
// client DLL where it knows its read in all entities
if ( IsPC ( ) )
{
int i ;
if ( ( i = CommandLine ( ) - > FindParm ( " -buildcubemaps " ) ) ! = 0 )
{
int numIterations = 1 ;
if ( CommandLine ( ) - > ParmCount ( ) > i + 1 )
{
numIterations = atoi ( CommandLine ( ) - > GetParm ( i + 1 ) ) ;
}
if ( numIterations = = 0 )
{
numIterations = 1 ;
}
char cmd [ 1024 ] = { 0 } ;
V_snprintf ( cmd , sizeof ( cmd ) , " buildcubemaps %u \n quit \n " , numIterations ) ;
Cbuf_AddText ( cmd ) ;
}
else if ( CommandLine ( ) - > FindParm ( " -navanalyze " ) )
{
Cbuf_AddText ( " nav_edit 1;nav_analyze_scripted \n " ) ;
}
else if ( CommandLine ( ) - > FindParm ( " -navforceanalyze " ) )
{
Cbuf_AddText ( " nav_edit 1;nav_analyze_scripted force \n " ) ;
}
else if ( CommandLine ( ) - > FindParm ( " -exit " ) )
{
Cbuf_AddText ( " quit \n " ) ;
}
}
// background maps are for main menu UI, QMS not needed or used, easier context
if ( ! engineClient - > IsLevelMainMenuBackground ( ) )
{
// map load complete, safe to allow QMS
Host_AllowQueuedMaterialSystem ( true ) ;
}
// This is a Hack, but we need to suppress rendering for a bit in single player to let values settle on the client
if ( ( cl . m_nMaxClients = = 1 ) & & ! demoplayer - > IsPlayingBack ( ) )
{
scr_nextdrawtick = host_tickcount + TIME_TO_TICKS ( 0.25f ) ;
}
# ifdef _X360
// At this point, check for a valid controller connection. If it's been lost, then we need to pop our game UI up
XINPUT_CAPABILITIES caps ;
if ( XInputGetCapabilities ( XBX_GetPrimaryUserId ( ) , XINPUT_FLAG_GAMEPAD , & caps ) = = ERROR_DEVICE_NOT_CONNECTED )
{
EngineVGui ( ) - > ActivateGameUI ( ) ;
}
# endif // _X360
// Now that we're connected, toggle the clan tag so it gets sent to the server
int id = cl_clanid . GetInt ( ) ;
cl_clanid . SetValue ( 0 ) ;
cl_clanid . SetValue ( id ) ;
MemAlloc_CompactHeap ( ) ;
extern double g_flAccumulatedModelLoadTime ;
extern double g_flAccumulatedSoundLoadTime ;
extern double g_flAccumulatedModelLoadTimeStudio ;
extern double g_flAccumulatedModelLoadTimeVCollideSync ;
extern double g_flAccumulatedModelLoadTimeVCollideAsync ;
extern double g_flAccumulatedModelLoadTimeVirtualModel ;
extern double g_flAccumulatedModelLoadTimeStaticMesh ;
extern double g_flAccumulatedModelLoadTimeBrush ;
extern double g_flAccumulatedModelLoadTimeSprite ;
extern double g_flAccumulatedModelLoadTimeMaterialNamesOnly ;
// extern double g_flLoadStudioHdr;
COM_TimestampedLog ( " Sound Loading time %.4f " , g_flAccumulatedSoundLoadTime ) ;
COM_TimestampedLog ( " Model Loading time %.4f " , g_flAccumulatedModelLoadTime ) ;
COM_TimestampedLog ( " Model Loading time studio %.4f " , g_flAccumulatedModelLoadTimeStudio ) ;
COM_TimestampedLog ( " Model Loading time GetVCollide %.4f -sync " , g_flAccumulatedModelLoadTimeVCollideSync ) ;
COM_TimestampedLog ( " Model Loading time GetVCollide %.4f -async " , g_flAccumulatedModelLoadTimeVCollideAsync ) ;
COM_TimestampedLog ( " Model Loading time GetVirtualModel %.4f " , g_flAccumulatedModelLoadTimeVirtualModel ) ;
COM_TimestampedLog ( " Model loading time Mod_GetModelMaterials only %.4f " , g_flAccumulatedModelLoadTimeMaterialNamesOnly ) ;
COM_TimestampedLog ( " Model Loading time world %.4f " , g_flAccumulatedModelLoadTimeBrush ) ;
COM_TimestampedLog ( " Model Loading time sprites %.4f " , g_flAccumulatedModelLoadTimeSprite ) ;
COM_TimestampedLog ( " Model Loading time meshes %.4f " , g_flAccumulatedModelLoadTimeStaticMesh ) ;
// COM_TimestampedLog( " Model Loading time meshes studiohdr load %.4f", g_flLoadStudioHdr );
COM_TimestampedLog ( " *** Map Load Complete " ) ;
float map_loadtime_start = dev_loadtime_map_start . GetFloat ( ) ;
if ( map_loadtime_start > 0.0 )
{
float elapsed = Plat_FloatTime ( ) - map_loadtime_start ;
dev_loadtime_map_elapsed . SetValue ( elapsed ) ;
// Clear this for next time so we know we did.
dev_loadtime_map_start . SetValue ( 0.0f ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
CL_NextDemo
Called to play the next demo in the demo loop
= = = = = = = = = = = = = = = = = = = = =
*/
void CL_NextDemo ( void )
{
char str [ 1024 ] ;
if ( cl . demonum = = - 1 )
return ; // don't play demos
SCR_BeginLoadingPlaque ( ) ;
if ( cl . demos [ cl . demonum ] . IsEmpty ( ) | | cl . demonum = = MAX_DEMOS )
{
cl . demonum = 0 ;
if ( cl . demos [ cl . demonum ] . IsEmpty ( ) )
{
scr_disabled_for_loading = false ;
ConMsg ( " No demos listed with startdemos \n " ) ;
cl . demonum = - 1 ;
return ;
}
else if ( ! demoplayer - > ShouldLoopDemos ( ) )
{
cl . demonum = - 1 ;
scr_disabled_for_loading = false ;
Host_Disconnect ( true ) ;
demoplayer - > OnLastDemoInLoopPlayed ( ) ;
return ;
}
}
Q_snprintf ( str , sizeof ( str ) , " %s %s " , CommandLine ( ) - > FindParm ( " -timedemoloop " ) ? " timedemo " : " playdemo " , cl . demos [ cl . demonum ] . Get ( ) ) ;
Cbuf_AddText ( str ) ;
cl . demonum + + ;
}
ConVar cl_screenshotname ( " cl_screenshotname " , " " , 0 , " Custom Screenshot name " ) ;
// We'll take a snapshot at the next available opportunity
void CL_TakeScreenshot ( const char * name )
{
cl_takesnapshot = true ;
cl_takejpeg = false ;
cl_takesnapshot_internal = false ;
if ( name ! = NULL )
{
Q_strncpy ( cl_snapshotname , name , sizeof ( cl_snapshotname ) ) ;
}
else
{
cl_snapshotname [ 0 ] = 0 ;
if ( Q_strlen ( cl_screenshotname . GetString ( ) ) > 0 )
{
Q_snprintf ( cl_snapshotname , sizeof ( cl_snapshotname ) , " %s " , cl_screenshotname . GetString ( ) ) ;
}
}
cl_snapshot_subdirname [ 0 ] = 0 ;
}
CON_COMMAND_F ( screenshot , " Take a screenshot. " , FCVAR_CLIENTCMD_CAN_EXECUTE )
{
GetTestScriptMgr ( ) - > SetWaitCheckPoint ( " screenshot " ) ;
// Don't playback screenshots unless specifically requested.
if ( demoplayer - > IsPlayingBack ( ) & & ! cl_playback_screenshots . GetBool ( ) )
return ;
if ( args . ArgC ( ) = = 2 )
{
CL_TakeScreenshot ( args [ 1 ] ) ;
}
else
{
CL_TakeScreenshot ( NULL ) ;
}
}
CON_COMMAND_F ( devshots_screenshot , " Used by the -makedevshots system to take a screenshot. For taking your own screenshots, use the 'screenshot' command instead. " , FCVAR_DONTRECORD )
{
CL_TakeScreenshot ( NULL ) ;
// See if we got a subdirectory to store the devshots in
if ( args . ArgC ( ) = = 2 )
{
Q_strncpy ( cl_snapshot_subdirname , args [ 1 ] , sizeof ( cl_snapshot_subdirname ) ) ;
// Use the first available shot in each subdirectory
cl_snapshotnum = 0 ;
}
}
// We'll take a snapshot at the next available opportunity
void CL_TakeJpeg ( const char * name , int quality )
{
// Don't playback screenshots unless specifically requested.
if ( demoplayer - > IsPlayingBack ( ) & & ! cl_playback_screenshots . GetBool ( ) )
return ;
cl_takesnapshot = true ;
cl_takejpeg = true ;
cl_jpegquality = clamp ( quality , 1 , 100 ) ;
cl_takesnapshot_internal = false ;
if ( name ! = NULL )
{
Q_strncpy ( cl_snapshotname , name , sizeof ( cl_snapshotname ) ) ;
}
else
{
cl_snapshotname [ 0 ] = 0 ;
}
}
CON_COMMAND ( jpeg , " Take a jpeg screenshot: jpeg <filename> <quality 1-100>. " )
{
if ( args . ArgC ( ) > = 2 )
{
if ( args . ArgC ( ) = = 3 )
{
CL_TakeJpeg ( args [ 1 ] , Q_atoi ( args [ 2 ] ) ) ;
}
else
{
CL_TakeJpeg ( args [ 1 ] , jpeg_quality . GetInt ( ) ) ;
}
}
else
{
CL_TakeJpeg ( NULL , jpeg_quality . GetInt ( ) ) ;
}
}
static void screenshot_internal ( const CCommand & args )
{
if ( args . ArgC ( ) ! = 2 )
{
Assert ( args . ArgC ( ) > = 2 ) ;
Warning ( " __screenshot_internal - wrong number of arguments " ) ;
return ;
}
Q_strncpy ( cl_snapshotname , args [ 1 ] , ARRAYSIZE ( cl_snapshotname ) ) ;
cl_takesnapshot = true ;
cl_takejpeg = true ;
cl_jpegquality = 70 ;
cl_takesnapshot_internal = true ;
}
ConCommand screenshot_internal_command ( " __screenshot_internal " , screenshot_internal , " Internal command to take a screenshot without renumbering or notifying Steam. " , FCVAR_DONTRECORD | FCVAR_HIDDEN ) ;
void CL_TakeSnapshotAndSwap ( )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
bool bReadPixelsFromFrontBuffer = g_pMaterialSystemHardwareConfig - > ReadPixelsFromFrontBuffer ( ) ;
if ( bReadPixelsFromFrontBuffer )
{
Shader_SwapBuffers ( ) ;
}
if ( cl_takesnapshot )
{
// Disable threading for the duration of the screenshots, because we need to get pointers to the (complete)
// back buffer right now.
bool bEnabled = materials - > AllowThreading ( false , g_nMaterialSystemThread ) ;
char base [ MAX_OSPATH ] ;
char filename [ MAX_OSPATH ] ;
IClientEntity * world = entitylist - > GetClientEntity ( 0 ) ;
g_pFileSystem - > CreateDirHierarchy ( " screenshots " , " DEFAULT_WRITE_PATH " ) ;
if ( cl_takesnapshot_internal )
{
// !KLUDGE! Don't save this screenshot to steam
ConVarRef cl_savescreenshotstosteam ( " cl_savescreenshotstosteam " ) ;
bool bSaveValue = cl_savescreenshotstosteam . GetBool ( ) ;
cl_savescreenshotstosteam . SetValue ( false ) ;
Q_snprintf ( filename , sizeof ( filename ) , " screenshots/%s.jpg " , cl_snapshotname ) ;
videomode - > TakeSnapshotJPEG ( filename , cl_jpegquality ) ;
cl_savescreenshotstosteam . SetValue ( bSaveValue ) ;
}
else
{
if ( world & & world - > GetModel ( ) )
{
Q_FileBase ( modelloader - > GetName ( ( model_t * ) world - > GetModel ( ) ) , base , sizeof ( base ) ) ;
if ( IsX360 ( ) )
{
// map name has an additional extension
V_StripExtension ( base , base , sizeof ( base ) ) ;
}
}
else
{
Q_strncpy ( base , " Snapshot " , sizeof ( base ) ) ;
}
char extension [ MAX_OSPATH ] ;
Q_snprintf ( extension , sizeof ( extension ) , " %s.%s " , GetPlatformExt ( ) , cl_takejpeg ? " jpg " : " tga " ) ;
// Using a subdir? If so, create it
if ( cl_snapshot_subdirname [ 0 ] )
{
Q_snprintf ( filename , sizeof ( filename ) , " screenshots/%s/%s " , base , cl_snapshot_subdirname ) ;
g_pFileSystem - > CreateDirHierarchy ( filename , " DEFAULT_WRITE_PATH " ) ;
}
if ( cl_snapshotname [ 0 ] )
{
Q_strncpy ( base , cl_snapshotname , sizeof ( base ) ) ;
Q_snprintf ( filename , sizeof ( filename ) , " screenshots/%s%s " , base , extension ) ;
int iNumber = 0 ;
char renamedfile [ MAX_OSPATH ] ;
while ( 1 )
{
Q_snprintf ( renamedfile , sizeof ( renamedfile ) , " screenshots/%s_%04d%s " , base , iNumber + + , extension ) ;
if ( ! g_pFileSystem - > GetFileTime ( renamedfile ) )
break ;
}
if ( iNumber > 0 & & g_pFileSystem - > GetFileTime ( filename ) )
{
g_pFileSystem - > RenameFile ( filename , renamedfile ) ;
}
cl_screenshotname . SetValue ( " " ) ;
}
else
{
while ( 1 )
{
if ( cl_snapshot_subdirname [ 0 ] )
{
Q_snprintf ( filename , sizeof ( filename ) , " screenshots/%s/%s/%s%04d%s " , base , cl_snapshot_subdirname , base , cl_snapshotnum + + , extension ) ;
}
else
{
Q_snprintf ( filename , sizeof ( filename ) , " screenshots/%s%04d%s " , base , cl_snapshotnum + + , extension ) ;
}
if ( ! g_pFileSystem - > GetFileTime ( filename ) )
{
// woo hoo! The file doesn't exist already, so use it.
break ;
}
}
}
if ( cl_takejpeg )
{
videomode - > TakeSnapshotJPEG ( filename , cl_jpegquality ) ;
g_ServerRemoteAccess . UploadScreenshot ( filename ) ;
}
else
{
videomode - > TakeSnapshotTGA ( filename ) ;
}
}
cl_takesnapshot = false ;
cl_takesnapshot_internal = false ;
GetTestScriptMgr ( ) - > CheckPoint ( " screenshot " ) ;
// Restore threading if it was previously enabled (if it wasn't this will do nothing).
materials - > AllowThreading ( bEnabled , g_nMaterialSystemThread ) ;
}
// If recording movie and the console is totally up, then write out this frame to movie file.
if ( cl_movieinfo . IsRecording ( ) & & ! Con_IsVisible ( ) & & ! scr_drawloading )
{
videomode - > WriteMovieFrame ( cl_movieinfo ) ;
+ + cl_movieinfo . movieframe ;
}
if ( ! bReadPixelsFromFrontBuffer )
{
Shader_SwapBuffers ( ) ;
}
// take a screenshot for savegames if necessary
saverestore - > UpdateSaveGameScreenshots ( ) ;
// take screenshot for bx movie maker
EngineTool_UpdateScreenshot ( ) ;
}
static float s_flPreviousHostFramerate = 0 ;
ConVar cl_simulate_no_quicktime ( " cl_simulate_no_quicktime " , " 0 " , FCVAR_HIDDEN ) ;
void CL_StartMovie ( const char * filename , int flags , int nWidth , int nHeight , float flFrameRate , int nJpegQuality , VideoSystem_t videoSystem )
{
Assert ( g_pVideoRecorder = = NULL ) ;
// StartMove depends on host_framerate not being 0.
s_flPreviousHostFramerate = host_framerate . GetFloat ( ) ;
host_framerate . SetValue ( flFrameRate ) ;
cl_movieinfo . Reset ( ) ;
Q_strncpy ( cl_movieinfo . moviename , filename , sizeof ( cl_movieinfo . moviename ) ) ;
cl_movieinfo . type = flags ;
cl_movieinfo . jpeg_quality = nJpegQuality ;
bool bSuccess = true ;
if ( cl_movieinfo . DoVideo ( ) | | cl_movieinfo . DoVideoSound ( ) )
{
// HACK: THIS MUST MATCH snd_device.h. Should be exposed more cleanly!!!
# define SOUND_DMA_SPEED 44100 // hardware playback rate
// MGP - switched over to using valve video services from avi
if ( videoSystem = = VideoSystem : : NONE & & g_pVideo )
{
// Find a video system based on features if they didn't specify a specific one.
VideoSystemFeature_t neededFeatures = VideoSystemFeature : : NO_FEATURES ;
if ( cl_movieinfo . DoVideo ( ) )
neededFeatures | = VideoSystemFeature : : ENCODE_VIDEO_TO_FILE ;
if ( cl_movieinfo . DoVideoSound ( ) )
neededFeatures | = VideoSystemFeature : : ENCODE_AUDIO_TO_FILE ;
videoSystem = g_pVideo - > FindNextSystemWithFeature ( neededFeatures ) ;
}
if ( ! cl_simulate_no_quicktime . GetBool ( ) & & g_pVideo & & videoSystem ! = VideoSystem : : NONE )
{
g_pVideoRecorder = g_pVideo - > CreateVideoRecorder ( videoSystem ) ;
if ( g_pVideoRecorder ! = NULL )
{
VideoFrameRate_t theFps ;
if ( IsIntegralValue ( flFrameRate ) )
{
theFps . SetFPS ( RoundFloatToInt ( flFrameRate ) , false ) ;
}
else if ( IsIntegralValue ( flFrameRate * 1001.0f / 1000.0f ) ) // 1001 is the ntsc divisor (30*1000/1001 = 29.97, etc)
{
theFps . SetFPS ( RoundFloatToInt ( flFrameRate + 0.05f ) , true ) ;
}
else
{
theFps . SetFPS ( RoundFloatToInt ( flFrameRate ) , false ) ;
}
const int nSize = 256 ;
CFmtStrN < nSize > fmtFullFilename ( " %s%c%s " , com_gamedir , CORRECT_PATH_SEPARATOR , filename ) ;
char szFullFilename [ nSize ] ;
V_FixupPathName ( szFullFilename , nSize , fmtFullFilename . Access ( ) ) ;
# ifdef USE_WEBM_FOR_REPLAY
V_DefaultExtension ( szFullFilename , " .webm " , sizeof ( szFullFilename ) ) ;
# else
V_DefaultExtension ( szFullFilename , " .mp4 " , sizeof ( szFullFilename ) ) ;
# endif
g_pVideoRecorder - > CreateNewMovieFile ( szFullFilename , cl_movieinfo . DoVideoSound ( ) ) ;
# ifdef USE_WEBM_FOR_REPLAY
g_pVideoRecorder - > SetMovieVideoParameters ( VideoEncodeCodec : : WEBM_CODEC , nJpegQuality , nWidth , nHeight , theFps ) ;
# else
g_pVideoRecorder - > SetMovieVideoParameters ( VideoEncodeCodec : : DEFAULT_CODEC , nJpegQuality , nWidth , nHeight , theFps ) ;
# endif
if ( cl_movieinfo . DoVideo ( ) )
{
g_pVideoRecorder - > SetMovieSourceImageParameters ( VideoEncodeSourceFormat : : BGR_24BIT , nWidth , nHeight ) ;
}
if ( cl_movieinfo . DoVideoSound ( ) )
{
g_pVideoRecorder - > SetMovieSourceAudioParameters ( AudioEncodeSourceFormat : : AUDIO_16BIT_PCMStereo , SOUND_DMA_SPEED , AudioEncodeOptions : : LIMIT_AUDIO_TRACK_TO_VIDEO_DURATION ) ;
}
}
else
{
bSuccess = false ;
}
}
else
{
bSuccess = false ;
}
}
if ( bSuccess )
{
SND_MovieStart ( ) ;
}
else
{
# ifdef USE_WEBM_FOR_REPLAY
Warning ( " Failed to launch startmovie! \n " ) ;
# else
Warning ( " Failed to launch startmovie! If you are trying to use h264, please make sure you have QuickTime installed. \n " ) ;
# endif
CL_EndMovie ( ) ;
}
}
void CL_EndMovie ( )
{
if ( ! CL_IsRecordingMovie ( ) )
return ;
host_framerate . SetValue ( s_flPreviousHostFramerate ) ;
s_flPreviousHostFramerate = 0.0f ;
SND_MovieEnd ( ) ;
if ( g_pVideoRecorder & & ( cl_movieinfo . DoVideo ( ) | | cl_movieinfo . DoVideoSound ( ) ) )
{
g_pVideoRecorder - > FinishMovie ( ) ;
g_pVideo - > DestroyVideoRecorder ( g_pVideoRecorder ) ;
g_pVideoRecorder = NULL ;
}
cl_movieinfo . Reset ( ) ;
}
bool CL_IsRecordingMovie ( )
{
return cl_movieinfo . IsRecording ( ) ;
}
/*
= = = = = = = = = = = = = = =
CL_StartMovie_f
Sets the engine up to dump frames
= = = = = = = = = = = = = = =
*/
CON_COMMAND_F ( startmovie , " Start recording movie frames. " , FCVAR_DONTRECORD )
{
if ( cmd_source ! = src_command )
return ;
if ( args . ArgC ( ) < 2 )
{
ConMsg ( " startmovie <filename> \n [ \n " ) ;
ConMsg ( " (default = TGAs + .wav file) \n " ) ;
# ifdef USE_WEBM_FOR_REPLAY
ConMsg ( " webm = WebM encoded audio and video \n " ) ;
# else
ConMsg ( " h264 = H.264-encoded audio and video (must have QuickTime installed!) \n " ) ;
# endif
ConMsg ( " raw = TGAs + .wav file, same as default \n " ) ;
ConMsg ( " tga = TGAs \n " ) ;
ConMsg ( " jpg/jpeg = JPegs \n " ) ;
ConMsg ( " wav = Write .wav audio file \n " ) ;
ConMsg ( " jpeg_quality nnn = set jpeq quality to nnn (range 1 to 100), default %d \n " , DEFAULT_JPEG_QUALITY ) ;
ConMsg ( " ] \n " ) ;
ConMsg ( " examples: \n " ) ;
ConMsg ( " startmovie testmovie jpg wav jpeg_qality 75 \n " ) ;
# ifdef USE_WEBM_FOR_REPLAY
ConMsg ( " startmovie testmovie webm \n " ) ;
# else
ConMsg ( " startmovie testmovie h264 <--- requires QuickTime \n " ) ;
ConMsg ( " AVI is no longer supported. \n " ) ;
# endif
return ;
}
if ( CL_IsRecordingMovie ( ) )
{
ConMsg ( " Already recording movie! \n " ) ;
return ;
}
int flags = MovieInfo_t : : FMOVIE_TGA | MovieInfo_t : : FMOVIE_WAV ;
VideoSystem_t videoSystem = VideoSystem : : NONE ;
int nJpegQuality = DEFAULT_JPEG_QUALITY ;
if ( args . ArgC ( ) > 2 )
{
flags = 0 ;
for ( int i = 2 ; i < args . ArgC ( ) ; + + i )
{
if ( ! Q_stricmp ( args [ i ] , " avi " ) )
{
//flags |= MovieInfo_t::FMOVIE_VID | MovieInfo_t::FMOVIE_VIDSOUND;
//videoSystem = VideoSystem::AVI;
# ifdef USE_WEBM_FOR_REPLAY
Warning ( " AVI is not supported on this platform! Use \" webm \" . \n " ) ;
# else
Warning ( " AVI is no longer supported! Make sure QuickTime is installed and use \" h264 \" - if you install QuickTime, you will need to reboot before using startmovie. \n " ) ;
# endif
return ;
}
# ifdef USE_WEBM_FOR_REPLAY
else if ( ! Q_stricmp ( args [ i ] , " webm " ) )
{
flags | = MovieInfo_t : : FMOVIE_VID | MovieInfo_t : : FMOVIE_VIDSOUND ;
videoSystem = VideoSystem : : WEBM ;
}
else if ( ! Q_stricmp ( args [ i ] , " h264 " ) )
{
Warning ( " h264 is not supported on this platform! Use \" webm \" . \n " ) ;
return ;
}
# else
else if ( ! Q_stricmp ( args [ i ] , " h264 " ) )
{
flags | = MovieInfo_t : : FMOVIE_VID | MovieInfo_t : : FMOVIE_VIDSOUND ;
videoSystem = VideoSystem : : QUICKTIME ;
}
else if ( ! Q_stricmp ( args [ i ] , " webm " ) )
{
Warning ( " WebM is not supported on this platform! Make sure QuickTime is installed and use \" h264 \" - if you install QuickTime, you will need to reboot before using startmovie. \n " ) ;
return ;
}
# endif
if ( ! Q_stricmp ( args [ i ] , " raw " ) )
{
flags | = MovieInfo_t : : FMOVIE_TGA | MovieInfo_t : : FMOVIE_WAV ;
}
if ( ! Q_stricmp ( args [ i ] , " tga " ) )
{
flags | = MovieInfo_t : : FMOVIE_TGA ;
}
if ( ! Q_stricmp ( args [ i ] , " jpeg " ) | | ! Q_stricmp ( args [ i ] , " jpg " ) )
{
flags & = ~ MovieInfo_t : : FMOVIE_TGA ;
flags | = MovieInfo_t : : FMOVIE_JPG ;
}
if ( ! Q_stricmp ( args [ i ] , " jpeg_quality " ) )
{
nJpegQuality = clamp ( Q_atoi ( args [ + + i ] ) , 1 , 100 ) ;
}
if ( ! Q_stricmp ( args [ i ] , " wav " ) )
{
flags | = MovieInfo_t : : FMOVIE_WAV ;
}
}
}
if ( flags = = 0 )
{
# ifdef USE_WEBM_FOR_REPLAY
Warning ( " Missing or unknown recording types, must specify one or both of 'webm' or 'raw' \n " ) ;
# else
Warning ( " Missing or unknown recording types, must specify one or both of 'h264' or 'raw' \n " ) ;
# endif
return ;
}
float flFrameRate = host_framerate . GetFloat ( ) ;
if ( flFrameRate = = 0.0f )
{
flFrameRate = 30.0f ;
}
CL_StartMovie ( args [ 1 ] , flags , videomode - > GetModeStereoWidth ( ) , videomode - > GetModeStereoHeight ( ) , flFrameRate , nJpegQuality , videoSystem ) ;
ConMsg ( " Started recording movie, frames will record after console is cleared... \n " ) ;
}
//-----------------------------------------------------------------------------
// Ends frame dumping
//-----------------------------------------------------------------------------
CON_COMMAND_F ( endmovie , " Stop recording movie frames. " , FCVAR_DONTRECORD )
{
if ( ! CL_IsRecordingMovie ( ) )
{
ConMsg ( " No movie started. \n " ) ;
}
else
{
CL_EndMovie ( ) ;
ConMsg ( " Stopped recording movie... \n " ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
CL_Rcon_f
Send the rest of the command line over as
an unconnected command .
= = = = = = = = = = = = = = = = = = = = =
*/
CON_COMMAND_F ( rcon , " Issue an rcon command. " , FCVAR_DONTRECORD )
{
char message [ 1024 ] ; // Command message
char szParam [ 256 ] ;
message [ 0 ] = 0 ;
for ( int i = 1 ; i < args . ArgC ( ) ; i + + )
{
const char * pParam = args [ i ] ;
// put quotes around empty arguments so we can pass things like this: rcon sv_password ""
// otherwise the "" on the end is lost
if ( strchr ( pParam , ' ' ) | | ( Q_strlen ( pParam ) = = 0 ) )
{
Q_snprintf ( szParam , sizeof ( szParam ) , " \" %s \" " , pParam ) ;
Q_strncat ( message , szParam , sizeof ( message ) , COPY_ALL_CHARACTERS ) ;
}
else
{
Q_strncat ( message , pParam , sizeof ( message ) , COPY_ALL_CHARACTERS ) ;
}
if ( i ! = ( args . ArgC ( ) - 1 ) )
{
Q_strncat ( message , " " , sizeof ( message ) , COPY_ALL_CHARACTERS ) ;
}
}
RCONClient ( ) . SendCmd ( message ) ;
}
CON_COMMAND_F ( box , " Draw a debug box. " , FCVAR_CHEAT )
{
if ( args . ArgC ( ) ! = 7 )
{
ConMsg ( " box x1 y1 z1 x2 y2 z2 \n " ) ;
return ;
}
Vector mins , maxs ;
for ( int i = 0 ; i < 3 ; + + i )
{
mins [ i ] = atof ( args [ i + 1 ] ) ;
maxs [ i ] = atof ( args [ i + 4 ] ) ;
}
CDebugOverlay : : AddBoxOverlay ( vec3_origin , mins , maxs , vec3_angle , 255 , 0 , 0 , 0 , 100 ) ;
}
/*
= = = = = = = = = = = = = =
CL_View_f
Debugging changes the view entity to the specified index
= = = = = = = = = = = = = = =
*/
CON_COMMAND_F ( cl_view , " Set the view entity index. " , FCVAR_CHEAT )
{
int nNewView ;
if ( args . ArgC ( ) ! = 2 )
{
ConMsg ( " cl_view entity# \n Current %i \n " , cl . m_nViewEntity ) ;
return ;
}
if ( cl . m_nMaxClients > 1 )
return ;
nNewView = atoi ( args [ 1 ] ) ;
if ( ! nNewView )
return ;
if ( nNewView > entitylist - > GetHighestEntityIndex ( ) )
return ;
cl . m_nViewEntity = nNewView ;
videomode - > MarkClientViewRectDirty ( ) ; // Force recalculation
ConMsg ( " View entity set to %i \n " , nNewView ) ;
}
static int CL_AllocLightFromArray ( dlight_t * pLights , int lightCount , int key )
{
int i ;
// first look for an exact key match
if ( key )
{
for ( i = 0 ; i < lightCount ; i + + )
{
if ( pLights [ i ] . key = = key )
return i ;
}
}
// then look for anything else
for ( i = 0 ; i < lightCount ; i + + )
{
if ( pLights [ i ] . die < cl . GetTime ( ) )
return i ;
}
return 0 ;
}
bool g_bActiveDlights = false ;
bool g_bActiveElights = false ;
/*
= = = = = = = = = = = = = = =
CL_AllocDlight
= = = = = = = = = = = = = = =
*/
dlight_t * CL_AllocDlight ( int key )
{
int i = CL_AllocLightFromArray ( cl_dlights , MAX_DLIGHTS , key ) ;
dlight_t * dl = & cl_dlights [ i ] ;
R_MarkDLightNotVisible ( i ) ;
memset ( dl , 0 , sizeof ( * dl ) ) ;
dl - > key = key ;
r_dlightchanged | = ( 1 < < i ) ;
r_dlightactive | = ( 1 < < i ) ;
g_bActiveDlights = true ;
return dl ;
}
/*
= = = = = = = = = = = = = = =
CL_AllocElight
= = = = = = = = = = = = = = =
*/
dlight_t * CL_AllocElight ( int key )
{
int i = CL_AllocLightFromArray ( cl_elights , MAX_ELIGHTS , key ) ;
dlight_t * el = & cl_elights [ i ] ;
memset ( el , 0 , sizeof ( * el ) ) ;
el - > key = key ;
g_bActiveElights = true ;
return el ;
}
/*
= = = = = = = = = = = = = = =
CL_DecayLights
= = = = = = = = = = = = = = =
*/
void CL_DecayLights ( void )
{
int i ;
dlight_t * dl ;
float time ;
time = cl . GetFrameTime ( ) ;
if ( time < = 0.0f )
return ;
g_bActiveDlights = false ;
g_bActiveElights = false ;
dl = cl_dlights ;
r_dlightchanged = 0 ;
r_dlightactive = 0 ;
for ( i = 0 ; i < MAX_DLIGHTS ; i + + , dl + + )
{
if ( ! dl - > IsRadiusGreaterThanZero ( ) )
{
R_MarkDLightNotVisible ( i ) ;
continue ;
}
if ( dl - > die < cl . GetTime ( ) )
{
r_dlightchanged | = ( 1 < < i ) ;
dl - > radius = 0 ;
}
else if ( dl - > decay )
{
r_dlightchanged | = ( 1 < < i ) ;
dl - > radius - = time * dl - > decay ;
if ( dl - > radius < 0 )
{
dl - > radius = 0 ;
}
}
if ( dl - > IsRadiusGreaterThanZero ( ) )
{
g_bActiveDlights = true ;
r_dlightactive | = ( 1 < < i ) ;
}
else
{
R_MarkDLightNotVisible ( i ) ;
}
}
dl = cl_elights ;
for ( i = 0 ; i < MAX_ELIGHTS ; i + + , dl + + )
{
if ( ! dl - > IsRadiusGreaterThanZero ( ) )
continue ;
if ( dl - > die < cl . GetTime ( ) )
{
dl - > radius = 0 ;
continue ;
}
dl - > radius - = time * dl - > decay ;
if ( dl - > radius < 0 )
{
dl - > radius = 0 ;
}
if ( dl - > IsRadiusGreaterThanZero ( ) )
{
g_bActiveElights = true ;
}
}
}
void CL_ExtraMouseUpdate ( float frametime )
{
// Not ready for commands yet.
if ( ! cl . IsActive ( ) )
return ;
if ( ! Host_ShouldRun ( ) )
return ;
// Don't create usercmds here during playback, they were encoded into the packet already
# if defined( REPLAY_ENABLED )
if ( demoplayer - > IsPlayingBack ( ) & & ! cl . ishltv & & ! cl . isreplay )
return ;
# else
if ( demoplayer - > IsPlayingBack ( ) & & ! cl . ishltv )
return ;
# endif
// Have client .dll create and store usercmd structure
g_ClientDLL - > ExtraMouseSample ( frametime , ! cl . m_bPaused ) ;
}
/*
= = = = = = = = = = = = = = = = =
CL_SendMove
Constructs the movement command and sends it to the server if it ' s time .
= = = = = = = = = = = = = = = = =
*/
void CL_SendMove ( void )
{
# if defined( STAGING_ONLY ) || defined( _DEBUG )
if ( cl_block_usercommand . GetBool ( ) )
return ;
# endif // STAGING_ONLY || _DEBUG
byte data [ MAX_CMD_BUFFER ] ;
int nextcommandnr = cl . lastoutgoingcommand + cl . chokedcommands + 1 ;
// send the client update packet
CLC_Move moveMsg ;
moveMsg . m_DataOut . StartWriting ( data , sizeof ( data ) ) ;
// Determine number of backup commands to send along
int cl_cmdbackup = 2 ;
moveMsg . m_nBackupCommands = clamp ( cl_cmdbackup , 0 , MAX_BACKUP_COMMANDS ) ;
// How many real new commands have queued up
moveMsg . m_nNewCommands = 1 + cl . chokedcommands ;
moveMsg . m_nNewCommands = clamp ( moveMsg . m_nNewCommands , 0 , MAX_NEW_COMMANDS ) ;
int numcmds = moveMsg . m_nNewCommands + moveMsg . m_nBackupCommands ;
int from = - 1 ; // first command is deltaed against zeros
bool bOK = true ;
for ( int to = nextcommandnr - numcmds + 1 ; to < = nextcommandnr ; to + + )
{
bool isnewcmd = to > = ( nextcommandnr - moveMsg . m_nNewCommands + 1 ) ;
// first valid command number is 1
bOK = bOK & & g_ClientDLL - > WriteUsercmdDeltaToBuffer ( & moveMsg . m_DataOut , from , to , isnewcmd ) ;
from = to ;
}
if ( bOK )
{
// only write message if all usercmds were written correctly, otherwise parsing would fail
cl . m_NetChannel - > SendNetMsg ( moveMsg ) ;
}
}
void CL_Move ( float accumulated_extra_samples , bool bFinalTick )
{
if ( ! cl . IsConnected ( ) )
return ;
if ( ! Host_ShouldRun ( ) )
return ;
tmZoneFiltered ( TELEMETRY_LEVEL0 , 50 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
// only send packets on the final tick in one engine frame
bool bSendPacket = true ;
// Don't create usercmds here during playback, they were encoded into the packet already
if ( demoplayer - > IsPlayingBack ( ) )
{
# if defined( REPLAY_ENABLED )
if ( cl . ishltv | | cl . isreplay )
# else
if ( cl . ishltv )
# endif
{
// still do it when playing back a HLTV/replay demo
bSendPacket = false ;
}
else
{
return ;
}
}
// don't send packets if update time not reached or chnnel still sending
// in loopback mode don't send only if host_limitlocal is enabled
if ( ( ! cl . m_NetChannel - > IsLoopback ( ) | | host_limitlocal . GetInt ( ) ) & &
( ( net_time < cl . m_flNextCmdTime ) | | ! cl . m_NetChannel - > CanPacket ( ) | | ! bFinalTick ) )
{
bSendPacket = false ;
}
if ( cl . IsActive ( ) )
{
VPROF ( " CL_Move " ) ;
int nextcommandnr = cl . lastoutgoingcommand + cl . chokedcommands + 1 ;
// Have client .dll create and store usercmd structure
g_ClientDLL - > CreateMove (
nextcommandnr ,
host_state . interval_per_tick - accumulated_extra_samples ,
! cl . IsPaused ( ) ) ;
// Store new usercmd to dem file
if ( demorecorder - > IsRecording ( ) )
{
// Back up one because we've incremented outgoing_sequence each frame by 1 unit
demorecorder - > RecordUserInput ( nextcommandnr ) ;
}
if ( bSendPacket )
{
CL_SendMove ( ) ;
}
else
{
// netchanll will increase internal outgoing sequnce number too
cl . m_NetChannel - > SetChoked ( ) ;
// Mark command as held back so we'll send it next time
cl . chokedcommands + + ;
}
}
if ( ! bSendPacket )
return ;
// Request non delta compression if high packet loss, show warning message
bool hasProblem = cl . m_NetChannel - > IsTimingOut ( ) & & ! demoplayer - > IsPlayingBack ( ) & & cl . IsActive ( ) ;
// Request non delta compression if high packet loss, show warning message
if ( hasProblem )
{
con_nprint_t np ;
np . time_to_live = 1.0 ;
np . index = 2 ;
np . fixed_width_font = false ;
np . color [ 0 ] = 1.0 ;
np . color [ 1 ] = 0.2 ;
np . color [ 2 ] = 0.2 ;
float flTimeOut = cl . m_NetChannel - > GetTimeoutSeconds ( ) ;
Assert ( flTimeOut ! = - 1.0f ) ;
float flRemainingTime = flTimeOut - cl . m_NetChannel - > GetTimeSinceLastReceived ( ) ;
Con_NXPrintf ( & np , " WARNING: Connection Problem " ) ;
np . index = 3 ;
Con_NXPrintf ( & np , " Auto-disconnect in %.1f seconds " , flRemainingTime ) ;
cl . ForceFullUpdate ( ) ; // sets m_nDeltaTick to -1
}
if ( cl . IsActive ( ) )
{
NET_Tick mymsg ( cl . m_nDeltaTick , host_frametime_unbounded , host_frametime_stddeviation ) ;
cl . m_NetChannel - > SendNetMsg ( mymsg ) ;
}
//COM_Log( "cl.log", "Sending command number %i(%i) to server\n", cl.m_NetChan->m_nOutSequenceNr, cl.m_NetChan->m_nOutSequenceNr & CL_UPDATE_MASK );
// Remember outgoing command that we are sending
cl . lastoutgoingcommand = cl . m_NetChannel - > SendDatagram ( NULL ) ;
cl . chokedcommands = 0 ;
// calc next packet send time
if ( cl . IsActive ( ) )
{
// use full update rate when active
float commandInterval = 1.0f / cl_cmdrate - > GetFloat ( ) ;
float maxDelta = min ( host_state . interval_per_tick , commandInterval ) ;
float delta = clamp ( ( float ) ( net_time - cl . m_flNextCmdTime ) , 0.0f , maxDelta ) ;
cl . m_flNextCmdTime = net_time + commandInterval - delta ;
}
else
{
// during signon process send only 5 packets/second
cl . m_flNextCmdTime = net_time + ( 1.0f / 5.0f ) ;
}
}
# define TICK_INTERVAL (host_state.interval_per_tick)
# define ROUND_TO_TICKS( t ) ( TICK_INTERVAL * TIME_TO_TICKS( t ) )
void CL_LatchInterpolationAmount ( )
{
if ( ! cl . IsConnected ( ) )
return ;
float dt = cl . m_NetChannel - > GetTimeSinceLastReceived ( ) ;
float flClientInterpolationAmount = ROUND_TO_TICKS ( cl . GetClientInterpAmount ( ) ) ;
float flInterp = 0.0f ;
if ( flClientInterpolationAmount > 0.001 )
{
flInterp = clamp ( dt / flClientInterpolationAmount , 0.0f , 3.0f ) ;
}
cl . m_NetChannel - > SetInterpolationAmount ( flInterp ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pMessage -
//-----------------------------------------------------------------------------
void CL_HudMessage ( const char * pMessage )
{
if ( g_ClientDLL )
{
g_ClientDLL - > HudText ( pMessage ) ;
}
}
CON_COMMAND_F ( cl_showents , " Dump entity list to console. " , FCVAR_CHEAT )
{
for ( int i = 0 ; i < entitylist - > GetMaxEntities ( ) ; i + + )
{
char entStr [ 256 ] , classStr [ 256 ] ;
IClientNetworkable * pEnt ;
if ( ( pEnt = entitylist - > GetClientNetworkable ( i ) ) ! = NULL )
{
entStr [ 0 ] = 0 ;
Q_snprintf ( classStr , sizeof ( classStr ) , " '%s' " , pEnt - > GetClientClass ( ) - > m_pNetworkName ) ;
}
else
{
Q_snprintf ( entStr , sizeof ( entStr ) , " (missing), " ) ;
Q_snprintf ( classStr , sizeof ( classStr ) , " (missing) " ) ;
}
if ( pEnt )
ConMsg ( " Ent %3d: %s class %s \n " , i , entStr , classStr ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: returns true if the background level should be loaded on startup
//-----------------------------------------------------------------------------
bool CL_ShouldLoadBackgroundLevel ( const CCommand & args )
{
if ( InEditMode ( ) )
return false ;
// If TF2 and PC we don't want to load the background map.
bool bIsTF2 = false ;
if ( ( Q_stricmp ( COM_GetModDirectory ( ) , " tf " ) = = 0 ) | | ( Q_stricmp ( COM_GetModDirectory ( ) , " tf_beta " ) = = 0 ) )
{
bIsTF2 = true ;
}
if ( bIsTF2 & & IsPC ( ) )
return false ;
if ( args . ArgC ( ) = = 2 )
{
// presence of args identifies an end-of-game situation
if ( IsX360 ( ) )
{
// 360 needs to get UI in the correct state to transition to the Background level
// from the credits.
EngineVGui ( ) - > OnCreditsFinished ( ) ;
return true ;
}
if ( ! Q_stricmp ( args [ 1 ] , " force " ) )
{
// Adrian: Have to do this so the menu shows up if we ever call this while in a level.
Host_Disconnect ( true ) ;
// pc can't get into background maps fast enough, so just show main menu
return false ;
}
if ( ! Q_stricmp ( args [ 1 ] , " playendgamevid " ) )
{
// Bail back to the menu and play the end game video.
CommandLine ( ) - > AppendParm ( " -endgamevid " , NULL ) ;
CommandLine ( ) - > RemoveParm ( " -recapvid " ) ;
game - > PlayStartupVideos ( ) ;
CommandLine ( ) - > RemoveParm ( " -endgamevid " ) ;
cl . Disconnect ( " Finished playing end game videos " , true ) ;
return false ;
}
if ( ! Q_stricmp ( args [ 1 ] , " playrecapvid " ) )
{
// Bail back to the menu and play the recap video
CommandLine ( ) - > AppendParm ( " -recapvid " , NULL ) ;
CommandLine ( ) - > RemoveParm ( " -endgamevid " ) ;
HostState_Restart ( ) ;
return false ;
}
}
// if force is set, then always return true
if ( CommandLine ( ) - > CheckParm ( " -forcestartupmenu " ) )
return true ;
// don't load the map in developer or console mode
if ( developer . GetInt ( ) | |
CommandLine ( ) - > CheckParm ( " -console " ) | |
CommandLine ( ) - > CheckParm ( " -dev " ) )
return false ;
// don't load the map if we're going straight into a level
if ( CommandLine ( ) - > CheckParm ( " +map " ) | |
CommandLine ( ) - > CheckParm ( " +connect " ) | |
CommandLine ( ) - > CheckParm ( " +playdemo " ) | |
CommandLine ( ) - > CheckParm ( " +timedemo " ) | |
CommandLine ( ) - > CheckParm ( " +timedemoquit " ) | |
CommandLine ( ) - > CheckParm ( " +load " ) | |
CommandLine ( ) - > CheckParm ( " -makereslists " ) )
return false ;
# ifdef _X360
// check if we are accepting an invite
if ( XboxLaunch ( ) - > GetLaunchFlags ( ) & LF_INVITERESTART )
return false ;
# endif
// nothing else is going on, so load the startup level
return true ;
}
# define DEFAULT_BACKGROUND_NAME "background01"
int g_iRandomChapterIndex = - 1 ;
int CL_GetBackgroundLevelIndex ( int nNumChapters )
{
if ( g_iRandomChapterIndex ! = - 1 )
return g_iRandomChapterIndex ;
int iChapterIndex = sv_unlockedchapters . GetInt ( ) ;
if ( iChapterIndex < = 0 )
{
// expected to be [1..N]
iChapterIndex = 1 ;
}
if ( sv_unlockedchapters . GetInt ( ) > = ( nNumChapters - 1 ) )
{
RandomSeed ( Plat_MSTime ( ) ) ;
g_iRandomChapterIndex = iChapterIndex = RandomInt ( 1 , nNumChapters ) ;
}
return iChapterIndex ;
}
//-----------------------------------------------------------------------------
// Purpose: returns the name of the background level to load
//-----------------------------------------------------------------------------
void CL_GetBackgroundLevelName ( char * pszBackgroundName , int bufSize , bool bMapName )
{
Q_strncpy ( pszBackgroundName , DEFAULT_BACKGROUND_NAME , bufSize ) ;
KeyValues * pChapterFile = new KeyValues ( pszBackgroundName ) ;
if ( pChapterFile - > LoadFromFile ( g_pFileSystem , " scripts/ChapterBackgrounds.txt " ) )
{
KeyValues * pChapterRoot = pChapterFile ;
const char * szChapterIndex ;
int nNumChapters = 1 ;
KeyValues * pChapters = pChapterFile - > GetNextKey ( ) ;
if ( bMapName & & pChapters )
{
const char * pszName = pChapters - > GetName ( ) ;
if ( pszName & & pszName [ 0 ] & & ! Q_strncmp ( " BackgroundMaps " , pszName , 14 ) )
{
pChapterRoot = pChapters ;
pChapters = pChapters - > GetFirstSubKey ( ) ;
}
else
{
pChapters = NULL ;
}
}
else
{
pChapters = NULL ;
}
if ( ! pChapters )
{
pChapters = pChapterFile - > GetFirstSubKey ( ) ;
}
// Find the highest indexed chapter
while ( pChapters )
{
szChapterIndex = pChapters - > GetName ( ) ;
if ( szChapterIndex )
{
int nChapter = atoi ( szChapterIndex ) ;
if ( nChapter > nNumChapters )
nNumChapters = nChapter ;
}
pChapters = pChapters - > GetNextKey ( ) ;
}
int nChapterToLoad = CL_GetBackgroundLevelIndex ( nNumChapters ) ;
// Find the chapter background with this index
char buf [ 4 ] ;
Q_snprintf ( buf , sizeof ( buf ) , " %d " , nChapterToLoad ) ;
KeyValues * pLoadChapter = pChapterRoot - > FindKey ( buf ) ;
// Copy the background name
if ( pLoadChapter )
{
Q_strncpy ( pszBackgroundName , pLoadChapter - > GetString ( ) , bufSize ) ;
}
}
pChapterFile - > deleteThis ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Callback to open the game menus
//-----------------------------------------------------------------------------
void CL_CheckToDisplayStartupMenus ( const CCommand & args )
{
if ( CL_ShouldLoadBackgroundLevel ( args ) )
{
char szBackgroundName [ _MAX_PATH ] ;
CL_GetBackgroundLevelName ( szBackgroundName , sizeof ( szBackgroundName ) , true ) ;
char cmd [ _MAX_PATH ] ;
Q_snprintf ( cmd , sizeof ( cmd ) , " map_background %s \n " , szBackgroundName ) ;
Cbuf_AddText ( cmd ) ;
}
}
static float s_fDemoRevealGameUITime = - 1 ;
float s_fDemoPlayMusicTime = - 1 ;
static bool s_bIsRavenHolmn = false ;
//-----------------------------------------------------------------------------
// Purpose: run the special demo logic when transitioning from the trainstation levels
//----------------------------------------------------------------------------
void CL_DemoTransitionFromTrainstation ( )
{
// kick them out to GameUI instead and bring up the chapter page with raveholm unlocked
sv_unlockedchapters . SetValue ( 6 ) ; // unlock ravenholm
Cbuf_AddText ( " sv_cheats 1; fadeout 1.5; sv_cheats 0; " ) ;
Cbuf_Execute ( ) ;
s_fDemoRevealGameUITime = Sys_FloatTime ( ) + 1.5 ;
s_bIsRavenHolmn = false ;
}
void CL_DemoTransitionFromRavenholm ( )
{
Cbuf_AddText ( " sv_cheats 1; fadeout 2; sv_cheats 0; " ) ;
Cbuf_Execute ( ) ;
s_fDemoRevealGameUITime = Sys_FloatTime ( ) + 1.9 ;
s_bIsRavenHolmn = true ;
}
void CL_DemoTransitionFromTestChmb ( )
{
Cbuf_AddText ( " sv_cheats 1; fadeout 2; sv_cheats 0; " ) ;
Cbuf_Execute ( ) ;
s_fDemoRevealGameUITime = Sys_FloatTime ( ) + 1.9 ;
}
//-----------------------------------------------------------------------------
// Purpose: make the gameui appear after a certain interval
//----------------------------------------------------------------------------
void V_RenderVGuiOnly ( ) ;
bool V_CheckGamma ( ) ;
void CL_DemoCheckGameUIRevealTime ( )
{
if ( s_fDemoRevealGameUITime > 0 )
{
if ( s_fDemoRevealGameUITime < Sys_FloatTime ( ) )
{
s_fDemoRevealGameUITime = - 1 ;
SCR_BeginLoadingPlaque ( ) ;
Cbuf_AddText ( " disconnect; " ) ;
CCommand args ;
CL_CheckToDisplayStartupMenus ( args ) ;
s_fDemoPlayMusicTime = Sys_FloatTime ( ) + 1.0 ;
}
}
if ( s_fDemoPlayMusicTime > 0 )
{
V_CheckGamma ( ) ;
V_RenderVGuiOnly ( ) ;
if ( s_fDemoPlayMusicTime < Sys_FloatTime ( ) )
{
s_fDemoPlayMusicTime = - 1 ;
EngineVGui ( ) - > ActivateGameUI ( ) ;
if ( CL_IsHL2Demo ( ) )
{
if ( s_bIsRavenHolmn )
{
Cbuf_AddText ( " play music/ravenholm_1.mp3; " ) ;
}
else
{
EngineVGui ( ) - > ShowNewGameDialog ( 6 ) ; // bring up the new game dialog in game UI
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: setup a debug string that is uploaded on crash
//----------------------------------------------------------------------------
extern bool g_bV3SteamInterface ;
char g_minidumpinfo [ 4096 ] = { 0 } ;
PAGED_POOL_INFO_t g_pagedpoolinfo = { 0 } ;
void DisplaySystemVersion ( char * osversion , int maxlen ) ;
void CL_SetPagedPoolInfo ( )
{
if ( IsX360 ( ) )
return ;
# if !defined( _X360 ) && !defined(NO_STEAM) && !defined(SWDS)
Plat_GetPagedPoolInfo ( & g_pagedpoolinfo ) ;
# endif
}
void CL_SetSteamCrashComment ( )
{
if ( IsX360 ( ) )
return ;
char map [ 80 ] ;
char videoinfo [ 2048 ] ;
char misc [ 256 ] ;
char driverinfo [ 2048 ] ;
char osversion [ 256 ] ;
map [ 0 ] = 0 ;
driverinfo [ 0 ] = 0 ;
videoinfo [ 0 ] = 0 ;
misc [ 0 ] = 0 ;
osversion [ 0 ] = 0 ;
if ( host_state . worldmodel )
{
CL_SetupMapName ( modelloader - > GetName ( host_state . worldmodel ) , map , sizeof ( map ) ) ;
}
DisplaySystemVersion ( osversion , sizeof ( osversion ) ) ;
MaterialAdapterInfo_t info ;
materials - > GetDisplayAdapterInfo ( materials - > GetCurrentAdapter ( ) , info ) ;
const char * dxlevel = " Unk " ;
int nDxLevel = g_pMaterialSystemHardwareConfig - > GetDXSupportLevel ( ) ;
if ( g_pMaterialSystemHardwareConfig )
{
dxlevel = COM_DXLevelToString ( nDxLevel ) ;
}
// Make a string out of the high part and low parts of driver version
char szDXDriverVersion [ 64 ] ;
Q_snprintf ( szDXDriverVersion , sizeof ( szDXDriverVersion ) , " %ld.%ld.%ld.%ld " ,
( long ) ( info . m_nDriverVersionHigh > > 16 ) ,
( long ) ( info . m_nDriverVersionHigh & 0xffff ) ,
( long ) ( info . m_nDriverVersionLow > > 16 ) ,
( long ) ( info . m_nDriverVersionLow & 0xffff ) ) ;
Q_snprintf ( driverinfo , sizeof ( driverinfo ) , " Driver Name: %s \n Driver Version: %s \n VendorId / DeviceId: 0x%x / 0x%x \n SubSystem / Rev: 0x%x / 0x%x \n DXLevel: %s [%d] \n Vid: %i x %i " ,
info . m_pDriverName ,
szDXDriverVersion ,
info . m_VendorID ,
info . m_DeviceID ,
info . m_SubSysID ,
info . m_Revision ,
dxlevel ? dxlevel : " Unk " , nDxLevel ,
videomode - > GetModeWidth ( ) , videomode - > GetModeHeight ( ) ) ;
ConVarRef mat_picmip ( " mat_picmip " ) ;
ConVarRef mat_forceaniso ( " mat_forceaniso " ) ;
ConVarRef mat_trilinear ( " mat_trilinear " ) ;
ConVarRef mat_antialias ( " mat_antialias " ) ;
ConVarRef mat_aaquality ( " mat_aaquality " ) ;
ConVarRef r_shadowrendertotexture ( " r_shadowrendertotexture " ) ;
ConVarRef r_flashlightdepthtexture ( " r_flashlightdepthtexture " ) ;
# ifndef _X360
ConVarRef r_waterforceexpensive ( " r_waterforceexpensive " ) ;
# endif
ConVarRef r_waterforcereflectentities ( " r_waterforcereflectentities " ) ;
ConVarRef mat_vsync ( " mat_vsync " ) ;
ConVarRef r_rootlod ( " r_rootlod " ) ;
ConVarRef mat_reducefillrate ( " mat_reducefillrate " ) ;
ConVarRef mat_motion_blur_enabled ( " mat_motion_blur_enabled " ) ;
ConVarRef mat_queue_mode ( " mat_queue_mode " ) ;
# ifdef _X360
Q_snprintf ( videoinfo , sizeof ( videoinfo ) , " picmip: %i forceansio: %i trilinear: %i antialias: %i vsync: %i rootlod: %i reducefillrate: %i \n " \
" shadowrendertotexture: %i r_flashlightdepthtexture %i waterforcereflectentities: %i mat_motion_blur_enabled: %i " ,
mat_picmip . GetInt ( ) , mat_forceaniso . GetInt ( ) , mat_trilinear . GetInt ( ) , mat_antialias . GetInt ( ) , mat_aaquality . GetInt ( ) ,
mat_vsync . GetInt ( ) , r_rootlod . GetInt ( ) , mat_reducefillrate . GetInt ( ) ,
r_shadowrendertotexture . GetInt ( ) , r_flashlightdepthtexture . GetInt ( ) ,
r_waterforcereflectentities . GetInt ( ) ,
mat_motion_blur_enabled . GetInt ( ) ) ;
# else
Q_snprintf ( videoinfo , sizeof ( videoinfo ) , " picmip: %i forceansio: %i trilinear: %i antialias: %i vsync: %i rootlod: %i reducefillrate: %i \n " \
" shadowrendertotexture: %i r_flashlightdepthtexture %i waterforceexpensive: %i waterforcereflectentities: %i mat_motion_blur_enabled: %i mat_queue_mode %i " ,
mat_picmip . GetInt ( ) , mat_forceaniso . GetInt ( ) , mat_trilinear . GetInt ( ) , mat_antialias . GetInt ( ) ,
mat_vsync . GetInt ( ) , r_rootlod . GetInt ( ) , mat_reducefillrate . GetInt ( ) ,
r_shadowrendertotexture . GetInt ( ) , r_flashlightdepthtexture . GetInt ( ) ,
r_waterforceexpensive . GetInt ( ) , r_waterforcereflectentities . GetInt ( ) ,
mat_motion_blur_enabled . GetInt ( ) , mat_queue_mode . GetInt ( ) ) ;
# endif
int latency = 0 ;
if ( cl . m_NetChannel )
{
latency = ( int ) ( 1000.0f * cl . m_NetChannel - > GetAvgLatency ( FLOW_OUTGOING ) ) ;
}
Q_snprintf ( misc , sizeof ( misc ) , " skill:%i rate %i update %i cmd %i latency %i msec " ,
skill . GetInt ( ) ,
cl_rate - > GetInt ( ) ,
( int ) cl_updaterate - > GetFloat ( ) ,
( int ) cl_cmdrate - > GetFloat ( ) ,
latency
) ;
const char * pNetChannel = " Not Connected " ;
if ( cl . m_NetChannel )
{
pNetChannel = cl . m_NetChannel - > GetRemoteAddress ( ) . ToString ( ) ;
}
CL_SetPagedPoolInfo ( ) ;
Q_snprintf ( g_minidumpinfo , sizeof ( g_minidumpinfo ) ,
" Map: %s \n " \
" Game: %s \n " \
" Build: %i \n " \
" Misc: %s \n " \
" Net: %s \n " \
" cmdline:%s \n " \
" driver: %s \n " \
" video: %s \n " \
" OS: %s \n " ,
map , com_gamedir , build_number ( ) , misc , pNetChannel , CommandLine ( ) - > GetCmdLine ( ) , driverinfo , videoinfo , osversion ) ;
char full [ 4096 ] ;
Q_snprintf ( full , sizeof ( full ) , " %sPP PAGES: used: %d, free %d \n " , g_minidumpinfo , ( int ) g_pagedpoolinfo . numPagesUsed , ( int ) g_pagedpoolinfo . numPagesFree ) ;
# ifndef NO_STEAM
SteamAPI_SetMiniDumpComment ( full ) ;
# endif
}
//
// register commands
//
static ConCommand startupmenu ( " startupmenu " , & CL_CheckToDisplayStartupMenus , " Opens initial menu screen and loads the background bsp, but only if no other level is being loaded, and we're not in developer mode. " ) ;
ConVar cl_language ( " cl_language " , " english " , FCVAR_USERINFO , " Language (from HKCU \\ Software \\ Valve \\ Steam \\ Language) " ) ;
void CL_InitLanguageCvar ( )
{
if ( Steam3Client ( ) . SteamApps ( ) )
{
cl_language . SetValue ( Steam3Client ( ) . SteamApps ( ) - > GetCurrentGameLanguage ( ) ) ;
}
else
{
char * szLang = getenv ( " LANG " ) ;
if ( CommandLine ( ) - > CheckParm ( " -language " ) )
cl_language . SetValue ( CommandLine ( ) - > ParmValue ( " -language " , " english " ) ) ;
else if ( szLang )
{
ELanguage lang = PchLanguageICUCodeToELanguage ( szLang , k_Lang_English ) ;
char * szShortLang = GetLanguageShortName ( lang ) ;
cl_language . SetValue ( szShortLang ) ;
}
else
cl_language . SetValue ( " english " ) ;
}
}
void CL_ChangeCloudSettingsCvar ( IConVar * var , const char * pOldValue , float flOldValue ) ;
ConVar cl_cloud_settings ( " cl_cloud_settings " , " 1 " , FCVAR_HIDDEN , " Cloud enabled from (from HKCU \\ Software \\ Valve \\ Steam \\ Apps \\ appid \\ Cloud) " , CL_ChangeCloudSettingsCvar ) ;
void CL_ChangeCloudSettingsCvar ( IConVar * var , const char * pOldValue , float flOldValue )
{
// !! bug do i need to do something linux-wise here.
if ( IsPC ( ) & & Steam3Client ( ) . SteamRemoteStorage ( ) )
{
ConVarRef ref ( var - > GetName ( ) ) ;
Steam3Client ( ) . SteamRemoteStorage ( ) - > SetCloudEnabledForApp ( ref . GetBool ( ) ) ;
if ( cl_cloud_settings . GetInt ( ) = = STEAMREMOTESTORAGE_CLOUD_ON & & flOldValue = = STEAMREMOTESTORAGE_CLOUD_OFF )
{
// If we were just turned on, get our configuration from remote storage.
engineClient - > ReadConfiguration ( false ) ;
engineClient - > ClientCmd_Unrestricted ( " refresh_options_dialog " ) ;
}
}
}
void CL_InitCloudSettingsCvar ( )
{
if ( IsPC ( ) & & Steam3Client ( ) . SteamRemoteStorage ( ) )
{
int iCloudSettings = STEAMREMOTESTORAGE_CLOUD_OFF ;
if ( Steam3Client ( ) . SteamRemoteStorage ( ) - > IsCloudEnabledForApp ( ) )
iCloudSettings = STEAMREMOTESTORAGE_CLOUD_ON ;
cl_cloud_settings . SetValue ( iCloudSettings ) ;
}
else
{
// If not on PC or steam not available, set to 0 to make sure no replication occurs or is attempted
cl_cloud_settings . SetValue ( STEAMREMOTESTORAGE_CLOUD_OFF ) ;
}
}
/*
= = = = = = = = = = = = = = = = =
CL_Init
= = = = = = = = = = = = = = = = =
*/
void CL_Init ( void )
{
cl . Clear ( ) ;
CL_InitLanguageCvar ( ) ;
CL_InitCloudSettingsCvar ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CL_Shutdown ( void )
{
}
CON_COMMAND_F ( cl_fullupdate , " Forces the server to send a full update packet " , FCVAR_CHEAT )
{
cl . ForceFullUpdate ( ) ;
}
# ifdef STAGING_ONLY
CON_COMMAND ( cl_download , " Downloads a file from server. " )
{
if ( args . ArgC ( ) ! = 2 )
return ;
if ( ! cl . m_NetChannel )
return ;
cl . m_NetChannel - > RequestFile ( args [ 1 ] ) ; // just for testing stuff
}
# endif // STAGING_ONLY
CON_COMMAND_F ( setinfo , " Adds a new user info value " , FCVAR_CLIENTCMD_CAN_EXECUTE )
{
if ( args . ArgC ( ) ! = 3 )
{
Msg ( " Syntax: setinfo <key> <value> \n " ) ;
return ;
}
const char * name = args [ 1 ] ;
const char * value = args [ 2 ] ;
// Prevent players manually changing their name (their Steam account provides it now)
if ( Q_stricmp ( name , " name " ) = = 0 )
return ;
// Discard any convar change request if contains funky characters
bool bFunky = false ;
for ( const char * s = name ; * s ! = ' \0 ' ; + + s )
{
if ( ! V_isalnum ( * s ) & & * s ! = ' _ ' )
{
bFunky = true ;
break ;
}
}
if ( bFunky )
{
Msg ( " Ignoring convar change request for variable '%s', which contains invalid character(s) \n " , name ) ;
return ;
}
ConCommandBase * pCommand = g_pCVar - > FindCommandBase ( name ) ;
ConVarRef sv_cheats ( " sv_cheats " ) ;
if ( pCommand )
{
if ( pCommand - > IsCommand ( ) )
{
Msg ( " Name %s is already registered as console command \n " , name ) ;
return ;
}
if ( ! pCommand - > IsFlagSet ( FCVAR_USERINFO ) )
{
Msg ( " Convar %s is already registered but not as user info value \n " , name ) ;
return ;
}
if ( pCommand - > IsFlagSet ( FCVAR_NOT_CONNECTED ) )
{
# ifndef DEDICATED
// Connected to server?
if ( cl . IsConnected ( ) )
{
extern IBaseClientDLL * g_ClientDLL ;
if ( pCommand - > IsFlagSet ( FCVAR_USERINFO ) & & g_ClientDLL & & g_ClientDLL - > IsConnectedUserInfoChangeAllowed ( NULL ) )
{
// Client.dll is allowing the convar change
}
else
{
ConMsg ( " Can't change %s when playing, disconnect from the server or switch team to spectators \n " , pCommand - > GetName ( ) ) ;
return ;
}
}
# endif
}
if ( IsPC ( ) )
{
# if !defined(NO_STEAM)
EUniverse eUniverse = GetSteamUniverse ( ) ;
if ( ( ( eUniverse ! = k_EUniverseBeta ) & & ( eUniverse ! = k_EUniverseDev ) ) & & pCommand - > IsFlagSet ( FCVAR_DEVELOPMENTONLY ) )
return ;
# endif
}
if ( pCommand - > IsFlagSet ( FCVAR_CHEAT ) & & sv_cheats . GetBool ( ) = = 0 )
{
Msg ( " Convar %s is marked as cheat and cheats are off \n " , name ) ;
return ;
}
}
else
{
// cvar not found, create it now
char * pszString = V_strdup ( name ) ;
pCommand = new ConVar ( pszString , " " , FCVAR_USERINFO , " Custom user info value " ) ;
}
ConVar * pConVar = ( ConVar * ) pCommand ;
pConVar - > SetValue ( value ) ;
if ( cl . IsConnected ( ) )
{
// send changed cvar to server
NET_SetConVar convar ( name , value ) ;
cl . m_NetChannel - > SendNetMsg ( convar ) ;
}
}
CON_COMMAND ( cl_precacheinfo , " Show precache info (client). " )
{
if ( args . ArgC ( ) = = 2 )
{
cl . DumpPrecacheStats ( args [ 1 ] ) ;
return ;
}
// Show all data
cl . DumpPrecacheStats ( MODEL_PRECACHE_TABLENAME ) ;
cl . DumpPrecacheStats ( DECAL_PRECACHE_TABLENAME ) ;
cl . DumpPrecacheStats ( SOUND_PRECACHE_TABLENAME ) ;
cl . DumpPrecacheStats ( GENERIC_PRECACHE_TABLENAME ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *object -
// stringTable -
// stringNumber -
// *newString -
// *newData -
//-----------------------------------------------------------------------------
void Callback_ModelChanged ( void * object , INetworkStringTable * stringTable , int stringNumber , const char * newString , const void * newData )
{
if ( stringTable = = cl . m_pModelPrecacheTable )
{
// Index 0 is always NULL, just ignore it
// Index 1 == the world, don't
if ( stringNumber > 1 )
{
// DevMsg( "Preloading model %s\n", newString );
cl . SetModel ( stringNumber ) ;
}
}
else
{
Assert ( 0 ) ; // Callback_*Changed called with wrong stringtable
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *object -
// stringTable -
// stringNumber -
// *newString -
// *newData -
//-----------------------------------------------------------------------------
void Callback_GenericChanged ( void * object , INetworkStringTable * stringTable , int stringNumber , const char * newString , const void * newData )
{
if ( stringTable = = cl . m_pGenericPrecacheTable )
{
// Index 0 is always NULL, just ignore it
if ( stringNumber > = 1 )
{
cl . SetGeneric ( stringNumber ) ;
}
}
else
{
Assert ( 0 ) ; // Callback_*Changed called with wrong stringtable
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *object -
// stringTable -
// stringNumber -
// *newString -
// *newData -
//-----------------------------------------------------------------------------
void Callback_SoundChanged ( void * object , INetworkStringTable * stringTable , int stringNumber , const char * newString , const void * newData )
{
if ( stringTable = = cl . m_pSoundPrecacheTable )
{
// Index 0 is always NULL, just ignore it
if ( stringNumber > = 1 )
{
cl . SetSound ( stringNumber ) ;
}
}
else
{
Assert ( 0 ) ; // Callback_*Changed called with wrong stringtable
}
}
void Callback_DecalChanged ( void * object , INetworkStringTable * stringTable , int stringNumber , const char * newString , const void * newData )
{
if ( stringTable = = cl . m_pDecalPrecacheTable )
{
cl . SetDecal ( stringNumber ) ;
}
else
{
Assert ( 0 ) ; // Callback_*Changed called with wrong stringtable
}
}
void Callback_InstanceBaselineChanged ( void * object , INetworkStringTable * stringTable , int stringNumber , const char * newString , const void * newData )
{
Assert ( stringTable = = cl . m_pInstanceBaselineTable ) ;
// cl.UpdateInstanceBaseline( stringNumber );
}
void Callback_UserInfoChanged ( void * object , INetworkStringTable * stringTable , int stringNumber , const char * newString , const void * newData )
{
Assert ( stringTable = = cl . m_pUserInfoTable ) ;
// stringnumber == player slot
player_info_t * player = ( player_info_t * ) newData ;
if ( ! player )
return ; // player left the game
// request custom user files if necessary
for ( int i = 0 ; i < MAX_CUSTOM_FILES ; i + + )
{
cl . CheckOthersCustomFile ( player - > customFiles [ i ] ) ;
}
// fire local client event game event
IGameEvent * event = g_GameEventManager . CreateEvent ( " player_info " ) ;
if ( event )
{
event - > SetInt ( " userid " , player - > userID ) ;
event - > SetInt ( " friendsid " , player - > friendsID ) ;
event - > SetInt ( " index " , stringNumber ) ;
event - > SetString ( " name " , player - > name ) ;
event - > SetString ( " networkid " , player - > guid ) ;
event - > SetBool ( " bot " , player - > fakeplayer ) ;
g_GameEventManager . FireEventClientSide ( event ) ;
}
}
void Callback_DynamicModelsChanged ( void * object , INetworkStringTable * stringTable , int stringNumber , const char * newString , const void * newData )
{
# ifndef SWDS
extern IVModelInfoClient * modelinfoclient ;
if ( modelinfoclient )
{
modelinfoclient - > OnDynamicModelsStringTableChange ( stringNumber , newString , newData ) ;
}
# endif
}
void CL_HookClientStringTables ( )
{
// install hooks
int numTables = cl . m_StringTableContainer - > GetNumTables ( ) ;
for ( int i = 0 ; i < numTables ; i + + )
{
// iterate through server tables
CNetworkStringTable * pTable =
( CNetworkStringTable * ) cl . m_StringTableContainer - > GetTable ( i ) ;
if ( ! pTable )
continue ;
cl . HookClientStringTable ( pTable - > GetTableName ( ) ) ;
}
}
// Installs the all, and invokes cb for all existing items
void CL_InstallAndInvokeClientStringTableCallbacks ( )
{
// install hooks
int numTables = cl . m_StringTableContainer - > GetNumTables ( ) ;
for ( int i = 0 ; i < numTables ; i + + )
{
// iterate through server tables
CNetworkStringTable * pTable =
( CNetworkStringTable * ) cl . m_StringTableContainer - > GetTable ( i ) ;
if ( ! pTable )
continue ;
pfnStringChanged pOldFunction = pTable - > GetCallback ( ) ;
cl . InstallStringTableCallback ( pTable - > GetTableName ( ) ) ;
pfnStringChanged pNewFunction = pTable - > GetCallback ( ) ;
if ( ! pNewFunction )
continue ;
// We already had it installed (e.g., from client .dll) so all of the callbacks have been called and don't need a second dose
if ( pNewFunction = = pOldFunction )
continue ;
for ( int j = 0 ; j < pTable - > GetNumStrings ( ) ; + + j )
{
int userDataSize ;
const void * pUserData = pTable - > GetStringUserData ( j , & userDataSize ) ;
( * pNewFunction ) ( NULL , pTable , j , pTable - > GetString ( j ) , pUserData ) ;
}
}
}
// Singleton client state
CClientState cl ;