summaryrefslogtreecommitdiff
path: root/engine/servermsghandler.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/servermsghandler.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'engine/servermsghandler.cpp')
-rw-r--r--engine/servermsghandler.cpp993
1 files changed, 993 insertions, 0 deletions
diff --git a/engine/servermsghandler.cpp b/engine/servermsghandler.cpp
new file mode 100644
index 0000000..9678809
--- /dev/null
+++ b/engine/servermsghandler.cpp
@@ -0,0 +1,993 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef SWDS
+#include "screen.h"
+#include "cl_main.h"
+#include "iprediction.h"
+#include "proto_oob.h"
+#include "demo.h"
+#include "tier0/icommandline.h"
+#include "ispatialpartitioninternal.h"
+#include "GameEventManager.h"
+#include "cdll_engine_int.h"
+#include "voice.h"
+#include "host_cmd.h"
+#include "server.h"
+#include "convar.h"
+#include "dt_recv_eng.h"
+#include "dt_common_eng.h"
+#include "LocalNetworkBackdoor.h"
+#include "vox.h"
+#include "sound.h"
+#include "r_efx.h"
+#include "r_local.h"
+#include "decal_private.h"
+#include "vgui_baseui_interface.h"
+#include "host_state.h"
+#include "cl_ents_parse.h"
+#include "eiface.h"
+#include "server.h"
+#include "cl_demoactionmanager.h"
+#include "decal.h"
+#include "r_decal.h"
+#include "materialsystem/imaterial.h"
+#include "EngineSoundInternal.h"
+#include "ivideomode.h"
+#include "download.h"
+#include "GameUI/IGameUI.h"
+#include "cl_demo.h"
+#include "cdll_engine_int.h"
+
+#if defined( REPLAY_ENABLED )
+#include "replay/ienginereplay.h"
+#include "replay_internal.h"
+#endif
+
+#include "audio_pch.h"
+
+#if defined ( _X360 )
+#include "matchmaking.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern IVEngineClient *engineClient;
+
+extern CNetworkStringTableContainer *networkStringTableContainerClient;
+extern CNetworkStringTableContainer *networkStringTableContainerServer;
+
+static ConVar cl_allowupload ( "cl_allowupload", "1", FCVAR_ARCHIVE, "Client uploads customization files" );
+static ConVar cl_voice_filter( "cl_voice_filter", "", 0, "Filter voice by name substring" ); // filter incoming voice data
+
+static ConVar *replay_voice_during_playback = NULL;
+
+extern ConCommand quit;
+
+void CClientState::ConnectionClosing( const char * reason )
+{
+ // if connected, shut down host
+ if ( m_nSignonState > SIGNONSTATE_NONE )
+ {
+ ConMsg( "Disconnect: %s.\n", reason );
+ if ( !Q_stricmp( reason, INVALID_STEAM_TICKET )
+ || !Q_stricmp( reason, INVALID_STEAM_LOGON_TICKET_CANCELED ) )
+ {
+ g_eSteamLoginFailure = STEAMLOGINFAILURE_BADTICKET;
+ }
+ else if ( !Q_stricmp( reason, INVALID_STEAM_LOGON_NOT_CONNECTED ) )
+ {
+ g_eSteamLoginFailure = STEAMLOGINFAILURE_NOSTEAMLOGIN;
+ }
+ else if ( !Q_stricmp( reason, INVALID_STEAM_LOGGED_IN_ELSEWHERE ) )
+ {
+ g_eSteamLoginFailure = STEAMLOGINFAILURE_LOGGED_IN_ELSEWHERE;
+ }
+ else if ( !Q_stricmp( reason, INVALID_STEAM_VACBANSTATE ) )
+ {
+ g_eSteamLoginFailure = STEAMLOGINFAILURE_VACBANNED;
+ }
+ else
+ {
+ g_eSteamLoginFailure = STEAMLOGINFAILURE_NONE;
+ }
+
+ if ( reason && reason[0] == '#' )
+ {
+ COM_ExplainDisconnection( true, reason );
+ }
+ else
+ {
+ COM_ExplainDisconnection( true, "Disconnect: %s.\n", reason );
+ }
+ SCR_EndLoadingPlaque();
+ Host_Disconnect( true, reason );
+ }
+}
+
+
+void CClientState::ConnectionCrashed( const char * reason )
+{
+ // if connected, shut down host
+ if ( m_nSignonState > SIGNONSTATE_NONE )
+ {
+ DebuggerBreakIfDebugging_StagingOnly();
+
+ COM_ExplainDisconnection( true, "Disconnect: %s.\n", reason );
+ SCR_EndLoadingPlaque();
+ Host_EndGame ( true, "%s", reason );
+ }
+}
+
+
+void CClientState::FileRequested(const char *fileName, unsigned int transferID)
+{
+ ConMsg( "File '%s' requested from server %s.\n", fileName, m_NetChannel->GetAddress() );
+
+ if ( !cl_allowupload.GetBool() )
+ {
+ ConMsg( "File uploading disabled.\n" );
+ m_NetChannel->DenyFile( fileName, transferID );
+ return;
+ }
+
+ // TODO check if file valid for uploading
+ m_NetChannel->SendFile( fileName, transferID );
+}
+
+void CClientState::FileReceived( const char * fileName, unsigned int transferID )
+{
+#ifndef _XBOX
+ // check if the client donwload manager requested this file
+ CL_FileReceived( fileName, transferID );
+ // notify client dll
+ if ( g_ClientDLL )
+ {
+ g_ClientDLL->FileReceived( fileName, transferID );
+ }
+#endif
+}
+
+void CClientState::FileDenied(const char *fileName, unsigned int transferID )
+{
+#ifndef _XBOX
+ // check if the file download manager requested that file
+ CL_FileDenied( fileName, transferID );
+#endif
+}
+
+void CClientState::FileSent( const char *fileName, unsigned int transferID )
+{
+}
+
+void CClientState::PacketStart( int incoming_sequence, int outgoing_acknowledged )
+{
+ // Ack'd incoming messages.
+ m_nCurrentSequence = incoming_sequence;
+ command_ack = outgoing_acknowledged;
+}
+
+
+void CClientState::PacketEnd()
+{
+ //
+ // we don't know if it is ok to save a demo message until
+ // after we have parsed the frame
+ //
+
+ // Play any sounds we received this packet
+ CL_DispatchSounds();
+
+ // Did we get any messages this tick (i.e., did we call PreEntityPacketReceived)?
+ if ( GetServerTickCount() != m_nDeltaTick )
+ return;
+
+ // How many commands total did we run this frame
+ int commands_acknowledged = command_ack - last_command_ack;
+
+// COM_Log( "cl.log", "Server ack'd %i commands this frame\n", commands_acknowledged );
+
+ //Msg( "%i/%i CL_PostReadMessages: last ack %i most recent %i acked %i commands\n",
+ // host_framecount, cl.tickcount,
+ // cl.last_command_ack,
+ // cl.netchan->outgoing_sequence - 1,
+ // commands_acknowledged );
+
+ // Highest command parsed from messages
+ last_command_ack = command_ack;
+
+ // Let prediction copy off pristine data and report any errors, etc.
+ g_pClientSidePrediction->PostNetworkDataReceived( commands_acknowledged );
+
+#ifndef _XBOX
+ demoaction->DispatchEvents();
+#endif
+}
+
+#undef CreateEvent
+void CClientState::Disconnect( const char *pszReason, bool bShowMainMenu )
+{
+#if defined( REPLAY_ENABLED )
+ if ( g_pClientReplayContext && IsConnected() )
+ {
+ g_pClientReplayContext->OnClientSideDisconnect();
+ }
+#endif
+
+ CBaseClientState::Disconnect( pszReason, bShowMainMenu );
+
+#ifndef _X360
+ IGameEvent *event = g_GameEventManager.CreateEvent( "client_disconnect" );
+ if ( event )
+ {
+ if ( !pszReason )
+ pszReason = "";
+ event->SetString( "message", pszReason );
+ g_GameEventManager.FireEventClientSide( event );
+ }
+#endif
+
+ // stop any demo activities
+#ifndef _XBOX
+ demoplayer->StopPlayback();
+ demorecorder->StopRecording();
+#endif
+
+ S_StopAllSounds( true );
+
+ R_DecalTermAll();
+
+ if ( m_nMaxClients > 1 )
+ {
+ if ( EngineVGui()->IsConsoleVisible() == false )
+ {
+ // start progress bar immediately for multiplayer level transitions
+ EngineVGui()->EnabledProgressBarForNextLoad();
+ }
+ }
+
+ CL_ClearState();
+
+#ifndef _XBOX
+ // End any in-progress downloads
+ CL_HTTPStop_f();
+#endif
+
+ // stop loading progress bar
+ if (bShowMainMenu)
+ {
+ SCR_EndLoadingPlaque();
+ }
+
+ // notify game ui dll of out-of-in-game status
+ EngineVGui()->NotifyOfServerDisconnect();
+
+ if (bShowMainMenu && !engineClient->IsDrawingLoadingImage() && (cl.demonum == -1))
+ {
+ // we're not in the middle of loading something, so show the UI
+ if ( EngineVGui() )
+ {
+ EngineVGui()->ActivateGameUI();
+ }
+ }
+
+ HostState_OnClientDisconnected();
+
+ // if we played a demo from the startdemos list, play next one
+ if (cl.demonum != -1)
+ {
+ CL_NextDemo();
+ }
+}
+
+
+bool CClientState::ProcessTick( NET_Tick *msg )
+{
+ int tick = msg->m_nTick;
+
+ m_NetChannel->SetRemoteFramerate( msg->m_flHostFrameTime, msg->m_flHostFrameTimeStdDeviation );
+
+ m_ClockDriftMgr.SetServerTick( tick );
+
+ // Remember this for GetLastTimeStamp().
+ m_flLastServerTickTime = tick * host_state.interval_per_tick;
+
+ // Use the server tick while reading network data (used for interpolation samples, etc).
+ g_ClientGlobalVariables.tickcount = tick;
+ g_ClientGlobalVariables.curtime = tick * host_state.interval_per_tick;
+ g_ClientGlobalVariables.frametime = (tick - oldtickcount) * host_state.interval_per_tick; // We used to call GetFrameTime() here, but 'insimulation' is always
+ // true so we have this code right in here to keep it simple.
+
+ return true;
+}
+
+
+bool CClientState::ProcessStringCmd( NET_StringCmd *msg )
+{
+ return CBaseClientState::ProcessStringCmd( msg );
+}
+
+
+bool CClientState::ProcessServerInfo( SVC_ServerInfo *msg )
+{
+ // Reset client state
+ CL_ClearState();
+
+ if ( !CBaseClientState::ProcessServerInfo( msg ) )
+ {
+ Disconnect( "CBaseClientState::ProcessServerInfo failed", true );
+ return false;
+ }
+#ifndef _XBOX
+ if ( demoplayer->IsPlayingBack() )
+ {
+ // Because a server doesn't run during
+ // demoplayback, but the decal system relies on this...
+ m_nServerCount = gHostSpawnCount;
+ }
+ else
+ {
+ // tell demo recorder that new map is loaded and we are receiving
+ // it's signon data (will be written into extra demo header file)
+ demorecorder->SetSignonState( SIGNONSTATE_NEW );
+ }
+#endif
+ // is server a HLTV proxy ?
+ ishltv = msg->m_bIsHLTV;
+
+#if defined( REPLAY_ENABLED )
+ // is server a replay proxy ?
+ isreplay = msg->m_bIsReplay;
+#endif
+
+ // The MD5 of the server map must match the MD5 of the client map. or else
+ // the client is probably cheating.
+ V_memcpy( serverMD5.bits, msg->m_nMapMD5.bits, MD5_DIGEST_LENGTH );
+
+ // Multiplayer game?
+ if ( m_nMaxClients > 1 )
+ {
+ if ( mp_decals.GetInt() < r_decals.GetInt() )
+ {
+ r_decals.SetValue( mp_decals.GetInt() );
+ }
+ }
+
+ g_ClientGlobalVariables.maxClients = m_nMaxClients;
+ g_ClientGlobalVariables.network_protocol = msg->m_nProtocol;
+
+#ifdef SHARED_NET_STRING_TABLES
+ // use same instance of StringTableContainer as the server does
+ m_StringTableContainer = networkStringTableContainerServer;
+ CL_HookClientStringTables();
+#else
+ // use own instance of StringTableContainer
+ m_StringTableContainer = networkStringTableContainerClient;
+#endif
+
+ CL_ReallocateDynamicData( m_nMaxClients );
+
+ if ( sv.IsPaused() )
+ {
+ if ( msg->m_fTickInterval != host_state.interval_per_tick )
+ {
+ Host_Error( "Expecting interval_per_tick %f, got %f\n",
+ host_state.interval_per_tick, msg->m_fTickInterval );
+ return false;
+ }
+ }
+ else
+ {
+ host_state.interval_per_tick = msg->m_fTickInterval;
+ }
+
+ // Re-init hud video, especially if we changed game directories
+ ClientDLL_HudVidInit();
+
+ // Don't verify the map and player .mdl crc's until after any missing resources have
+ // been downloaded. This will still occur before requesting the rest of the signon.
+
+
+ gHostSpawnCount = m_nServerCount;
+
+ videomode->MarkClientViewRectDirty(); // leave intermission full screen
+ return true;
+}
+
+bool CClientState::ProcessClassInfo( SVC_ClassInfo *msg )
+{
+ if ( msg->m_bCreateOnClient )
+ {
+#ifndef _XBOX
+ if ( !demoplayer->IsPlayingBack() )
+#endif
+ {
+ // Create all of the send tables locally
+ DataTable_CreateClientTablesFromServerTables();
+
+ // Now create all of the server classes locally, too
+ DataTable_CreateClientClassInfosFromServerClasses( this );
+
+ // store the current data tables in demo file to make sure
+ // they are the same during playback
+#ifndef _XBOX
+ demorecorder->RecordServerClasses( serverGameDLL->GetAllServerClasses() );
+#endif
+ }
+
+ LinkClasses(); // link server and client classes
+ }
+ else
+ {
+ CBaseClientState::ProcessClassInfo( msg );
+ }
+
+#ifdef DEDICATED
+ bool bAllowMismatches = false;
+#else
+ bool bAllowMismatches = ( demoplayer && demoplayer->IsPlayingBack() );
+#endif // DEDICATED
+
+ if ( !RecvTable_CreateDecoders( serverGameDLL->GetStandardSendProxies(), bAllowMismatches ) ) // create receive table decoders
+ {
+ Host_EndGame( true, "CL_ParseClassInfo_EndClasses: CreateDecoders failed.\n" );
+ return false;
+ }
+
+#ifndef _XBOX
+ if ( !demoplayer->IsPlayingBack() )
+#endif
+ {
+ CLocalNetworkBackdoor::InitFastCopy();
+ }
+
+ return true;
+}
+
+bool CClientState::ProcessSetPause( SVC_SetPause *msg )
+{
+ CBaseClientState::ProcessSetPause( msg );
+
+ return true;
+}
+
+bool CClientState::ProcessSetPauseTimed( SVC_SetPauseTimed *msg )
+{
+ CBaseClientState::ProcessSetPauseTimed( msg );
+
+ return true;
+}
+
+bool CClientState::ProcessVoiceInit( SVC_VoiceInit *msg )
+{
+#if !defined( NO_VOICE )//#ifndef _XBOX
+ if ( msg->m_szVoiceCodec[0] == 0 )
+ {
+ Voice_Deinit();
+ }
+ else
+ {
+ Voice_Init( msg->m_szVoiceCodec, msg->m_nSampleRate );
+ }
+#endif
+ return true;
+}
+
+ConVar voice_debugfeedback( "voice_debugfeedback", "0" );
+
+bool CClientState::ProcessVoiceData( SVC_VoiceData *msg )
+{
+ char chReceived[4096];
+ int bitsRead = msg->m_DataIn.ReadBitsClamped( chReceived, msg->m_nLength );
+
+#if defined ( _X360 )
+ DWORD dwLength = msg->m_nLength;
+ XUID xuid = msg->m_xuid;
+ Audio_GetXVoice()->PlayIncomingVoiceData( xuid, (byte*)chReceived, dwLength );
+
+ if ( voice_debugfeedback.GetBool() )
+ {
+ Msg( "Received voice from: %d\n", msg->m_nFromClient + 1 );
+ }
+
+ return true;
+#endif
+
+#if !defined( NO_VOICE )//#ifndef _XBOX
+ int iEntity = msg->m_nFromClient + 1;
+ if ( iEntity == (m_nPlayerSlot + 1) )
+ {
+ Voice_LocalPlayerTalkingAck();
+ }
+
+ player_info_t playerinfo;
+ engineClient->GetPlayerInfo( iEntity, &playerinfo );
+
+ if ( Q_strlen( cl_voice_filter.GetString() ) > 0 && Q_strstr( playerinfo.name, cl_voice_filter.GetString() ) == NULL )
+ return true;
+
+#if defined( REPLAY_ENABLED )
+ extern IEngineClientReplay *g_pEngineClientReplay;
+ bool bInReplay = engineClient->IsPlayingDemo() && g_pEngineClientReplay && g_pEngineClientReplay->IsPlayingReplayDemo();
+
+ if ( replay_voice_during_playback == NULL )
+ {
+ replay_voice_during_playback = g_pCVar->FindVar( "replay_voice_during_playback" );
+ Assert( replay_voice_during_playback != NULL );
+ }
+
+ // Don't play back voice data during replay unless the client specified it to.
+ if ( bInReplay && replay_voice_during_playback && !replay_voice_during_playback->GetBool() )
+ return true;
+#endif
+
+ // Data length can be zero when the server is just acking a client's voice data.
+ if ( bitsRead == 0 )
+ return true;
+
+ if ( !Voice_Enabled() )
+ {
+ return true;
+ }
+
+ // Have we already initialized the channels for this guy?
+ int nChannel = Voice_GetChannel( iEntity );
+ if ( nChannel == VOICE_CHANNEL_ERROR )
+ {
+ // Create a channel in the voice engine and a channel in the sound engine for this guy.
+ nChannel = Voice_AssignChannel( iEntity, msg->m_bProximity );
+ if ( nChannel == VOICE_CHANNEL_ERROR )
+ {
+ // If they used -nosound, then it's not a problem.
+ if ( S_IsInitted() )
+ ConDMsg("ProcessVoiceData: Voice_AssignChannel failed for client %d!\n", iEntity-1);
+
+ return true;
+ }
+ }
+
+ // Give the voice engine the data (it in turn gives it to the mixer for the sound engine).
+ Voice_AddIncomingData( nChannel, chReceived, Bits2Bytes( bitsRead ), m_nCurrentSequence );
+#endif
+ return true;
+};
+
+bool CClientState::ProcessPrefetch( SVC_Prefetch *msg )
+{
+ char const *soundname = cl.GetSoundName( msg->m_nSoundIndex );
+ if ( soundname && soundname [ 0 ] )
+ {
+ EngineSoundClient()->PrefetchSound( soundname );
+ }
+ return true;
+}
+
+void CClientState::ProcessSoundsWithProtoVersion( SVC_Sounds *msg, CUtlVector< SoundInfo_t > &sounds, int nProtoVersion )
+{
+ SoundInfo_t defaultSound; defaultSound.SetDefault();
+ SoundInfo_t *pDeltaSound = &defaultSound;
+
+ // Max is 32 in multiplayer and 255 in singleplayer
+ // Reserve this memory up front so it doesn't realloc under pDeltaSound pointing at it
+ sounds.EnsureCapacity( 256 );
+
+ for ( int i = 0; i < msg->m_nNumSounds; i++ )
+ {
+ int nSound = sounds.AddToTail();
+ SoundInfo_t *pSound = &(sounds[ nSound ]);
+
+ pSound->ReadDelta( pDeltaSound, msg->m_DataIn, nProtoVersion );
+
+ pDeltaSound = pSound; // copy delta values
+
+ if ( msg->m_bReliableSound )
+ {
+ // client is incrementing the reliable sequence numbers itself
+ m_nSoundSequence = ( m_nSoundSequence + 1 ) & SOUND_SEQNUMBER_MASK;
+ Assert ( pSound->nSequenceNumber == 0 );
+ pSound->nSequenceNumber = m_nSoundSequence;
+ }
+ }
+}
+
+bool CClientState::ProcessSounds( SVC_Sounds *msg )
+{
+ if ( msg->m_DataIn.IsOverflowed() )
+ {
+ // Overflowed before we even started! There's nothing we can do with this buffer.
+ return false;
+ }
+
+ CUtlVector< SoundInfo_t > sounds;
+
+ int startbit = msg->m_DataIn.GetNumBitsRead();
+
+ // Process with the reported proto version
+ ProcessSoundsWithProtoVersion( msg, sounds, g_ClientGlobalVariables.network_protocol );
+
+ int nRelativeBitsRead = msg->m_DataIn.GetNumBitsRead() - startbit;
+
+ if ( msg->m_nLength != nRelativeBitsRead || msg->m_DataIn.IsOverflowed() )
+ {
+ // The number of bits read is not what we expect!
+ sounds.RemoveAll();
+
+ int nFallbackProtocol = 0;
+
+ // If the demo file thinks it's version 18 or 19, it might actually be the other.
+ // This is a work around for when we broke compatibility Halloween 2011.
+ // -Jeep
+ if ( g_ClientGlobalVariables.network_protocol == PROTOCOL_VERSION_18 )
+ {
+ nFallbackProtocol = PROTOCOL_VERSION_19;
+ }
+ else if ( g_ClientGlobalVariables.network_protocol == PROTOCOL_VERSION_19 )
+ {
+ nFallbackProtocol = PROTOCOL_VERSION_18;
+ }
+
+ if ( nFallbackProtocol != 0 )
+ {
+ // Roll back our buffer to before we read those bits and wipe the overflow flag
+ msg->m_DataIn.Reset();
+ msg->m_DataIn.Seek( startbit );
+
+ // Try again with the fallback version
+ ProcessSoundsWithProtoVersion( msg, sounds, nFallbackProtocol );
+
+ nRelativeBitsRead = msg->m_DataIn.GetNumBitsRead() - startbit;
+ }
+ }
+
+ if ( msg->m_nLength == nRelativeBitsRead )
+ {
+ // Now that we know the bits were read correctly, add all the sounds
+ for ( int i = 0; i < sounds.Count(); ++i )
+ {
+ // Add all received sounds to sorted queue (sounds may arrive in multiple messages),
+ // will be processed after all packets have been completely parsed
+ CL_AddSound( sounds[ i ] );
+ }
+
+ // read the correct number of bits
+ return true;
+ }
+
+ // Wipe the overflow flag and set the buffer to how much we expected to read
+ msg->m_DataIn.Reset();
+ msg->m_DataIn.Seek( startbit + msg->m_nLength );
+
+ // didn't read the correct number of bits with either proto version attempt
+ return false;
+}
+
+
+bool CClientState::ProcessFixAngle( SVC_FixAngle *msg )
+{
+ for (int i=0 ; i<3 ; i++)
+ {
+ // Clamp between -180 and 180
+ if (msg->m_Angle[i]>180)
+ {
+ msg->m_Angle[i] -= 360;
+ }
+ }
+
+ if ( msg->m_bRelative )
+ {
+ // Update running counter
+ addangletotal += msg->m_Angle[YAW];
+
+ AddAngle a;
+ a.total = addangletotal;
+ a.starttime = m_flLastServerTickTime;
+
+ addangle.AddToTail( a );
+ }
+ else
+ {
+
+ viewangles = msg->m_Angle;
+ }
+
+ return true;
+}
+
+
+bool CClientState::ProcessCrosshairAngle( SVC_CrosshairAngle *msg )
+{
+ g_ClientDLL->SetCrosshairAngle( msg->m_Angle );
+
+ return true;
+}
+
+
+bool CClientState::ProcessBSPDecal( SVC_BSPDecal *msg )
+{
+ model_t * model;
+
+ if ( msg->m_nEntityIndex )
+ {
+ model = GetModel( msg->m_nModelIndex );
+ }
+ else
+ {
+ model = host_state.worldmodel;
+ if ( !model )
+ {
+ Warning( "ProcessBSPDecal: Trying to project on world before host_state.worldmodel is set!!!\n" );
+ }
+ }
+
+ if ( model == NULL )
+ {
+ IMaterial *mat = Draw_DecalMaterial( msg->m_nDecalTextureIndex );
+ char const *matname = "???";
+ if ( mat )
+ {
+ matname = mat->GetName();
+ }
+
+ Warning( "Warning! Static BSP decal (%s), on NULL model index %i for entity index %i.\n",
+ matname,
+ msg->m_nModelIndex,
+ msg->m_nEntityIndex );
+ return true;
+ }
+
+ if (r_decals.GetInt())
+ {
+ g_pEfx->DecalShoot(
+ msg->m_nDecalTextureIndex,
+ msg->m_nEntityIndex,
+ model,
+ vec3_origin,
+ vec3_angle,
+ msg->m_Pos,
+ NULL,
+ msg->m_bLowPriority ? 0 : FDECAL_PERMANENT );
+ }
+
+ return true;
+}
+
+
+bool CClientState::ProcessGameEvent(SVC_GameEvent *msg)
+{
+ tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
+
+ int startbit = msg->m_DataIn.GetNumBitsRead();
+
+ IGameEvent *event = g_GameEventManager.UnserializeEvent( &msg->m_DataIn );
+
+ int length = msg->m_DataIn.GetNumBitsRead() - startbit;
+
+ if ( length != msg->m_nLength )
+ {
+ DevMsg("CClientState::ProcessGameEvent: KeyValue length mismatch.\n" );
+ return true;
+ }
+
+ if ( !event )
+ {
+ DevMsg("CClientState::ProcessGameEvent: UnserializeKeyValue failed.\n" );
+ return true;
+ }
+
+ g_GameEventManager.FireEventClientSide( event );
+
+ return true;
+}
+
+
+bool CClientState::ProcessUserMessage(SVC_UserMessage *msg)
+{
+ // buffer for incoming user message
+ ALIGN4 byte userdata[ MAX_USER_MSG_DATA ] ALIGN4_POST = { 0 };
+ bf_read userMsg( "UserMessage(read)", userdata, sizeof( userdata ) );
+ int bitsRead = msg->m_DataIn.ReadBitsClamped( userdata, msg->m_nLength );
+ userMsg.StartReading( userdata, Bits2Bytes( bitsRead ) );
+
+ // dispatch message to client.dll
+ if ( !g_ClientDLL->DispatchUserMessage( msg->m_nMsgType, userMsg ) )
+ {
+ ConMsg( "Couldn't dispatch user message (%i)\n", msg->m_nMsgType );
+ return false;
+ }
+
+ return true;
+}
+
+
+bool CClientState::ProcessEntityMessage(SVC_EntityMessage *msg)
+{
+ // Look up entity
+ IClientNetworkable *entity = entitylist->GetClientNetworkable( msg->m_nEntityIndex );
+
+ if ( !entity )
+ {
+ // message was send to use, even we don't have this entity TODO change that on server side
+ return true;
+ }
+
+ // route to entity
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ // buffer for incoming user message
+ ALIGN4 byte entityData[ MAX_ENTITY_MSG_DATA ] ALIGN4_POST = { 0 };
+ bf_read entMsg( "EntityMessage(read)", entityData, sizeof( entityData ) );
+ int bitsRead = msg->m_DataIn.ReadBitsClamped( entityData, msg->m_nLength );
+ entMsg.StartReading( entityData, Bits2Bytes( bitsRead ) );
+
+ entity->ReceiveMessage( msg->m_nClassID, entMsg );
+
+ return true;
+}
+
+
+bool CClientState::ProcessPacketEntities( SVC_PacketEntities *msg )
+{
+ if ( !msg->m_bIsDelta )
+ {
+ // Delta too old or is initial message
+#ifndef _XBOX
+ // we can start recording now that we've received an uncompressed packet
+ demorecorder->SetSignonState( SIGNONSTATE_FULL );
+#endif
+ // Tell prediction that we're recreating entities due to an uncompressed packet arriving
+ if ( g_pClientSidePrediction )
+ {
+ g_pClientSidePrediction->OnReceivedUncompressedPacket();
+ }
+ }
+ else
+ {
+ if ( m_nDeltaTick == -1 )
+ {
+ // we requested a full update but still got a delta compressed packet. ignore it.
+ return true;
+ }
+
+ // Preprocessing primarily does client prediction. So if we're processing deltas--do it
+ // otherwise, we're about to be told exactly what the state of everything is--so skip it.
+ CL_PreprocessEntities(); // setup client prediction
+ }
+
+ TRACE_PACKET(( "CL Receive (%d <-%d)\n", m_nCurrentSequence, msg->m_nDeltaFrom ));
+ TRACE_PACKET(( "CL Num Ents (%d)\n", msg->m_nUpdatedEntries ));
+
+ if ( g_pLocalNetworkBackdoor )
+ {
+ if ( m_nSignonState == SIGNONSTATE_SPAWN )
+ {
+ // We are done with signon sequence.
+ SetSignonState( SIGNONSTATE_FULL, m_nServerCount );
+ }
+
+ // ignore message, all entities are transmitted using fast local memcopy routines
+ m_nDeltaTick = GetServerTickCount();
+ return true;
+ }
+
+ if ( !CL_ProcessPacketEntities ( msg ) )
+ return false;
+
+ return CBaseClientState::ProcessPacketEntities( msg );
+}
+
+
+bool CClientState::ProcessTempEntities( SVC_TempEntities *msg )
+{
+ bool bReliable = false;
+
+ float fire_time = cl.GetTime();
+
+#ifndef _XBOX
+ // delay firing temp ents by cl_interp in multiplayer or demoplayback
+ if ( cl.m_nMaxClients > 1 || demoplayer->IsPlayingBack() )
+ {
+ float flInterpAmount = GetClientInterpAmount();
+ fire_time += flInterpAmount;
+ }
+#endif
+
+ if ( msg->m_nNumEntries == 0 )
+ {
+ bReliable = true;
+ msg->m_nNumEntries = 1;
+ }
+
+ int flags = bReliable ? FEV_RELIABLE : 0;
+
+ // Don't actually queue unreliable events if playing a demo and skipping ahead
+#ifndef _XBOX
+ if ( !bReliable && demoplayer->IsSkipping() )
+ {
+ return true;
+ }
+#endif
+ bf_read &buffer = msg->m_DataIn; // shortcut
+
+ int classID = -1;
+ void *from = NULL;
+ C_ServerClassInfo *pServerClass = NULL;
+ ClientClass *pClientClass = NULL;
+ ALIGN4 unsigned char data[CEventInfo::MAX_EVENT_DATA] ALIGN4_POST;
+ bf_write toBuf( data, sizeof(data) );
+ CEventInfo *ei = NULL;
+
+ for (int i = 0; i < msg->m_nNumEntries; i++ )
+ {
+ float delay = 0.0f;
+
+ if ( buffer.ReadOneBit() )
+ {
+ delay = (float)buffer.ReadSBitLong( 8 ) / 100.0f;
+ }
+
+ toBuf.Reset();
+
+ if ( buffer.ReadOneBit() )
+ {
+ from = NULL; // full update
+
+ classID = buffer.ReadUBitLong( m_nServerClassBits ); // classID
+
+ // Look up the client class, etc.
+
+ // Match the server classes to the client classes.
+ pServerClass = m_pServerClasses ? &m_pServerClasses[ classID - 1 ] : NULL;
+
+ if ( !pServerClass )
+ {
+ DevMsg("CL_QueueEvent: missing server class info for %i.\n", classID - 1 );
+ return false;
+ }
+
+ // See if the client .dll has a handler for this class
+ pClientClass = FindClientClass( pServerClass->m_ClassName );
+
+ if ( !pClientClass || !pClientClass->m_pRecvTable )
+ {
+ DevMsg("CL_QueueEvent: missing client receive table for %s.\n", pServerClass->m_ClassName );
+ return false;
+ }
+
+ RecvTable_MergeDeltas( pClientClass->m_pRecvTable, NULL, &buffer, &toBuf );
+ }
+ else
+ {
+ Assert( ei );
+
+ unsigned int buffer_size = PAD_NUMBER( Bits2Bytes( ei->bits ), 4 );
+ bf_read fromBuf( ei->pData, buffer_size );
+
+ RecvTable_MergeDeltas( pClientClass->m_pRecvTable, &fromBuf, &buffer, &toBuf );
+ }
+
+ // Add a slot
+ ei = &cl.events[ cl.events.AddToTail() ];
+
+ Assert( ei );
+
+ int size = Bits2Bytes(toBuf.GetNumBitsWritten() );
+
+ ei->classID = classID;
+ ei->fire_delay = fire_time + delay;
+ ei->flags = flags;
+ ei->pClientClass = pClientClass;
+ ei->bits = toBuf.GetNumBitsWritten();
+
+ // deltaBitsReader.ReadNextPropIndex reads uint32s, so make sure we alloc in 4-byte chunks.
+ ei->pData = new byte[ ALIGN_VALUE( size, 4 ) ]; // copy raw data
+ Q_memcpy( ei->pData, data, size );
+ }
+
+ return true;
+}
+
+#endif // swds