diff options
Diffstat (limited to 'engine/servermsghandler.cpp')
| -rw-r--r-- | engine/servermsghandler.cpp | 993 |
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 |