css_enhanced_waf/game/client/cdll_client_int.cpp
unknown d03c92b6f0 We have now better angle update
Since it was previously based on old frametime,
you would see your angle changes from previous frametime,
not the current one...
It's now fixed.
2024-09-12 22:33:24 +02:00

2677 lines
79 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include <crtmemdebug.h>
#include "vgui_int.h"
#include "clientmode.h"
#include "iinput.h"
#include "iviewrender.h"
#include "ivieweffects.h"
#include "ivmodemanager.h"
#include "prediction.h"
#include "clientsideeffects.h"
#include "particlemgr.h"
#include "steam/steam_api.h"
#include "initializer.h"
#include "smoke_fog_overlay.h"
#include "view.h"
#include "ienginevgui.h"
#include "iefx.h"
#include "enginesprite.h"
#include "networkstringtable_clientdll.h"
#include "voice_status.h"
#include "filesystem.h"
#include "c_te_legacytempents.h"
#include "c_rope.h"
#include "engine/ishadowmgr.h"
#include "engine/IStaticPropMgr.h"
#include "hud_basechat.h"
#include "hud_crosshair.h"
#include "view_shared.h"
#include "env_wind_shared.h"
#include "detailobjectsystem.h"
#include "clienteffectprecachesystem.h"
#include "soundenvelope.h"
#include "c_basetempentity.h"
#include "materialsystem/imaterialsystemstub.h"
#include "VGuiMatSurface/IMatSystemSurface.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "c_soundscape.h"
#include "engine/ivdebugoverlay.h"
#include "vguicenterprint.h"
#include "iviewrender_beams.h"
#include "tier0/vprof.h"
#include "engine/IEngineTrace.h"
#include "engine/ivmodelinfo.h"
#include "physics.h"
#include "usermessages.h"
#include "gamestringpool.h"
#include "c_user_message_register.h"
#include "IGameUIFuncs.h"
#include "saverestoretypes.h"
#include "saverestore.h"
#include "physics_saverestore.h"
#include "igameevents.h"
#include "datacache/idatacache.h"
#include "datacache/imdlcache.h"
#include "kbutton.h"
#include "tier0/icommandline.h"
#include "gamerules_register.h"
#include "vgui_controls/AnimationController.h"
#include "bitmap/tgawriter.h"
#include "c_world.h"
#include "perfvisualbenchmark.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "hud_closecaption.h"
#include "colorcorrectionmgr.h"
#include "physpropclientside.h"
#include "panelmetaclassmgr.h"
#include "c_vguiscreen.h"
#include "imessagechars.h"
#include "game/client/IGameClientExports.h"
#include "client_factorylist.h"
#include "ragdoll_shared.h"
#include "rendertexture.h"
#include "view_scene.h"
#include "iclientmode.h"
#include "con_nprint.h"
#include "inputsystem/iinputsystem.h"
#include "appframework/IAppSystemGroup.h"
#include "scenefilecache/ISceneFileCache.h"
#include "tier2/tier2dm.h"
#include "tier3/tier3.h"
#include "ihudlcd.h"
#include "toolframework_client.h"
#include "hltvcamera.h"
#if defined( REPLAY_ENABLED )
#include "replay/replaycamera.h"
#include "replay/replay_ragdoll.h"
#include "qlimits.h"
#include "replay/replay.h"
#include "replay/ireplaysystem.h"
#include "replay/iclientreplay.h"
#include "replay/ienginereplay.h"
#include "replay/ireplaymanager.h"
#include "replay/ireplayscreenshotmanager.h"
#include "replay/iclientreplaycontext.h"
#include "replay/vgui/replayconfirmquitdlg.h"
#include "replay/vgui/replaybrowsermainpanel.h"
#include "replay/vgui/replayinputpanel.h"
#include "replay/vgui/replayperformanceeditor.h"
#endif
#include "vgui/ILocalize.h"
#include "vgui/IVGui.h"
#include "ixboxsystem.h"
#include "ipresence.h"
#include "engine/imatchmaking.h"
#include "cdll_bounded_cvars.h"
#include "matsys_controls/matsyscontrols.h"
#include "gamestats.h"
#include "particle_parse.h"
#if defined( TF_CLIENT_DLL )
#include "rtime.h"
#include "tf_hud_disconnect_prompt.h"
#include "../engine/audio/public/sound.h"
#include "tf_shared_content_manager.h"
#endif
#include "clientsteamcontext.h"
#include "renamed_recvtable_compat.h"
#include "mouthinfo.h"
#include "sourcevr/isourcevirtualreality.h"
#include "client_virtualreality.h"
#include "mumble.h"
// NVNT includes
#include "hud_macros.h"
#include "haptics/ihaptics.h"
#include "haptics/haptic_utils.h"
#include "haptics/haptic_msgs.h"
#if defined( TF_CLIENT_DLL )
#include "abuse_report.h"
#endif
#ifdef USES_ECON_ITEMS
#include "econ_item_system.h"
#endif // USES_ECON_ITEMS
#if defined( TF_CLIENT_DLL )
#include "econ/tool_items/custom_texture_cache.h"
#endif
#ifdef WORKSHOP_IMPORT_ENABLED
#include "fbxsystem/fbxsystem.h"
#endif
#include "touch.h"
extern vgui::IInputInternal *g_InputInternal;
//=============================================================================
// HPE_BEGIN
// [dwenger] Necessary for stats display
//=============================================================================
#include "achievements_and_stats_interface.h"
//=============================================================================
// HPE_END
//=============================================================================
#ifdef PORTAL
#include "PortalRender.h"
#endif
#ifdef SIXENSE
#include "sixense/in_sixense.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern IClientMode *GetClientModeNormal();
// IF YOU ADD AN INTERFACE, EXTERN IT IN THE HEADER FILE.
IVEngineClient *engine = NULL;
IVModelRender *modelrender = NULL;
IVEfx *effects = NULL;
IVRenderView *render = NULL;
IVDebugOverlay *debugoverlay = NULL;
IMaterialSystemStub *materials_stub = NULL;
IDataCache *datacache = NULL;
IVModelInfoClient *modelinfo = NULL;
IEngineVGui *enginevgui = NULL;
INetworkStringTableContainer *networkstringtable = NULL;
ISpatialPartition* partition = NULL;
IFileSystem *filesystem = NULL;
IShadowMgr *shadowmgr = NULL;
IStaticPropMgrClient *staticpropmgr = NULL;
IEngineSound *enginesound = NULL;
IUniformRandomStream *random = NULL;
static CGaussianRandomStream s_GaussianRandomStream;
CGaussianRandomStream *randomgaussian = &s_GaussianRandomStream;
ISharedGameRules *sharedgamerules = NULL;
IEngineTrace *enginetrace = NULL;
IGameUIFuncs *gameuifuncs = NULL;
IGameEventManager2 *gameeventmanager = NULL;
ISoundEmitterSystemBase *soundemitterbase = NULL;
IInputSystem *inputsystem = NULL;
ISceneFileCache *scenefilecache = NULL;
IXboxSystem *xboxsystem = NULL; // Xbox 360 only
IMatchmaking *matchmaking = NULL;
IUploadGameStats *gamestatsuploader = NULL;
IClientReplayContext *g_pClientReplayContext = NULL;
#if defined( REPLAY_ENABLED )
IReplayManager *g_pReplayManager = NULL;
IReplayMovieManager *g_pReplayMovieManager = NULL;
IReplayScreenshotManager *g_pReplayScreenshotManager = NULL;
IReplayPerformanceManager *g_pReplayPerformanceManager = NULL;
IReplayPerformanceController *g_pReplayPerformanceController = NULL;
IEngineReplay *g_pEngineReplay = NULL;
IEngineClientReplay *g_pEngineClientReplay = NULL;
IReplaySystem *g_pReplay = NULL;
#endif
IHaptics* haptics = NULL;// NVNT haptics system interface singleton
//=============================================================================
// HPE_BEGIN
// [dwenger] Necessary for stats display
//=============================================================================
AchievementsAndStatsInterface* g_pAchievementsAndStatsInterface = NULL;
//=============================================================================
// HPE_END
//=============================================================================
IGameSystem *SoundEmitterSystem();
IGameSystem *ToolFrameworkClientSystem();
// Engine player info, no game related infos here
BEGIN_BYTESWAP_DATADESC( player_info_s )
DEFINE_ARRAY( name, FIELD_CHARACTER, MAX_PLAYER_NAME_LENGTH ),
DEFINE_FIELD( userID, FIELD_INTEGER ),
DEFINE_ARRAY( guid, FIELD_CHARACTER, SIGNED_GUID_LEN + 1 ),
DEFINE_FIELD( friendsID, FIELD_INTEGER ),
DEFINE_ARRAY( friendsName, FIELD_CHARACTER, MAX_PLAYER_NAME_LENGTH ),
DEFINE_FIELD( fakeplayer, FIELD_BOOLEAN ),
DEFINE_FIELD( ishltv, FIELD_BOOLEAN ),
#if defined( REPLAY_ENABLED )
DEFINE_FIELD( isreplay, FIELD_BOOLEAN ),
#endif
DEFINE_ARRAY( customFiles, FIELD_INTEGER, MAX_CUSTOM_FILES ),
DEFINE_FIELD( filesDownloaded, FIELD_INTEGER ),
END_BYTESWAP_DATADESC()
static bool g_bRequestCacheUsedMaterials = false;
void RequestCacheUsedMaterials()
{
g_bRequestCacheUsedMaterials = true;
}
void ProcessCacheUsedMaterials()
{
if ( !g_bRequestCacheUsedMaterials )
return;
g_bRequestCacheUsedMaterials = false;
if ( materials )
{
materials->CacheUsedMaterials();
}
}
// String tables
INetworkStringTable *g_pStringTableParticleEffectNames = NULL;
INetworkStringTable *g_StringTableEffectDispatch = NULL;
INetworkStringTable *g_StringTableVguiScreen = NULL;
INetworkStringTable *g_pStringTableMaterials = NULL;
INetworkStringTable *g_pStringTableInfoPanel = NULL;
INetworkStringTable *g_pStringTableClientSideChoreoScenes = NULL;
INetworkStringTable *g_pStringTableServerMapCycle = NULL;
#ifdef TF_CLIENT_DLL
INetworkStringTable *g_pStringTableServerPopFiles = NULL;
INetworkStringTable *g_pStringTableServerMapCycleMvM = NULL;
#endif
static CGlobalVarsBase dummyvars( true );
// So stuff that might reference gpGlobals during DLL initialization won't have a NULL pointer.
// Once the engine calls Init on this DLL, this pointer gets assigned to the shared data in the engine
CGlobalVarsBase *gpGlobals = &dummyvars;
class CHudChat;
class CViewRender;
extern CViewRender g_DefaultViewRender;
extern void StopAllRumbleEffects( void );
static C_BaseEntityClassList *s_pClassLists = NULL;
C_BaseEntityClassList::C_BaseEntityClassList()
{
m_pNextClassList = s_pClassLists;
s_pClassLists = this;
}
C_BaseEntityClassList::~C_BaseEntityClassList()
{
}
// Any entities that want an OnDataChanged during simulation register for it here.
class CDataChangedEvent
{
public:
CDataChangedEvent() = default;
CDataChangedEvent( IClientNetworkable *ent, DataUpdateType_t updateType, int *pStoredEvent )
{
m_pEntity = ent;
m_UpdateType = updateType;
m_pStoredEvent = pStoredEvent;
}
IClientNetworkable *m_pEntity;
DataUpdateType_t m_UpdateType;
int *m_pStoredEvent;
};
ISaveRestoreBlockHandler *GetEntitySaveRestoreBlockHandler();
ISaveRestoreBlockHandler *GetViewEffectsRestoreBlockHandler();
CUtlLinkedList<CDataChangedEvent, unsigned short> g_DataChangedEvents;
ClientFrameStage_t g_CurFrameStage = FRAME_UNDEFINED;
class IMoveHelper;
void DispatchHudText( const char *pszName );
static ConVar s_CV_ShowParticleCounts("showparticlecounts", "0", 0, "Display number of particles drawn per frame");
static ConVar s_cl_team("cl_team", "default", FCVAR_USERINFO|FCVAR_ARCHIVE, "Default team when joining a game");
static ConVar s_cl_class("cl_class", "default", FCVAR_USERINFO|FCVAR_ARCHIVE, "Default class when joining a game");
#ifdef HL1MP_CLIENT_DLL
static ConVar s_cl_load_hl1_content("cl_load_hl1_content", "0", FCVAR_ARCHIVE, "Mount the content from Half-Life: Source if possible");
#endif
// Physics system
bool g_bLevelInitialized;
bool g_bTextMode = false;
class IClientPurchaseInterfaceV2 *g_pClientPurchaseInterface = (class IClientPurchaseInterfaceV2 *)(&g_bTextMode + 156);
static ConVar *g_pcv_ThreadMode = NULL;
//-----------------------------------------------------------------------------
// Purpose: interface for gameui to modify voice bans
//-----------------------------------------------------------------------------
class CGameClientExports : public IGameClientExports
{
public:
// ingame voice manipulation
bool IsPlayerGameVoiceMuted(int playerIndex)
{
return GetClientVoiceMgr()->IsPlayerBlocked(playerIndex);
}
void MutePlayerGameVoice(int playerIndex)
{
GetClientVoiceMgr()->SetPlayerBlockedState(playerIndex, true);
}
void UnmutePlayerGameVoice(int playerIndex)
{
GetClientVoiceMgr()->SetPlayerBlockedState(playerIndex, false);
}
void OnGameUIActivated( void )
{
IGameEvent *event = gameeventmanager->CreateEvent( "gameui_activated" );
if ( event )
{
gameeventmanager->FireEventClientSide( event );
}
}
void OnGameUIHidden( void )
{
IGameEvent *event = gameeventmanager->CreateEvent( "gameui_hidden" );
if ( event )
{
gameeventmanager->FireEventClientSide( event );
}
}
//=============================================================================
// HPE_BEGIN
// [dwenger] Necessary for stats display
//=============================================================================
void CreateAchievementsPanel( vgui::Panel* pParent )
{
if (g_pAchievementsAndStatsInterface)
{
g_pAchievementsAndStatsInterface->CreatePanel( pParent );
}
}
void DisplayAchievementPanel()
{
if (g_pAchievementsAndStatsInterface)
{
g_pAchievementsAndStatsInterface->DisplayPanel();
}
}
void ShutdownAchievementPanel()
{
if (g_pAchievementsAndStatsInterface)
{
g_pAchievementsAndStatsInterface->ReleasePanel();
}
}
int GetAchievementsPanelMinWidth( void ) const
{
if ( g_pAchievementsAndStatsInterface )
{
return g_pAchievementsAndStatsInterface->GetAchievementsPanelMinWidth();
}
return 0;
}
//=============================================================================
// HPE_END
//=============================================================================
const char *GetHolidayString()
{
return UTIL_GetActiveHolidayString();
}
};
EXPOSE_SINGLE_INTERFACE( CGameClientExports, IGameClientExports, GAMECLIENTEXPORTS_INTERFACE_VERSION );
class CClientDLLSharedAppSystems : public IClientDLLSharedAppSystems
{
public:
CClientDLLSharedAppSystems()
{
AddAppSystem( "soundemittersystem" DLL_EXT_STRING, SOUNDEMITTERSYSTEM_INTERFACE_VERSION );
AddAppSystem( "scenefilecache" DLL_EXT_STRING, SCENE_FILE_CACHE_INTERFACE_VERSION );
}
virtual int Count()
{
return m_Systems.Count();
}
virtual char const *GetDllName( int idx )
{
return m_Systems[ idx ].m_pModuleName;
}
virtual char const *GetInterfaceName( int idx )
{
return m_Systems[ idx ].m_pInterfaceName;
}
private:
void AddAppSystem( char const *moduleName, char const *interfaceName )
{
AppSystemInfo_t sys;
sys.m_pModuleName = moduleName;
sys.m_pInterfaceName = interfaceName;
m_Systems.AddToTail( sys );
}
CUtlVector< AppSystemInfo_t > m_Systems;
};
EXPOSE_SINGLE_INTERFACE( CClientDLLSharedAppSystems, IClientDLLSharedAppSystems, CLIENT_DLL_SHARED_APPSYSTEMS );
//-----------------------------------------------------------------------------
// Helper interface for voice.
//-----------------------------------------------------------------------------
class CHLVoiceStatusHelper : public IVoiceStatusHelper
{
public:
virtual void GetPlayerTextColor(int entindex, int color[3])
{
color[0] = color[1] = color[2] = 128;
}
virtual void UpdateCursorState()
{
}
virtual bool CanShowSpeakerLabels()
{
return true;
}
};
static CHLVoiceStatusHelper g_VoiceStatusHelper;
//-----------------------------------------------------------------------------
// Code to display which entities are having their bones setup each frame.
//-----------------------------------------------------------------------------
ConVar cl_ShowBoneSetupEnts( "cl_ShowBoneSetupEnts", "0", 0, "Show which entities are having their bones setup each frame." );
class CBoneSetupEnt
{
public:
char m_ModelName[128];
int m_Index;
int m_Count;
};
bool BoneSetupCompare( const CBoneSetupEnt &a, const CBoneSetupEnt &b )
{
return a.m_Index < b.m_Index;
}
CUtlRBTree<CBoneSetupEnt> g_BoneSetupEnts( BoneSetupCompare );
void TrackBoneSetupEnt( C_BaseAnimating *pEnt )
{
#ifdef _DEBUG
if ( IsRetail() )
return;
if ( !cl_ShowBoneSetupEnts.GetInt() )
return;
CBoneSetupEnt ent;
ent.m_Index = pEnt->entindex();
unsigned short i = g_BoneSetupEnts.Find( ent );
if ( i == g_BoneSetupEnts.InvalidIndex() )
{
Q_strncpy( ent.m_ModelName, modelinfo->GetModelName( pEnt->GetModel() ), sizeof( ent.m_ModelName ) );
ent.m_Count = 1;
g_BoneSetupEnts.Insert( ent );
}
else
{
g_BoneSetupEnts[i].m_Count++;
}
#endif
}
void DisplayBoneSetupEnts()
{
#ifdef _DEBUG
if ( IsRetail() )
return;
if ( !cl_ShowBoneSetupEnts.GetInt() )
return;
unsigned short i;
int nElements = 0;
for ( i=g_BoneSetupEnts.FirstInorder(); i != g_BoneSetupEnts.LastInorder(); i=g_BoneSetupEnts.NextInorder( i ) )
++nElements;
engine->Con_NPrintf( 0, "%d bone setup ents (name/count/entindex) ------------", nElements );
con_nprint_s printInfo;
printInfo.time_to_live = -1;
printInfo.fixed_width_font = true;
printInfo.color[0] = printInfo.color[1] = printInfo.color[2] = 1;
printInfo.index = 2;
for ( i=g_BoneSetupEnts.FirstInorder(); i != g_BoneSetupEnts.LastInorder(); i=g_BoneSetupEnts.NextInorder( i ) )
{
CBoneSetupEnt *pEnt = &g_BoneSetupEnts[i];
if ( pEnt->m_Count >= 3 )
{
printInfo.color[0] = 1;
printInfo.color[1] = printInfo.color[2] = 0;
}
else if ( pEnt->m_Count == 2 )
{
printInfo.color[0] = (float)200 / 255;
printInfo.color[1] = (float)220 / 255;
printInfo.color[2] = 0;
}
else
{
printInfo.color[0] = printInfo.color[0] = printInfo.color[0] = 1;
}
engine->Con_NXPrintf( &printInfo, "%25s / %3d / %3d", pEnt->m_ModelName, pEnt->m_Count, pEnt->m_Index );
printInfo.index++;
}
g_BoneSetupEnts.RemoveAll();
#endif
}
//-----------------------------------------------------------------------------
// Purpose: engine to client .dll interface
//-----------------------------------------------------------------------------
class CHLClient : public IBaseClientDLL
{
public:
CHLClient();
virtual int Init( CreateInterfaceFn appSystemFactory, CreateInterfaceFn physicsFactory, CGlobalVarsBase *pGlobals );
virtual void PostInit();
virtual void Shutdown( void );
virtual bool ReplayInit( CreateInterfaceFn fnReplayFactory );
virtual bool ReplayPostInit();
virtual void LevelInitPreEntity( const char *pMapName );
virtual void LevelInitPostEntity();
virtual void LevelShutdown( void );
virtual ClientClass *GetAllClasses( void );
virtual int HudVidInit( void );
virtual void HudProcessInput( bool bActive );
virtual void HudUpdate( bool bActive );
virtual void HudReset( void );
virtual void HudText( const char * message );
// Mouse Input Interfaces
virtual void IN_ActivateMouse( void );
virtual void IN_DeactivateMouse( void );
virtual void IN_Accumulate( void );
virtual void IN_ClearStates( void );
virtual bool IN_IsKeyDown( const char *name, bool& isdown );
virtual void IN_OnMouseWheeled( int nDelta );
// Raw signal
virtual int IN_KeyEvent( int eventcode, ButtonCode_t keynum, const char *pszCurrentBinding );
virtual void IN_SetSampleTime( float frametime );
// Create movement command
virtual void CreateMove ( int sequence_number, float input_sample_frametime, bool active );
virtual void ExtraMovementSample( int number_of_ticks_this_frame, int current_command, float frametime, bool active );
virtual bool WriteUsercmdDeltaToBuffer( bf_write *buf, int from, int to, bool isnewcommand );
virtual void EncodeUserCmdToBuffer( bf_write& buf, int slot );
virtual void DecodeUserCmdFromBuffer( bf_read& buf, int slot );
virtual void View_Render( vrect_t *rect );
virtual void RenderView( const CViewSetup &view, int nClearFlags, int whatToDraw );
virtual void View_Fade( ScreenFade_t *pSF );
virtual void SetCrosshairAngle( const QAngle& angle );
virtual void InitSprite( CEngineSprite *pSprite, const char *loadname );
virtual void ShutdownSprite( CEngineSprite *pSprite );
virtual int GetSpriteSize( void ) const;
virtual void VoiceStatus( int entindex, qboolean bTalking );
virtual void InstallStringTableCallback( const char *tableName );
virtual void FrameStageNotify( ClientFrameStage_t curStage );
virtual bool DispatchUserMessage( int msg_type, bf_read &msg_data );
// Save/restore system hooks
virtual CSaveRestoreData *SaveInit( int size );
virtual void SaveWriteFields( CSaveRestoreData *, const char *, void *, datamap_t *, typedescription_t *, int );
virtual void SaveReadFields( CSaveRestoreData *, const char *, void *, datamap_t *, typedescription_t *, int );
virtual void PreSave( CSaveRestoreData * );
virtual void Save( CSaveRestoreData * );
virtual void WriteSaveHeaders( CSaveRestoreData * );
virtual void ReadRestoreHeaders( CSaveRestoreData * );
virtual void Restore( CSaveRestoreData *, bool );
virtual void DispatchOnRestore();
virtual void WriteSaveGameScreenshot( const char *pFilename );
// Given a list of "S(wavname) S(wavname2)" tokens, look up the localized text and emit
// the appropriate close caption if running with closecaption = 1
virtual void EmitSentenceCloseCaption( char const *tokenstream );
virtual void EmitCloseCaption( char const *captionname, float duration );
virtual CStandardRecvProxies* GetStandardRecvProxies();
virtual bool CanRecordDemo( char *errorMsg, int length ) const;
virtual void OnDemoRecordStart( char const* pDemoBaseName );
virtual void OnDemoRecordStop();
virtual void OnDemoPlaybackStart( char const* pDemoBaseName );
virtual void OnDemoPlaybackStop();
virtual bool ShouldDrawDropdownConsole();
// Get client screen dimensions
virtual int GetScreenWidth();
virtual int GetScreenHeight();
// save game screenshot writing
virtual void WriteSaveGameScreenshotOfSize( const char *pFilename, int width, int height, bool bCreatePowerOf2Padded/*=false*/, bool bWriteVTF/*=false*/ );
// Gets the location of the player viewpoint
virtual bool GetPlayerView( CViewSetup &playerView );
// Matchmaking
virtual void SetupGameProperties( CUtlVector< XUSER_CONTEXT > &contexts, CUtlVector< XUSER_PROPERTY > &properties );
virtual uint GetPresenceID( const char *pIDName );
virtual const char *GetPropertyIdString( const uint id );
virtual void GetPropertyDisplayString( uint id, uint value, char *pOutput, int nBytes );
virtual void StartStatsReporting( HANDLE handle, bool bArbitrated );
virtual void InvalidateMdlCache();
virtual void ReloadFilesInList( IFileList *pFilesToReload );
// Let the client handle UI toggle - if this function returns false, the UI will toggle, otherwise it will not.
virtual bool HandleUiToggle();
// Allow the console to be shown?
virtual bool ShouldAllowConsole();
// Get renamed recv tables
virtual CRenamedRecvTableInfo *GetRenamedRecvTableInfos();
// Get the mouthinfo for the sound being played inside UI panels
virtual CMouthInfo *GetClientUIMouthInfo();
// Notify the client that a file has been received from the game server
virtual void FileReceived( const char * fileName, unsigned int transferID );
virtual const char* TranslateEffectForVisionFilter( const char *pchEffectType, const char *pchEffectName );
virtual void ClientAdjustStartSoundParams( struct StartSoundParams_t& params );
// Returns true if the disconnect command has been handled by the client
virtual bool DisconnectAttempt( void );
virtual void MarkEntitiesAsTouching( IClientEntity *e1, IClientEntity *e2 );
public:
void PrecacheMaterial( const char *pMaterialName );
virtual bool IsConnectedUserInfoChangeAllowed( IConVar *pCvar );
virtual void IN_TouchEvent( int type, int fingerId, int x, int y );
private:
void UncacheAllMaterials( );
void ResetStringTablePointers();
CUtlVector< IMaterial * > m_CachedMaterials;
};
CHLClient gHLClient;
IBaseClientDLL *clientdll = &gHLClient;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CHLClient, IBaseClientDLL, CLIENT_DLL_INTERFACE_VERSION, gHLClient );
//-----------------------------------------------------------------------------
// Precaches a material
//-----------------------------------------------------------------------------
void PrecacheMaterial( const char *pMaterialName )
{
gHLClient.PrecacheMaterial( pMaterialName );
}
//-----------------------------------------------------------------------------
// Converts a previously precached material into an index
//-----------------------------------------------------------------------------
int GetMaterialIndex( const char *pMaterialName )
{
if (pMaterialName)
{
int nIndex = g_pStringTableMaterials->FindStringIndex( pMaterialName );
Assert( nIndex >= 0 );
if (nIndex >= 0)
return nIndex;
}
// This is the invalid string index
return 0;
}
//-----------------------------------------------------------------------------
// Converts precached material indices into strings
//-----------------------------------------------------------------------------
const char *GetMaterialNameFromIndex( int nIndex )
{
if (nIndex != (g_pStringTableMaterials->GetMaxStrings() - 1))
{
return g_pStringTableMaterials->GetString( nIndex );
}
else
{
return NULL;
}
}
//-----------------------------------------------------------------------------
// Precaches a particle system
//-----------------------------------------------------------------------------
void PrecacheParticleSystem( const char *pParticleSystemName )
{
g_pStringTableParticleEffectNames->AddString( false, pParticleSystemName );
g_pParticleSystemMgr->PrecacheParticleSystem( pParticleSystemName );
}
//-----------------------------------------------------------------------------
// Converts a previously precached particle system into an index
//-----------------------------------------------------------------------------
int GetParticleSystemIndex( const char *pParticleSystemName )
{
if ( pParticleSystemName )
{
int nIndex = g_pStringTableParticleEffectNames->FindStringIndex( pParticleSystemName );
if ( nIndex != INVALID_STRING_INDEX )
return nIndex;
DevWarning("Client: Missing precache for particle system \"%s\"!\n", pParticleSystemName );
}
// This is the invalid string index
return 0;
}
//-----------------------------------------------------------------------------
// Converts precached particle system indices into strings
//-----------------------------------------------------------------------------
const char *GetParticleSystemNameFromIndex( int nIndex )
{
if ( nIndex < g_pStringTableParticleEffectNames->GetMaxStrings() )
return g_pStringTableParticleEffectNames->GetString( nIndex );
return "error";
}
//-----------------------------------------------------------------------------
// Returns true if host_thread_mode is set to non-zero (and engine is running in threaded mode)
//-----------------------------------------------------------------------------
bool IsEngineThreaded()
{
if ( g_pcv_ThreadMode )
{
return g_pcv_ThreadMode->GetBool();
}
return false;
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CHLClient::CHLClient()
{
// Kinda bogus, but the logic in the engine is too convoluted to put it there
g_bLevelInitialized = false;
}
extern IGameSystem *ViewportClientSystem();
//-----------------------------------------------------------------------------
ISourceVirtualReality *g_pSourceVR = NULL;
// Purpose: Called when the DLL is first loaded.
// Input : engineFactory -
// Output : int
//-----------------------------------------------------------------------------
int CHLClient::Init( CreateInterfaceFn appSystemFactory, CreateInterfaceFn physicsFactory, CGlobalVarsBase *pGlobals )
{
InitCRTMemDebug();
MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
#ifdef SIXENSE
g_pSixenseInput = new SixenseInput;
#endif
// Hook up global variables
gpGlobals = pGlobals;
ConnectTier1Libraries( &appSystemFactory, 1 );
ConnectTier2Libraries( &appSystemFactory, 1 );
ConnectTier3Libraries( &appSystemFactory, 1 );
#ifndef NO_STEAM
ClientSteamContext().Activate();
#endif
// We aren't happy unless we get all of our interfaces.
// please don't collapse this into one monolithic boolean expression (impossible to debug)
if ( (engine = (IVEngineClient *)appSystemFactory( VENGINE_CLIENT_INTERFACE_VERSION, NULL )) == NULL )
return false;
if ( (modelrender = (IVModelRender *)appSystemFactory( VENGINE_HUDMODEL_INTERFACE_VERSION, NULL )) == NULL )
return false;
if ( (effects = (IVEfx *)appSystemFactory( VENGINE_EFFECTS_INTERFACE_VERSION, NULL )) == NULL )
return false;
if ( (enginetrace = (IEngineTrace *)appSystemFactory( INTERFACEVERSION_ENGINETRACE_CLIENT, NULL )) == NULL )
return false;
if ( (render = (IVRenderView *)appSystemFactory( VENGINE_RENDERVIEW_INTERFACE_VERSION, NULL )) == NULL )
return false;
if ( (debugoverlay = (IVDebugOverlay *)appSystemFactory( VDEBUG_OVERLAY_INTERFACE_VERSION, NULL )) == NULL )
return false;
if ( (datacache = (IDataCache*)appSystemFactory(DATACACHE_INTERFACE_VERSION, NULL )) == NULL )
return false;
if ( !mdlcache )
return false;
if ( (modelinfo = (IVModelInfoClient *)appSystemFactory(VMODELINFO_CLIENT_INTERFACE_VERSION, NULL )) == NULL )
return false;
if ( (enginevgui = (IEngineVGui *)appSystemFactory(VENGINE_VGUI_VERSION, NULL )) == NULL )
return false;
if ( (networkstringtable = (INetworkStringTableContainer *)appSystemFactory(INTERFACENAME_NETWORKSTRINGTABLECLIENT,NULL)) == NULL )
return false;
if ( (partition = (ISpatialPartition *)appSystemFactory(INTERFACEVERSION_SPATIALPARTITION, NULL)) == NULL )
return false;
if ( (shadowmgr = (IShadowMgr *)appSystemFactory(ENGINE_SHADOWMGR_INTERFACE_VERSION, NULL)) == NULL )
return false;
if ( (staticpropmgr = (IStaticPropMgrClient *)appSystemFactory(INTERFACEVERSION_STATICPROPMGR_CLIENT, NULL)) == NULL )
return false;
if ( (enginesound = (IEngineSound *)appSystemFactory(IENGINESOUND_CLIENT_INTERFACE_VERSION, NULL)) == NULL )
return false;
if ( (filesystem = (IFileSystem *)appSystemFactory(FILESYSTEM_INTERFACE_VERSION, NULL)) == NULL )
return false;
if ( (random = (IUniformRandomStream *)appSystemFactory(VENGINE_CLIENT_RANDOM_INTERFACE_VERSION, NULL)) == NULL )
return false;
if ( (gameuifuncs = (IGameUIFuncs * )appSystemFactory( VENGINE_GAMEUIFUNCS_VERSION, NULL )) == NULL )
return false;
if ( (gameeventmanager = (IGameEventManager2 *)appSystemFactory(INTERFACEVERSION_GAMEEVENTSMANAGER2,NULL)) == NULL )
return false;
if ( (soundemitterbase = (ISoundEmitterSystemBase *)appSystemFactory(SOUNDEMITTERSYSTEM_INTERFACE_VERSION, NULL)) == NULL )
return false;
if ( (inputsystem = (IInputSystem *)appSystemFactory(INPUTSYSTEM_INTERFACE_VERSION, NULL)) == NULL )
return false;
if ( (scenefilecache = (ISceneFileCache *)appSystemFactory( SCENE_FILE_CACHE_INTERFACE_VERSION, NULL )) == NULL )
return false;
if ( IsX360() && (xboxsystem = (IXboxSystem *)appSystemFactory( XBOXSYSTEM_INTERFACE_VERSION, NULL )) == NULL )
return false;
if ( IsX360() && (matchmaking = (IMatchmaking *)appSystemFactory( VENGINE_MATCHMAKING_VERSION, NULL )) == NULL )
return false;
#ifndef _XBOX
if ( ( gamestatsuploader = (IUploadGameStats *)appSystemFactory( INTERFACEVERSION_UPLOADGAMESTATS, NULL )) == NULL )
return false;
#endif
#if defined( REPLAY_ENABLED )
if ( IsPC() && (g_pEngineReplay = (IEngineReplay *)appSystemFactory( ENGINE_REPLAY_INTERFACE_VERSION, NULL )) == NULL )
return false;
if ( IsPC() && (g_pEngineClientReplay = (IEngineClientReplay *)appSystemFactory( ENGINE_REPLAY_CLIENT_INTERFACE_VERSION, NULL )) == NULL )
return false;
#endif
if (!g_pMatSystemSurface)
return false;
#ifdef WORKSHOP_IMPORT_ENABLED
if ( !ConnectDataModel( appSystemFactory ) )
return false;
if ( InitDataModel() != INIT_OK )
return false;
InitFbx();
#endif
// it's ok if this is NULL. That just means the sourcevr.dll wasn't found
g_pSourceVR = (ISourceVirtualReality *)appSystemFactory(SOURCE_VIRTUAL_REALITY_INTERFACE_VERSION, NULL);
factorylist_t factories;
factories.appSystemFactory = appSystemFactory;
factories.physicsFactory = physicsFactory;
FactoryList_Store( factories );
// Yes, both the client and game .dlls will try to Connect, the soundemittersystem.dll will handle this gracefully
if ( !soundemitterbase->Connect( appSystemFactory ) )
{
return false;
}
if ( CommandLine()->FindParm( "-textmode" ) )
g_bTextMode = true;
if ( CommandLine()->FindParm( "-makedevshots" ) )
g_MakingDevShots = true;
// Not fatal if the material system stub isn't around.
materials_stub = (IMaterialSystemStub*)appSystemFactory( MATERIAL_SYSTEM_STUB_INTERFACE_VERSION, NULL );
if( !g_pMaterialSystemHardwareConfig )
return false;
// Hook up the gaussian random number generator
s_GaussianRandomStream.AttachToStream( random );
// Initialize the console variables.
ConVar_Register( FCVAR_CLIENTDLL );
g_pcv_ThreadMode = g_pCVar->FindVar( "host_thread_mode" );
if (!Initializer::InitializeAllObjects())
return false;
if (!ParticleMgr()->Init(MAX_TOTAL_PARTICLES, materials))
return false;
if (!VGui_Startup( appSystemFactory ))
return false;
vgui::VGui_InitMatSysInterfacesList( "ClientDLL", &appSystemFactory, 1 );
// Add the client systems.
// Client Leaf System has to be initialized first, since DetailObjectSystem uses it
IGameSystem::Add( GameStringSystem() );
IGameSystem::Add( SoundEmitterSystem() );
IGameSystem::Add( ToolFrameworkClientSystem() );
IGameSystem::Add( ClientLeafSystem() );
IGameSystem::Add( DetailObjectSystem() );
IGameSystem::Add( ViewportClientSystem() );
IGameSystem::Add( ClientEffectPrecacheSystem() );
IGameSystem::Add( g_pClientShadowMgr );
IGameSystem::Add( g_pColorCorrectionMgr ); // NOTE: This must happen prior to ClientThinkList (color correction is updated there)
IGameSystem::Add( ClientThinkList() );
IGameSystem::Add( ClientSoundscapeSystem() );
IGameSystem::Add( PerfVisualBenchmark() );
IGameSystem::Add( MumbleSystem() );
#if defined( TF_CLIENT_DLL )
IGameSystem::Add( CustomTextureToolCacheGameSystem() );
IGameSystem::Add( TFSharedContentManager() );
#endif
#if defined( TF_CLIENT_DLL )
if ( g_AbuseReportMgr != NULL )
{
IGameSystem::Add( g_AbuseReportMgr );
}
#endif
#if defined( CLIENT_DLL ) && defined( COPY_CHECK_STRESSTEST )
IGameSystem::Add( GetPredictionCopyTester() );
#endif
modemanager->Init( );
g_pClientMode->InitViewport();
gHUD.Init();
gTouch.Init();
g_pClientMode->Init();
if ( !IGameSystem::InitAllSystems() )
return false;
g_pClientMode->Enable();
if ( !view )
{
view = ( IViewRender * )&g_DefaultViewRender;
}
view->Init();
vieweffects->Init();
C_BaseTempEntity::PrecacheTempEnts();
input->Init_All();
VGui_CreateGlobalPanels();
InitSmokeFogOverlay();
// Register user messages..
CUserMessageRegister::RegisterAll();
ClientVoiceMgr_Init();
// Embed voice status icons inside chat element
{
vgui::VPANEL parent = enginevgui->GetPanel( PANEL_CLIENTDLL );
GetClientVoiceMgr()->Init( &g_VoiceStatusHelper, parent );
}
if ( !PhysicsDLLInit( physicsFactory ) )
return false;
g_pGameSaveRestoreBlockSet->AddBlockHandler( GetEntitySaveRestoreBlockHandler() );
g_pGameSaveRestoreBlockSet->AddBlockHandler( GetPhysSaveRestoreBlockHandler() );
g_pGameSaveRestoreBlockSet->AddBlockHandler( GetViewEffectsRestoreBlockHandler() );
ClientWorldFactoryInit();
C_BaseAnimating::InitBoneSetupThreadPool();
#if defined( WIN32 ) && !defined( _X360 )
// NVNT connect haptics sytem
ConnectHaptics(appSystemFactory);
#endif
#ifndef _X360
HookHapticMessages(); // Always hook the messages
#endif
return true;
}
bool CHLClient::ReplayInit( CreateInterfaceFn fnReplayFactory )
{
#if defined( REPLAY_ENABLED )
if ( !IsPC() )
return false;
if ( (g_pReplay = (IReplaySystem *)fnReplayFactory( REPLAY_INTERFACE_VERSION, NULL ) ) == NULL )
return false;
if ( (g_pClientReplayContext = g_pReplay->CL_GetContext()) == NULL )
return false;
return true;
#else
return false;
#endif
}
bool CHLClient::ReplayPostInit()
{
#if defined( REPLAY_ENABLED )
if ( ( g_pReplayManager = g_pClientReplayContext->GetReplayManager() ) == NULL )
return false;
if ( ( g_pReplayScreenshotManager = g_pClientReplayContext->GetScreenshotManager() ) == NULL )
return false;
if ( ( g_pReplayPerformanceManager = g_pClientReplayContext->GetPerformanceManager() ) == NULL )
return false;
if ( ( g_pReplayPerformanceController = g_pClientReplayContext->GetPerformanceController() ) == NULL )
return false;
if ( ( g_pReplayMovieManager = g_pClientReplayContext->GetMovieManager() ) == NULL )
return false;
return true;
#else
return false;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Called after client & server DLL are loaded and all systems initialized
//-----------------------------------------------------------------------------
void CHLClient::PostInit()
{
IGameSystem::PostInitAllSystems();
#ifdef SIXENSE
// allow sixnese input to perform post-init operations
g_pSixenseInput->PostInit();
#endif
g_ClientVirtualReality.StartupComplete();
#ifdef HL1MP_CLIENT_DLL
if ( s_cl_load_hl1_content.GetBool() && steamapicontext && steamapicontext->SteamApps() )
{
char szPath[ MAX_PATH*2 ];
int ccFolder= steamapicontext->SteamApps()->GetAppInstallDir( 280, szPath, sizeof(szPath) );
if ( ccFolder > 0 )
{
V_AppendSlash( szPath, sizeof(szPath) );
V_strncat( szPath, "hl1", sizeof( szPath ) );
g_pFullFileSystem->AddSearchPath( szPath, "HL1" );
g_pFullFileSystem->AddSearchPath( szPath, "GAME" );
}
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Called when the client .dll is being dismissed
//-----------------------------------------------------------------------------
void CHLClient::Shutdown( void )
{
if (g_pAchievementsAndStatsInterface)
{
g_pAchievementsAndStatsInterface->ReleasePanel();
}
#ifdef SIXENSE
g_pSixenseInput->Shutdown();
delete g_pSixenseInput;
g_pSixenseInput = NULL;
#endif
C_BaseAnimating::ShutdownBoneSetupThreadPool();
ClientWorldFactoryShutdown();
g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetViewEffectsRestoreBlockHandler() );
g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetPhysSaveRestoreBlockHandler() );
g_pGameSaveRestoreBlockSet->RemoveBlockHandler( GetEntitySaveRestoreBlockHandler() );
ClientVoiceMgr_Shutdown();
Initializer::FreeAllObjects();
g_pClientMode->Disable();
g_pClientMode->Shutdown();
input->Shutdown_All();
C_BaseTempEntity::ClearDynamicTempEnts();
TermSmokeFogOverlay();
view->Shutdown();
g_pParticleSystemMgr->UncacheAllParticleSystems();
UncacheAllMaterials();
IGameSystem::ShutdownAllSystems();
gHUD.Shutdown();
VGui_Shutdown();
gTouch.Shutdown();
ParticleMgr()->Term();
ClearKeyValuesCache();
#ifndef NO_STEAM
ClientSteamContext().Shutdown();
#endif
#ifdef WORKSHOP_IMPORT_ENABLED
ShutdownDataModel();
DisconnectDataModel();
ShutdownFbx();
#endif
// This call disconnects the VGui libraries which we rely on later in the shutdown path, so don't do it
// DisconnectTier3Libraries( );
DisconnectTier2Libraries( );
ConVar_Unregister();
DisconnectTier1Libraries( );
gameeventmanager = NULL;
#if defined( WIN32 ) && !defined( _X360 )
// NVNT Disconnect haptics system
DisconnectHaptics();
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Called when the game initializes
// and whenever the vid_mode is changed
// so the HUD can reinitialize itself.
// Output : int
//-----------------------------------------------------------------------------
int CHLClient::HudVidInit( void )
{
gHUD.VidInit();
GetClientVoiceMgr()->VidInit();
return 1;
}
//-----------------------------------------------------------------------------
// Method used to allow the client to filter input messages before the
// move record is transmitted to the server
//-----------------------------------------------------------------------------
void CHLClient::HudProcessInput( bool bActive )
{
g_pClientMode->ProcessInput( bActive );
}
//-----------------------------------------------------------------------------
// Purpose: Called when shared data gets changed, allows dll to modify data
// Input : bActive -
//-----------------------------------------------------------------------------
void CHLClient::HudUpdate( bool bActive )
{
float frametime = gpGlobals->frametime;
#if defined( TF_CLIENT_DLL )
CRTime::UpdateRealTime();
#endif
GetClientVoiceMgr()->Frame( frametime );
gHUD.UpdateHud( bActive );
{
C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false );
IGameSystem::UpdateAllSystems( frametime );
}
// run vgui animations
vgui::GetAnimationController()->UpdateAnimations( engine->Time() );
hudlcd->SetGlobalStat( "(time_int)", VarArgs( "%d", (int)gpGlobals->curtime ) );
hudlcd->SetGlobalStat( "(time_float)", VarArgs( "%.2f", gpGlobals->curtime ) );
// I don't think this is necessary any longer, but I will leave it until
// I can check into this further.
C_BaseTempEntity::CheckDynamicTempEnts();
#ifdef SIXENSE
// If we're not connected, update sixense so we can move the mouse cursor when in the menus
if( !engine->IsConnected() || engine->IsPaused() )
{
g_pSixenseInput->SixenseFrame( 0, NULL );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Called to restore to "non"HUD state.
//-----------------------------------------------------------------------------
void CHLClient::HudReset( void )
{
gHUD.VidInit();
PhysicsReset();
}
//-----------------------------------------------------------------------------
// Purpose: Called to add hud text message
//-----------------------------------------------------------------------------
void CHLClient::HudText( const char * message )
{
DispatchHudText( message );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CHLClient::ShouldDrawDropdownConsole()
{
#if defined( REPLAY_ENABLED )
extern ConVar hud_freezecamhide;
extern bool IsTakingAFreezecamScreenshot();
if ( hud_freezecamhide.GetBool() && IsTakingAFreezecamScreenshot() )
{
return false;
}
#endif
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : ClientClass
//-----------------------------------------------------------------------------
ClientClass *CHLClient::GetAllClasses( void )
{
return g_pClientClassHead;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHLClient::IN_ActivateMouse( void )
{
input->ActivateMouse();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHLClient::IN_DeactivateMouse( void )
{
input->DeactivateMouse();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHLClient::IN_Accumulate ( void )
{
input->AccumulateMouse();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHLClient::IN_ClearStates ( void )
{
input->ClearStates();
}
//-----------------------------------------------------------------------------
// Purpose: Engine can query for particular keys
// Input : *name -
//-----------------------------------------------------------------------------
bool CHLClient::IN_IsKeyDown( const char *name, bool& isdown )
{
kbutton_t *key = input->FindKey( name );
if ( !key )
{
return false;
}
isdown = ( key->state & 1 ) ? true : false;
// Found the key by name
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Engine can issue a key event
// Input : eventcode -
// keynum -
// *pszCurrentBinding -
void CHLClient::IN_OnMouseWheeled( int nDelta )
{
#if defined( REPLAY_ENABLED )
CReplayPerformanceEditorPanel *pPerfEditor = ReplayUI_GetPerformanceEditor();
if ( pPerfEditor )
{
pPerfEditor->OnInGameMouseWheelEvent( nDelta );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Engine can issue a key event
// Input : eventcode -
// keynum -
// *pszCurrentBinding -
// Output : int
//-----------------------------------------------------------------------------
int CHLClient::IN_KeyEvent( int eventcode, ButtonCode_t keynum, const char *pszCurrentBinding )
{
return input->KeyEvent( eventcode, keynum, pszCurrentBinding );
}
void CHLClient::ExtraMovementSample( int number_of_ticks_this_frame, int current_command, float frametime, bool active )
{
Assert( C_BaseEntity::IsAbsRecomputationsEnabled() );
Assert( C_BaseEntity::IsAbsQueriesValid() );
C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false );
MDLCACHE_CRITICAL_SECTION();
input->ExtraMovementSample( number_of_ticks_this_frame, current_command, frametime, active );
}
void CHLClient::IN_SetSampleTime( float frametime )
{
input->Joystick_SetSampleTime( frametime );
input->IN_SetSampleTime( frametime );
#ifdef SIXENSE
g_pSixenseInput->ResetFrameTime( frametime );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Fills in usercmd_s structure based on current view angles and key/controller inputs
// Input : frametime - timestamp for last frame
// *cmd - the command to fill in
// active - whether the user is fully connected to a server
//-----------------------------------------------------------------------------
void CHLClient::CreateMove ( int sequence_number, float input_sample_frametime, bool active )
{
Assert( C_BaseEntity::IsAbsRecomputationsEnabled() );
Assert( C_BaseEntity::IsAbsQueriesValid() );
C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false );
MDLCACHE_CRITICAL_SECTION();
input->CreateMove( sequence_number, input_sample_frametime, active );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *buf -
// from -
// to -
//-----------------------------------------------------------------------------
bool CHLClient::WriteUsercmdDeltaToBuffer( bf_write *buf, int from, int to, bool isnewcommand )
{
return input->WriteUsercmdDeltaToBuffer( buf, from, to, isnewcommand );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : buf -
// buffersize -
// slot -
//-----------------------------------------------------------------------------
void CHLClient::EncodeUserCmdToBuffer( bf_write& buf, int slot )
{
input->EncodeUserCmdToBuffer( buf, slot );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : buf -
// buffersize -
// slot -
//-----------------------------------------------------------------------------
void CHLClient::DecodeUserCmdFromBuffer( bf_read& buf, int slot )
{
input->DecodeUserCmdFromBuffer( buf, slot );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHLClient::View_Render( vrect_t *rect )
{
VPROF( "View_Render" );
// UNDONE: This gets hit at startup sometimes, investigate - will cause NaNs in calcs inside Render()
if ( rect->width == 0 || rect->height == 0 )
return;
view->Render( rect );
UpdatePerfStats();
}
//-----------------------------------------------------------------------------
// Gets the location of the player viewpoint
//-----------------------------------------------------------------------------
bool CHLClient::GetPlayerView( CViewSetup &playerView )
{
playerView = *view->GetPlayerViewSetup();
return true;
}
//-----------------------------------------------------------------------------
// Matchmaking
//-----------------------------------------------------------------------------
void CHLClient::SetupGameProperties( CUtlVector< XUSER_CONTEXT > &contexts, CUtlVector< XUSER_PROPERTY > &properties )
{
presence->SetupGameProperties( contexts, properties );
}
uint CHLClient::GetPresenceID( const char *pIDName )
{
return presence->GetPresenceID( pIDName );
}
const char *CHLClient::GetPropertyIdString( const uint id )
{
return presence->GetPropertyIdString( id );
}
void CHLClient::GetPropertyDisplayString( uint id, uint value, char *pOutput, int nBytes )
{
presence->GetPropertyDisplayString( id, value, pOutput, nBytes );
}
void CHLClient::StartStatsReporting( HANDLE handle, bool bArbitrated )
{
presence->StartStatsReporting( handle, bArbitrated );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CHLClient::InvalidateMdlCache()
{
C_BaseAnimating *pAnimating;
for ( C_BaseEntity *pEntity = ClientEntityList().FirstBaseEntity(); pEntity; pEntity = ClientEntityList().NextBaseEntity(pEntity) )
{
pAnimating = dynamic_cast<C_BaseAnimating *>(pEntity);
if ( pAnimating )
{
pAnimating->InvalidateMdlCache();
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pSF -
//-----------------------------------------------------------------------------
void CHLClient::View_Fade( ScreenFade_t *pSF )
{
if ( pSF != NULL )
vieweffects->Fade( *pSF );
}
//-----------------------------------------------------------------------------
// Purpose: Per level init
//-----------------------------------------------------------------------------
void CHLClient::LevelInitPreEntity( char const* pMapName )
{
// HACK: Bogus, but the logic is too complicated in the engine
if (g_bLevelInitialized)
return;
g_bLevelInitialized = true;
input->LevelInit();
vieweffects->LevelInit();
//Tony; loadup per-map manifests.
ParseParticleEffectsMap( pMapName, true );
// Tell mode manager that map is changing
modemanager->LevelInit( pMapName );
ParticleMgr()->LevelInit();
hudlcd->SetGlobalStat( "(mapname)", pMapName );
C_BaseTempEntity::ClearDynamicTempEnts();
clienteffects->Flush();
view->LevelInit();
tempents->LevelInit();
ResetToneMapping(1.0);
IGameSystem::LevelInitPreEntityAllSystems(pMapName);
#ifdef USES_ECON_ITEMS
GameItemSchema_t *pItemSchema = ItemSystem()->GetItemSchema();
if ( pItemSchema )
{
pItemSchema->BInitFromDelayedBuffer();
}
#endif // USES_ECON_ITEMS
ResetWindspeed();
#if !defined( NO_ENTITY_PREDICTION )
// don't do prediction if single player!
// don't set direct because of FCVAR_USERINFO
if ( gpGlobals->maxClients > 1 )
{
if ( !cl_predict->GetInt() )
{
engine->ClientCmd( "cl_predict 1" );
}
}
else
{
if ( cl_predict->GetInt() )
{
engine->ClientCmd( "cl_predict 0" );
}
}
#endif
// Check low violence settings for this map
g_RagdollLVManager.SetLowViolence( pMapName );
gHUD.LevelInit();
gTouch.LevelInit();
#if defined( REPLAY_ENABLED )
// Initialize replay ragdoll recorder
if ( !engine->IsPlayingDemo() )
{
CReplayRagdollRecorder::Instance().Init();
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Per level init
//-----------------------------------------------------------------------------
void CHLClient::LevelInitPostEntity( )
{
IGameSystem::LevelInitPostEntityAllSystems();
C_PhysPropClientside::RecreateAll();
internalCenterPrint->Clear();
}
//-----------------------------------------------------------------------------
// Purpose: Reset our global string table pointers
//-----------------------------------------------------------------------------
void CHLClient::ResetStringTablePointers()
{
g_pStringTableParticleEffectNames = NULL;
g_StringTableEffectDispatch = NULL;
g_StringTableVguiScreen = NULL;
g_pStringTableMaterials = NULL;
g_pStringTableInfoPanel = NULL;
g_pStringTableClientSideChoreoScenes = NULL;
g_pStringTableServerMapCycle = NULL;
#ifdef TF_CLIENT_DLL
g_pStringTableServerPopFiles = NULL;
g_pStringTableServerMapCycleMvM = NULL;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Per level de-init
//-----------------------------------------------------------------------------
void CHLClient::LevelShutdown( void )
{
// HACK: Bogus, but the logic is too complicated in the engine
if (!g_bLevelInitialized)
return;
g_bLevelInitialized = false;
// Disable abs recomputations when everything is shutting down
CBaseEntity::EnableAbsRecomputations( false );
// Level shutdown sequence.
// First do the pre-entity shutdown of all systems
IGameSystem::LevelShutdownPreEntityAllSystems();
C_PhysPropClientside::DestroyAll();
modemanager->LevelShutdown();
// Remove temporary entities before removing entities from the client entity list so that the te_* may
// clean up before hand.
tempents->LevelShutdown();
// Now release/delete the entities
cl_entitylist->Release();
C_BaseEntityClassList *pClassList = s_pClassLists;
while ( pClassList )
{
pClassList->LevelShutdown();
pClassList = pClassList->m_pNextClassList;
}
// Now do the post-entity shutdown of all systems
IGameSystem::LevelShutdownPostEntityAllSystems();
view->LevelShutdown();
beams->ClearBeams();
ParticleMgr()->RemoveAllEffects();
StopAllRumbleEffects();
gHUD.LevelShutdown();
internalCenterPrint->Clear();
messagechars->Clear();
#ifndef TF_CLIENT_DLL
// don't want to do this for TF2 because we have particle systems in our
// character loadout screen that can be viewed when we're not connected to a server
g_pParticleSystemMgr->UncacheAllParticleSystems();
#endif
UncacheAllMaterials();
#ifdef _XBOX
ReleaseRenderTargets();
#endif
// string tables are cleared on disconnect from a server, so reset our global pointers to NULL
ResetStringTablePointers();
#if defined( REPLAY_ENABLED )
// Shutdown the ragdoll recorder
CReplayRagdollRecorder::Instance().Shutdown();
CReplayRagdollCache::Instance().Shutdown();
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Engine received crosshair offset ( autoaim )
// Input : angle -
//-----------------------------------------------------------------------------
void CHLClient::SetCrosshairAngle( const QAngle& angle )
{
CHudCrosshair *crosshair = GET_HUDELEMENT( CHudCrosshair );
if ( crosshair )
{
crosshair->SetCrosshairAngle( angle );
}
}
//-----------------------------------------------------------------------------
// Purpose: Helper to initialize sprite from .spr semaphor
// Input : *pSprite -
// *loadname -
//-----------------------------------------------------------------------------
void CHLClient::InitSprite( CEngineSprite *pSprite, const char *loadname )
{
if ( pSprite )
{
pSprite->Init( loadname );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pSprite -
//-----------------------------------------------------------------------------
void CHLClient::ShutdownSprite( CEngineSprite *pSprite )
{
if ( pSprite )
{
pSprite->Shutdown();
}
}
//-----------------------------------------------------------------------------
// Purpose: Tells engine how much space to allocate for sprite objects
// Output : int
//-----------------------------------------------------------------------------
int CHLClient::GetSpriteSize( void ) const
{
return sizeof( CEngineSprite );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : entindex -
// bTalking -
//-----------------------------------------------------------------------------
void CHLClient::VoiceStatus( int entindex, qboolean bTalking )
{
GetClientVoiceMgr()->UpdateSpeakerStatus( entindex, !!bTalking );
}
//-----------------------------------------------------------------------------
// Called when the string table for materials changes
//-----------------------------------------------------------------------------
void OnMaterialStringTableChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, void const *newData )
{
// Make sure this puppy is precached
gHLClient.PrecacheMaterial( newString );
RequestCacheUsedMaterials();
}
//-----------------------------------------------------------------------------
// Called when the string table for particle systems changes
//-----------------------------------------------------------------------------
void OnParticleSystemStringTableChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, void const *newData )
{
// Make sure this puppy is precached
g_pParticleSystemMgr->PrecacheParticleSystem( newString );
RequestCacheUsedMaterials();
}
//-----------------------------------------------------------------------------
// Called when the string table for VGUI changes
//-----------------------------------------------------------------------------
void OnVguiScreenTableChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, void const *newData )
{
// Make sure this puppy is precached
vgui::Panel *pPanel = PanelMetaClassMgr()->CreatePanelMetaClass( newString, 100, NULL, NULL );
if ( pPanel )
PanelMetaClassMgr()->DestroyPanelMetaClass( pPanel );
}
//-----------------------------------------------------------------------------
// Purpose: Preload the string on the client (if single player it should already be in the cache from the server!!!)
// Input : *object -
// *stringTable -
// stringNumber -
// *newString -
// *newData -
//-----------------------------------------------------------------------------
void OnSceneStringTableChanged( void *object, INetworkStringTable *stringTable, int stringNumber, const char *newString, void const *newData )
{
}
//-----------------------------------------------------------------------------
// Purpose: Hook up any callbacks here, the table definition has been parsed but
// no data has been added yet
//-----------------------------------------------------------------------------
void CHLClient::InstallStringTableCallback( const char *tableName )
{
// Here, cache off string table IDs
if (!Q_strcasecmp(tableName, "VguiScreen"))
{
// Look up the id
g_StringTableVguiScreen = networkstringtable->FindTable( tableName );
// When the material list changes, we need to know immediately
g_StringTableVguiScreen->SetStringChangedCallback( NULL, OnVguiScreenTableChanged );
}
else if (!Q_strcasecmp(tableName, "Materials"))
{
// Look up the id
g_pStringTableMaterials = networkstringtable->FindTable( tableName );
// When the material list changes, we need to know immediately
g_pStringTableMaterials->SetStringChangedCallback( NULL, OnMaterialStringTableChanged );
}
else if ( !Q_strcasecmp( tableName, "EffectDispatch" ) )
{
g_StringTableEffectDispatch = networkstringtable->FindTable( tableName );
}
else if ( !Q_strcasecmp( tableName, "InfoPanel" ) )
{
g_pStringTableInfoPanel = networkstringtable->FindTable( tableName );
}
else if ( !Q_strcasecmp( tableName, "Scenes" ) )
{
g_pStringTableClientSideChoreoScenes = networkstringtable->FindTable( tableName );
g_pStringTableClientSideChoreoScenes->SetStringChangedCallback( NULL, OnSceneStringTableChanged );
}
else if ( !Q_strcasecmp( tableName, "ParticleEffectNames" ) )
{
g_pStringTableParticleEffectNames = networkstringtable->FindTable( tableName );
networkstringtable->SetAllowClientSideAddString( g_pStringTableParticleEffectNames, true );
// When the particle system list changes, we need to know immediately
g_pStringTableParticleEffectNames->SetStringChangedCallback( NULL, OnParticleSystemStringTableChanged );
}
else if ( !Q_strcasecmp( tableName, "ServerMapCycle" ) )
{
g_pStringTableServerMapCycle = networkstringtable->FindTable( tableName );
}
#ifdef TF_CLIENT_DLL
else if ( !Q_strcasecmp( tableName, "ServerPopFiles" ) )
{
g_pStringTableServerPopFiles = networkstringtable->FindTable( tableName );
}
else if ( !Q_strcasecmp( tableName, "ServerMapCycleMvM" ) )
{
g_pStringTableServerMapCycleMvM = networkstringtable->FindTable( tableName );
}
#endif
InstallStringTableCallback_GameRules();
}
//-----------------------------------------------------------------------------
// Purpose: Marks entities as touching
// Input : *e1 -
// *e2 -
//-----------------------------------------------------------------------------
void CHLClient::MarkEntitiesAsTouching( IClientEntity *e1, IClientEntity *e2 )
{
CBaseEntity *entity = e1->GetBaseEntity();
CBaseEntity *entityTouched = e2->GetBaseEntity();
if ( entity && entityTouched )
{
trace_t tr;
UTIL_ClearTrace( tr );
tr.endpos = (entity->GetAbsOrigin() + entityTouched->GetAbsOrigin()) * 0.5;
entity->PhysicsMarkEntitiesAsTouching( entityTouched, tr );
}
}
//-----------------------------------------------------------------------------
// Material precache
//-----------------------------------------------------------------------------
void CHLClient::PrecacheMaterial( const char *pMaterialName )
{
Assert( pMaterialName );
int nLen = Q_strlen( pMaterialName );
char *pTempBuf = (char*)stackalloc( nLen + 1 );
memcpy( pTempBuf, pMaterialName, nLen + 1 );
char *pFound = Q_strstr( pTempBuf, ".vmt\0" );
if ( pFound )
{
*pFound = 0;
}
IMaterial *pMaterial = materials->FindMaterial( pTempBuf, TEXTURE_GROUP_PRECACHED );
if ( !IsErrorMaterial( pMaterial ) )
{
pMaterial->IncrementReferenceCount();
m_CachedMaterials.AddToTail( pMaterial );
}
else
{
if (IsOSX())
{
printf("\n ##### CHLClient::PrecacheMaterial could not find material %s (%s)", pMaterialName, pTempBuf );
}
}
}
void CHLClient::UncacheAllMaterials( )
{
for (int i = m_CachedMaterials.Count(); --i >= 0; )
{
m_CachedMaterials[i]->DecrementReferenceCount();
}
m_CachedMaterials.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pszName -
// iSize -
// *pbuf -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CHLClient::DispatchUserMessage( int msg_type, bf_read &msg_data )
{
return usermessages->DispatchUserMessage( msg_type, msg_data );
}
void SimulateEntities()
{
VPROF_BUDGET("Client SimulateEntities", VPROF_BUDGETGROUP_CLIENT_SIM);
// Service timer events (think functions).
ClientThinkList()->PerformThinkFunctions();
// TODO: make an ISimulateable interface so C_BaseNetworkables can simulate?
{
VPROF_("C_BaseEntity::Simulate", 1, VPROF_BUDGETGROUP_CLIENT_SIM, false, BUDGETFLAG_CLIENT);
C_BaseEntityIterator iterator;
C_BaseEntity *pEnt;
while ( (pEnt = iterator.Next()) != NULL )
{
pEnt->Simulate();
}
}
}
bool AddDataChangeEvent( IClientNetworkable *ent, DataUpdateType_t updateType, int *pStoredEvent )
{
VPROF( "AddDataChangeEvent" );
Assert( ent );
// Make sure we don't already have an event queued for this guy.
if ( *pStoredEvent >= 0 )
{
Assert( g_DataChangedEvents[*pStoredEvent].m_pEntity == ent );
// DATA_UPDATE_CREATED always overrides DATA_UPDATE_CHANGED.
if ( updateType == DATA_UPDATE_CREATED )
g_DataChangedEvents[*pStoredEvent].m_UpdateType = updateType;
return false;
}
else
{
*pStoredEvent = g_DataChangedEvents.AddToTail( CDataChangedEvent( ent, updateType, pStoredEvent ) );
return true;
}
}
void ClearDataChangedEvent( int iStoredEvent )
{
if ( iStoredEvent != -1 )
g_DataChangedEvents.Remove( iStoredEvent );
}
void ProcessOnDataChangedEvents()
{
VPROF_("ProcessOnDataChangedEvents", 1, VPROF_BUDGETGROUP_CLIENT_SIM, false, BUDGETFLAG_CLIENT);
FOR_EACH_LL( g_DataChangedEvents, i )
{
CDataChangedEvent *pEvent = &g_DataChangedEvents[i];
// Reset their stored event identifier.
*pEvent->m_pStoredEvent = -1;
// Send the event.
IClientNetworkable *pNetworkable = pEvent->m_pEntity;
pNetworkable->OnDataChanged( pEvent->m_UpdateType );
}
g_DataChangedEvents.Purge();
}
void UpdateClientRenderableInPVSStatus()
{
// Vis for this view should already be setup at this point.
// For each client-only entity, notify it if it's newly coming into the PVS.
CUtlLinkedList<CClientEntityList::CPVSNotifyInfo,unsigned short> &theList = ClientEntityList().GetPVSNotifiers();
FOR_EACH_LL( theList, i )
{
CClientEntityList::CPVSNotifyInfo *pInfo = &theList[i];
if ( pInfo->m_InPVSStatus & INPVS_YES )
{
// Ok, this entity already thinks it's in the PVS. No need to notify it.
// We need to set the INPVS_YES_THISFRAME flag if it's in this frame at all, so we
// don't tell the entity it's not in the PVS anymore at the end of the frame.
if ( !( pInfo->m_InPVSStatus & INPVS_THISFRAME ) )
{
if ( g_pClientLeafSystem->IsRenderableInPVS( pInfo->m_pRenderable ) )
{
pInfo->m_InPVSStatus |= INPVS_THISFRAME;
}
}
}
else
{
// This entity doesn't think it's in the PVS yet. If it is now in the PVS, let it know.
if ( g_pClientLeafSystem->IsRenderableInPVS( pInfo->m_pRenderable ) )
{
pInfo->m_InPVSStatus |= ( INPVS_YES | INPVS_THISFRAME | INPVS_NEEDSNOTIFY );
}
}
}
}
void UpdatePVSNotifiers()
{
MDLCACHE_CRITICAL_SECTION();
// At this point, all the entities that were rendered in the previous frame have INPVS_THISFRAME set
// so we can tell the entities that aren't in the PVS anymore so.
CUtlLinkedList<CClientEntityList::CPVSNotifyInfo,unsigned short> &theList = ClientEntityList().GetPVSNotifiers();
FOR_EACH_LL( theList, i )
{
CClientEntityList::CPVSNotifyInfo *pInfo = &theList[i];
// If this entity thinks it's in the PVS, but it wasn't in the PVS this frame, tell it so.
if ( pInfo->m_InPVSStatus & INPVS_YES )
{
if ( pInfo->m_InPVSStatus & INPVS_THISFRAME )
{
if ( pInfo->m_InPVSStatus & INPVS_NEEDSNOTIFY )
{
pInfo->m_pNotify->OnPVSStatusChanged( true );
}
// Clear it for the next time around.
pInfo->m_InPVSStatus &= ~( INPVS_THISFRAME | INPVS_NEEDSNOTIFY );
}
else
{
pInfo->m_InPVSStatus &= ~INPVS_YES;
pInfo->m_pNotify->OnPVSStatusChanged( false );
}
}
}
}
void OnRenderStart()
{
VPROF( "OnRenderStart" );
MDLCACHE_CRITICAL_SECTION();
MDLCACHE_COARSE_LOCK();
#ifdef PORTAL
g_pPortalRender->UpdatePortalPixelVisibility(); //updating this one or two lines before querying again just isn't cutting it. Update as soon as it's cheap to do so.
#endif
partition->SuppressLists( PARTITION_ALL_CLIENT_EDICTS, true );
C_BaseEntity::SetAbsQueriesValid( false );
Rope_ResetCounters();
// Interpolate server entities and move aiments.
{
PREDICTION_TRACKVALUECHANGESCOPE( "interpolation" );
C_BaseEntity::InterpolateServerEntities();
}
{
// vprof node for this bloc of math
VPROF( "OnRenderStart: dirty bone caches");
// Invalidate any bone information.
C_BaseAnimating::InvalidateBoneCaches();
C_BaseEntity::SetAbsQueriesValid( true );
C_BaseEntity::EnableAbsRecomputations( true );
// Enable access to all model bones except view models.
// This is necessary for aim-ent computation to occur properly
C_BaseAnimating::PushAllowBoneAccess( true, false, "OnRenderStart->CViewRender::SetUpView" ); // pops in CViewRender::SetUpView
// FIXME: This needs to be done before the player moves; it forces
// aiments the player may be attached to to forcibly update their position
C_BaseEntity::MarkAimEntsDirty();
}
// Make sure the camera simulation happens before OnRenderStart, where it's used.
// NOTE: the only thing that happens in CAM_Think is thirdperson related code.
input->CAM_Think();
// This will place the player + the view models + all parent
// entities at the correct abs position so that their attachment points
// are at the correct location
view->OnRenderStart();
RopeManager()->OnRenderStart();
// This will place all entities in the correct position in world space and in the KD-tree
C_BaseAnimating::UpdateClientSideAnimations();
partition->SuppressLists( PARTITION_ALL_CLIENT_EDICTS, false );
// Process OnDataChanged events.
ProcessOnDataChangedEvents();
// Reset the overlay alpha. Entities can change the state of this in their think functions.
g_SmokeFogOverlayAlpha = 0;
// This must occur prior to SimulatEntities,
// which is where the client thinks for c_colorcorrection + c_colorcorrectionvolumes
// update the color correction weights.
// FIXME: The place where IGameSystem::Update is called should be in here
// so we don't have to explicitly call ResetColorCorrectionWeights + SimulateEntities, etc.
g_pColorCorrectionMgr->ResetColorCorrectionWeights();
// Simulate all the entities.
SimulateEntities();
PhysicsSimulate();
C_BaseAnimating::ThreadedBoneSetup();
{
VPROF_("Client TempEnts", 0, VPROF_BUDGETGROUP_CLIENT_SIM, false, BUDGETFLAG_CLIENT);
// This creates things like temp entities.
engine->FireEvents();
// Update temp entities
tempents->Update();
// Update temp ent beams...
beams->UpdateTempEntBeams();
// Lock the frame from beam additions
SetBeamCreationAllowed( false );
}
// Update particle effects (eventually, the effects should use Simulate() instead of having
// their own update system).
{
// Enable FP exceptions here when FP_EXCEPTIONS_ENABLED is defined,
// to help track down bad math.
FPExceptionEnabler enableExceptions;
VPROF_BUDGET( "ParticleMgr()->Simulate", VPROF_BUDGETGROUP_PARTICLE_SIMULATION );
ParticleMgr()->Simulate( gpGlobals->frametime );
}
// Now that the view model's position is setup and aiments are marked dirty, update
// their positions so they're in the leaf system correctly.
C_BaseEntity::CalcAimEntPositions();
// For entities marked for recording, post bone messages to IToolSystems
if ( ToolsEnabled() )
{
C_BaseEntity::ToolRecordEntities();
}
#if defined( REPLAY_ENABLED )
// This will record any ragdolls if Replay mode is enabled on the server
CReplayRagdollRecorder::Instance().Think();
CReplayRagdollCache::Instance().Think();
#endif
// Finally, link all the entities into the leaf system right before rendering.
C_BaseEntity::AddVisibleEntities();
}
void OnRenderEnd()
{
// Disallow access to bones (access is enabled in CViewRender::SetUpView).
C_BaseAnimating::PopBoneAccess( "CViewRender::SetUpView->OnRenderEnd" );
UpdatePVSNotifiers();
DisplayBoneSetupEnts();
}
void CHLClient::FrameStageNotify( ClientFrameStage_t curStage )
{
g_CurFrameStage = curStage;
switch( curStage )
{
default:
break;
case FRAME_RENDER_START:
{
VPROF( "CHLClient::FrameStageNotify FRAME_RENDER_START" );
// Last thing before rendering, run simulation.
OnRenderStart();
}
break;
case FRAME_RENDER_END:
{
VPROF( "CHLClient::FrameStageNotify FRAME_RENDER_END" );
OnRenderEnd();
PREDICTION_SPEWVALUECHANGES();
}
break;
case FRAME_NET_UPDATE_START:
{
VPROF( "CHLClient::FrameStageNotify FRAME_NET_UPDATE_START" );
// disabled all recomputations while we update entities
C_BaseEntity::EnableAbsRecomputations( false );
C_BaseEntity::SetAbsQueriesValid( false );
Interpolation_SetLastPacketTimeStamp( engine->GetLastTimeStamp() );
partition->SuppressLists( PARTITION_ALL_CLIENT_EDICTS, true );
PREDICTION_STARTTRACKVALUE( "netupdate" );
}
break;
case FRAME_NET_UPDATE_END:
{
ProcessCacheUsedMaterials();
// reenable abs recomputation since now all entities have been updated
C_BaseEntity::EnableAbsRecomputations( true );
C_BaseEntity::SetAbsQueriesValid( true );
partition->SuppressLists( PARTITION_ALL_CLIENT_EDICTS, false );
PREDICTION_ENDTRACKVALUE();
}
break;
case FRAME_NET_UPDATE_POSTDATAUPDATE_START:
{
VPROF( "CHLClient::FrameStageNotify FRAME_NET_UPDATE_POSTDATAUPDATE_START" );
PREDICTION_STARTTRACKVALUE( "postdataupdate" );
}
break;
case FRAME_NET_UPDATE_POSTDATAUPDATE_END:
{
VPROF( "CHLClient::FrameStageNotify FRAME_NET_UPDATE_POSTDATAUPDATE_END" );
PREDICTION_ENDTRACKVALUE();
// Let prediction copy off pristine data
prediction->PostEntityPacketReceived();
HLTVCamera()->PostEntityPacketReceived();
#if defined( REPLAY_ENABLED )
ReplayCamera()->PostEntityPacketReceived();
#endif
}
break;
case FRAME_START:
{
// Mark the frame as open for client fx additions
SetFXCreationAllowed( true );
SetBeamCreationAllowed( true );
C_BaseEntity::CheckCLInterpChanged();
}
break;
}
}
CSaveRestoreData *SaveInit( int size );
// Save/restore system hooks
CSaveRestoreData *CHLClient::SaveInit( int size )
{
return ::SaveInit(size);
}
void CHLClient::SaveWriteFields( CSaveRestoreData *pSaveData, const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount )
{
CSave saveHelper( pSaveData );
saveHelper.WriteFields( pname, pBaseData, pMap, pFields, fieldCount );
}
void CHLClient::SaveReadFields( CSaveRestoreData *pSaveData, const char *pname, void *pBaseData, datamap_t *pMap, typedescription_t *pFields, int fieldCount )
{
CRestore restoreHelper( pSaveData );
restoreHelper.ReadFields( pname, pBaseData, pMap, pFields, fieldCount );
}
void CHLClient::PreSave( CSaveRestoreData *s )
{
g_pGameSaveRestoreBlockSet->PreSave( s );
}
void CHLClient::Save( CSaveRestoreData *s )
{
CSave saveHelper( s );
g_pGameSaveRestoreBlockSet->Save( &saveHelper );
}
void CHLClient::WriteSaveHeaders( CSaveRestoreData *s )
{
CSave saveHelper( s );
g_pGameSaveRestoreBlockSet->WriteSaveHeaders( &saveHelper );
g_pGameSaveRestoreBlockSet->PostSave();
}
void CHLClient::ReadRestoreHeaders( CSaveRestoreData *s )
{
CRestore restoreHelper( s );
g_pGameSaveRestoreBlockSet->PreRestore();
g_pGameSaveRestoreBlockSet->ReadRestoreHeaders( &restoreHelper );
}
void CHLClient::Restore( CSaveRestoreData *s, bool b )
{
CRestore restore(s);
g_pGameSaveRestoreBlockSet->Restore( &restore, b );
g_pGameSaveRestoreBlockSet->PostRestore();
}
static CUtlVector<EHANDLE> g_RestoredEntities;
void AddRestoredEntity( C_BaseEntity *pEntity )
{
if ( !pEntity )
return;
g_RestoredEntities.AddToTail( EHANDLE(pEntity) );
}
void CHLClient::DispatchOnRestore()
{
for ( int i = 0; i < g_RestoredEntities.Count(); i++ )
{
if ( g_RestoredEntities[i] != NULL )
{
MDLCACHE_CRITICAL_SECTION();
g_RestoredEntities[i]->OnRestore();
}
}
g_RestoredEntities.RemoveAll();
}
void CHLClient::WriteSaveGameScreenshot( const char *pFilename )
{
view->WriteSaveGameScreenshot( pFilename );
}
// Given a list of "S(wavname) S(wavname2)" tokens, look up the localized text and emit
// the appropriate close caption if running with closecaption = 1
void CHLClient::EmitSentenceCloseCaption( char const *tokenstream )
{
extern ConVar closecaption;
if ( !closecaption.GetBool() )
return;
CHudCloseCaption *hudCloseCaption = GET_HUDELEMENT( CHudCloseCaption );
if ( hudCloseCaption )
{
hudCloseCaption->ProcessSentenceCaptionStream( tokenstream );
}
}
void CHLClient::EmitCloseCaption( char const *captionname, float duration )
{
extern ConVar closecaption;
if ( !closecaption.GetBool() )
return;
CHudCloseCaption *hudCloseCaption = GET_HUDELEMENT( CHudCloseCaption );
if ( hudCloseCaption )
{
hudCloseCaption->ProcessCaption( captionname, duration );
}
}
CStandardRecvProxies* CHLClient::GetStandardRecvProxies()
{
return &g_StandardRecvProxies;
}
bool CHLClient::CanRecordDemo( char *errorMsg, int length ) const
{
if ( GetClientModeNormal() )
{
return GetClientModeNormal()->CanRecordDemo( errorMsg, length );
}
return true;
}
void CHLClient::OnDemoRecordStart( char const* pDemoBaseName )
{
}
void CHLClient::OnDemoRecordStop()
{
}
void CHLClient::OnDemoPlaybackStart( char const* pDemoBaseName )
{
#if defined( REPLAY_ENABLED )
// Load any ragdoll override frames from disk
char szRagdollFile[MAX_OSPATH];
V_snprintf( szRagdollFile, sizeof(szRagdollFile), "%s.dmx", pDemoBaseName );
CReplayRagdollCache::Instance().Init( szRagdollFile );
#endif
}
void CHLClient::OnDemoPlaybackStop()
{
#ifdef DEMOPOLISH_ENABLED
if ( DemoPolish_GetController().m_bInit )
{
DemoPolish_GetController().Shutdown();
}
#endif
#if defined( REPLAY_ENABLED )
CReplayRagdollCache::Instance().Shutdown();
#endif
}
int CHLClient::GetScreenWidth()
{
return ScreenWidth();
}
int CHLClient::GetScreenHeight()
{
return ScreenHeight();
}
// NEW INTERFACES
// save game screenshot writing
void CHLClient::WriteSaveGameScreenshotOfSize( const char *pFilename, int width, int height, bool bCreatePowerOf2Padded/*=false*/,
bool bWriteVTF/*=false*/ )
{
view->WriteSaveGameScreenshotOfSize( pFilename, width, height, bCreatePowerOf2Padded, bWriteVTF );
}
// See RenderViewInfo_t
void CHLClient::RenderView( const CViewSetup &setup, int nClearFlags, int whatToDraw )
{
VPROF("RenderView");
view->RenderView( setup, nClearFlags, whatToDraw );
}
void ReloadSoundEntriesInList( IFileList *pFilesToReload );
//-----------------------------------------------------------------------------
// For sv_pure mode. The filesystem figures out which files the client needs to reload to be "pure" ala the server's preferences.
//-----------------------------------------------------------------------------
void CHLClient::ReloadFilesInList( IFileList *pFilesToReload )
{
ReloadParticleEffectsInList( pFilesToReload );
ReloadSoundEntriesInList( pFilesToReload );
}
bool CHLClient::HandleUiToggle()
{
#if defined( REPLAY_ENABLED )
if ( !g_pEngineReplay || !g_pEngineReplay->IsSupportedModAndPlatform() )
return false;
CReplayPerformanceEditorPanel *pEditor = ReplayUI_GetPerformanceEditor();
if ( !pEditor )
return false;
pEditor->HandleUiToggle();
return true;
#else
return false;
#endif
}
bool CHLClient::ShouldAllowConsole()
{
return true;
}
CRenamedRecvTableInfo *CHLClient::GetRenamedRecvTableInfos()
{
return g_pRenamedRecvTableInfoHead;
}
CMouthInfo g_ClientUIMouth;
// Get the mouthinfo for the sound being played inside UI panels
CMouthInfo *CHLClient::GetClientUIMouthInfo()
{
return &g_ClientUIMouth;
}
void CHLClient::FileReceived( const char * fileName, unsigned int transferID )
{
if ( g_pGameRules )
{
g_pGameRules->OnFileReceived( fileName, transferID );
}
}
void CHLClient::ClientAdjustStartSoundParams( StartSoundParams_t& params )
{
#ifdef TF_CLIENT_DLL
CBaseEntity *pEntity = ClientEntityList().GetEnt( params.soundsource );
// A player speaking
if ( params.entchannel == CHAN_VOICE && GameRules() && pEntity && pEntity->IsPlayer() )
{
// Use high-pitched voices for other players if the local player has an item that allows them to hear it (Pyro Goggles)
if ( !GameRules()->IsLocalPlayer( params.soundsource ) && IsLocalPlayerUsingVisionFilterFlags( TF_VISION_FILTER_PYRO ) )
{
params.pitch *= 1.3f;
}
// Halloween voice futzery?
else
{
float flHeadScale = 1.f;
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pEntity, flHeadScale, head_scale );
int iHalloweenVoiceSpell = 0;
CALL_ATTRIB_HOOK_INT_ON_OTHER( pEntity, iHalloweenVoiceSpell, halloween_voice_modulation );
if ( iHalloweenVoiceSpell > 0 )
{
params.pitch *= 0.8f;
}
else if( flHeadScale != 1.f )
{
// Big head, deep voice
if( flHeadScale > 1.f )
{
params.pitch *= 0.8f;
}
else // Small head, high voice
{
params.pitch *= 1.3f;
}
}
}
}
#endif
}
const char* CHLClient::TranslateEffectForVisionFilter( const char *pchEffectType, const char *pchEffectName )
{
if ( !GameRules() )
return pchEffectName;
return GameRules()->TranslateEffectForVisionFilter( pchEffectType, pchEffectName );
}
bool CHLClient::DisconnectAttempt( void )
{
bool bRet = false;
#if defined( TF_CLIENT_DLL )
bRet = HandleDisconnectAttempt();
#endif
return bRet;
}
bool CHLClient::IsConnectedUserInfoChangeAllowed( IConVar *pCvar )
{
return GameRules() ? GameRules()->IsConnectedUserInfoChangeAllowed( NULL ) : true;
}
#ifndef NO_STEAM
CSteamID GetSteamIDForPlayerIndex( int iPlayerIndex )
{
player_info_t pi;
if ( steamapicontext && steamapicontext->SteamUtils() )
{
if ( engine->GetPlayerInfo( iPlayerIndex, &pi ) )
{
if ( pi.friendsID )
{
return CSteamID( pi.friendsID, 1, steamapicontext->SteamUtils()->GetConnectedUniverse(), k_EAccountTypeIndividual );
}
}
}
return CSteamID();
}
#endif
void CHLClient::IN_TouchEvent( int type, int fingerId, int x, int y )
{
if( enginevgui->IsGameUIVisible() )
return;
touch_event_t ev;
ev.type = type;
ev.fingerid = fingerId;
memcpy( &ev.x, &x, sizeof(ev.x) );
memcpy( &ev.y, &y, sizeof(ev.y) );
if( type == IE_FingerMotion )
inputsystem->GetTouchAccumulators( fingerId, ev.dx, ev.dy );
gTouch.ProcessEvent( &ev );
}