//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #ifndef NETMESSAGES_H #define NETMESSAGES_H #ifdef _WIN32 #pragma once #pragma warning(disable : 4100) // unreferenced formal parameter #endif #include #include #include #include #include #include "qlimits.h" #include "mathlib/vector.h" #include #include #include #include "protocol.h" #include #include #include #include "networksystem/inetworksystem.h" #include #include #include #include #include "tier1/utlstring.h" #include "tier1/tokenset.h" #include "netmessages_signon.h" // eliminates a conflict with TYPE_BOOL in OSX #ifdef TYPE_BOOL #undef TYPE_BOOL #endif #include "netmessages.pb.h" #if !defined( _X360 ) #include "xbox/xboxstubs.h" #endif template< int msgType, typename PB_OBJECT_TYPE, int groupType = INetChannelInfo::GENERIC, bool bReliable = true > class CNetMessagePB : public INetMessage, public PB_OBJECT_TYPE { public: typedef CNetMessagePB< msgType, PB_OBJECT_TYPE, groupType, bReliable > MyType_t; typedef PB_OBJECT_TYPE PBType_t; static const int sk_Type = msgType; CNetMessagePB() : m_bReliable( bReliable ) { } virtual ~CNetMessagePB() { } virtual bool ReadFromBuffer( bf_read &buffer ) { int size = buffer.ReadVarInt32(); if ( size < 0 || size > NET_MAX_PAYLOAD ) { return false; } // Check its valid if ( size > buffer.GetNumBytesLeft() ) { return false; } // If the read buffer is byte aligned, we can parse right out of it if ( ( buffer.GetNumBitsRead() % 8 ) == 0 ) { bool parseResult = PB_OBJECT_TYPE::ParseFromArray( buffer.GetBasePointer() + buffer.GetNumBytesRead(), size ); buffer.SeekRelative( size * 8 ); return parseResult; } // otherwise we have to do a temp allocation so we can read it all shifted #ifdef NET_SHOW_UNALIGNED_MSGS DevMsg("Warning: unaligned read of protobuf message %s (%d bytes)\n", PB_OBJECT_TYPE::GetTypeName().c_str(), size ); #endif void *parseBuffer = stackalloc( size ); if ( !buffer.ReadBytes( parseBuffer, size ) ) { return false; } if ( ! PB_OBJECT_TYPE::ParseFromArray( parseBuffer, size ) ) { return false; } return true; } virtual bool WriteToBuffer( bf_write &buffer ) const { if ( !PB_OBJECT_TYPE::IsInitialized() ) { Msg("WriteToBuffer Message %s is not initialized! Probably missing required fields!\n", PB_OBJECT_TYPE::GetTypeName().c_str() ); } int size = PB_OBJECT_TYPE::ByteSize(); // If the write is byte aligned we can go direct if ( ( buffer.GetNumBitsWritten() % 8 ) == 0 ) { int sizeWithHeader = size + 1 + buffer.ByteSizeVarInt32( GetType() ) + buffer.ByteSizeVarInt32( size ); if ( buffer.GetNumBytesLeft() >= sizeWithHeader ) { buffer.WriteVarInt32( GetType() ); buffer.WriteVarInt32( size ); if ( !PB_OBJECT_TYPE::SerializeWithCachedSizesToArray( ( google::protobuf::uint8 * )buffer.GetData() + buffer.GetNumBytesWritten() ) ) { return false; } // Tell the buffer we just splatted into it buffer.SeekToBit( buffer.GetNumBitsWritten() + ( size * 8 ) ); return true; } // Won't fit return false; } // otherwise we have to do a temp allocation so we can write it all shifted #ifdef NET_SHOW_UNALIGNED_MSGS DevMsg("Warning: unaligned write of protobuf message %s (%d bytes)\n", PB_OBJECT_TYPE::GetTypeName().c_str(), size ); #endif void *serializeBuffer = stackalloc( size ); if ( ! PB_OBJECT_TYPE::SerializeWithCachedSizesToArray( ( google::protobuf::uint8 * )serializeBuffer ) ) { return false; } buffer.WriteVarInt32( GetType() ); buffer.WriteVarInt32( size ); return buffer.WriteBytes( serializeBuffer, size ); } virtual const char *ToString() const { m_toString = PB_OBJECT_TYPE::DebugString(); return m_toString.c_str(); } virtual int GetType() const { return msgType; } virtual size_t GetSize() const { return sizeof( *this ); } virtual const char *GetName() const { if ( s_typeName.empty() ) { s_typeName = PB_OBJECT_TYPE::GetTypeName(); } return s_typeName.c_str(); } virtual int GetGroup() const { return groupType; } virtual void SetReliable( bool state ) { m_bReliable = state; } virtual bool IsReliable() const { return m_bReliable; } virtual INetMessage *Clone() const { MyType_t *pClone = new MyType_t; pClone->CopyFrom( *this ); pClone->m_bReliable = m_bReliable; return pClone; } protected: bool m_bReliable; // true if message should be sent reliable mutable std::string m_toString; // cached copy of ToString() static std::string s_typeName; }; template< int msgType, typename PB_OBJECT_TYPE, int groupType , bool bReliable > std::string CNetMessagePB< msgType, PB_OBJECT_TYPE, groupType , bReliable >::s_typeName; class CNetMessageBinder { public: CNetMessageBinder() : m_pBind( NULL ) { } ~CNetMessageBinder() { delete m_pBind; } template< class _N > void Bind( INetChannel *pNetChannel, CUtlDelegate< bool ( const typename _N::PBType_t & obj ) > handler ) { delete m_pBind; m_pBind = new BindParams<_N>( pNetChannel, handler ); } void Unbind() { delete m_pBind; m_pBind = NULL; } bool IsBound() const { return m_pBind != NULL; } private: template < class _N > struct BindParams : public INetMessageBinder { BindParams( INetChannel *pNetChannel, CUtlDelegate< bool ( const typename _N::PBType_t & obj ) > handler ) : m_NetChannel( pNetChannel ) , m_handler( handler ) { if ( m_NetChannel ) { m_NetChannel->RegisterMessage( this ); } } virtual ~BindParams() { if ( m_NetChannel ) { m_NetChannel->UnregisterMessage( this ); } } virtual int GetType( void ) const { return _N::sk_Type; } virtual void SetNetChannel(INetChannel * netchan) { if( m_NetChannel != netchan ) { if( m_NetChannel ) m_NetChannel->UnregisterMessage( this ); m_NetChannel = netchan; if( m_NetChannel ) m_NetChannel->RegisterMessage( this ); } } virtual INetMessage *CreateFromBuffer( bf_read &buffer ) { INetMessage *pMsg = new typename _N::MyType_t; if ( !pMsg->ReadFromBuffer( buffer ) ) { delete pMsg; return NULL; } return pMsg; } virtual bool Process( const INetMessage &src ) { const typename _N::MyType_t &typedSrc = static_cast< const typename _N::MyType_t & >( src ); Assert( m_handler ); if( m_handler ) { return m_handler( static_cast< typename _N::PBType_t const & >( typedSrc ) ); } return false; } INetChannel *m_NetChannel; // netchannel this message is from/for CUtlDelegate< bool ( const typename _N::PBType_t & obj ) > m_handler; }; INetMessageBinder *m_pBind; }; /////////////////////////////////////////////////////////////////////////////////////// // bidirectional net messages: /////////////////////////////////////////////////////////////////////////////////////// class CNETMsg_Tick_t : public CNetMessagePB< net_Tick, CNETMsg_Tick > { public: static float FrametimeToFloat( uint32 frametime ) { return ( float )frametime / 1000000.0f; } CNETMsg_Tick_t( int tick, float host_computationtime, float host_computationtime_stddeviation, float host_framestarttime_std_deviation ) { SetReliable( false ); set_tick( tick ); set_host_computationtime( MIN( ( uint32 )( 1000000.0 * host_computationtime ), 1000000u ) ); set_host_computationtime_std_deviation( MIN( ( uint32 )( 1000000.0 * host_computationtime_stddeviation ), 1000000u ) ); set_host_framestarttime_std_deviation( MIN( ( uint32 )( 1000000.0 * host_framestarttime_std_deviation ), 1000000u ) ); } }; class CNETMsg_StringCmd_t : public CNetMessagePB< net_StringCmd, CNETMsg_StringCmd, INetChannelInfo::STRINGCMD > { public: CNETMsg_StringCmd_t( const char *command ) { set_command( command ); } }; class CNETMsg_PlayerAvatarData_t : public CNetMessagePB< net_PlayerAvatarData, CNETMsg_PlayerAvatarData, INetChannelInfo::PAINTMAP > { // 12 KB player avatar 64x64 rgb only no alpha // WARNING-WARNING-WARNING // This message is extremely large for our net channels // and must be pumped through special fragmented waiting list // via chunk-based ack mechanism! // See: INetChannel::EnqueueVeryLargeAsyncTransfer // WARNING-WARNING-WARNING public: CNETMsg_PlayerAvatarData_t() {} CNETMsg_PlayerAvatarData_t( uint32 unAccountID, void const *pvData, uint32 cbData ) { set_accountid( unAccountID ); set_rgb( ( const char * ) pvData, cbData ); } }; class CNETMsg_SignonState_t : public CNetMessagePB< net_SignonState, CNETMsg_SignonState, INetChannelInfo::SIGNON > { public: CNETMsg_SignonState_t( int state, int spawncount ) { set_signon_state( state ); set_spawn_count( spawncount ); set_num_server_players( 0 ); } }; inline void NetMsgSetCVarUsingDictionary( CMsg_CVars::CVar *convar, char const * name, char const * value ) { convar->set_value( value ); if ( 0 ) ( void ) 0; /** Removed for partner depot **/ else { #ifdef _DEBUG DevWarning( "Missing dictionary entry for cvar '%s'\n", name ); #endif convar->set_name( name ); } } inline void NetMsgExpandCVarUsingDictionary( CMsg_CVars::CVar *convar ) { if ( convar->has_name() ) return; switch ( convar->dictionary_name() ) { case 0: return; /** Removed for partner depot **/ default: DevWarning( "Invalid dictionary entry for cvar # %d\n", convar->dictionary_name() ); convar->set_name( "undefined" ); break; } } inline const char * NetMsgGetCVarUsingDictionary( CMsg_CVars::CVar const &convar ) { if ( convar.has_name() ) return convar.name().c_str(); switch ( convar.dictionary_name() ) { case 0: return ""; /** Removed for partner depot **/ default: DevWarning( "Invalid dictionary entry for cvar # %d\n", convar.dictionary_name() ); return "undefined"; } } class CNETMsg_SetConVar_t : public CNetMessagePB< net_SetConVar, CNETMsg_SetConVar, INetChannelInfo::STRINGCMD > { public: CNETMsg_SetConVar_t() {} CNETMsg_SetConVar_t( const char * name, const char * value ) { AddToTail( name, value ); } void AddToTail( const char * name, const char * value ) { NetMsgSetCVarUsingDictionary( mutable_convars()->add_cvars(), name, value ); } }; typedef CNetMessagePB< net_NOP, CNETMsg_NOP > CNETMsg_NOP_t; typedef CNetMessagePB< net_Disconnect, CNETMsg_Disconnect > CNETMsg_Disconnect_t; typedef CNetMessagePB< net_File, CNETMsg_File > CNETMsg_File_t; typedef CNetMessagePB< net_SplitScreenUser, CNETMsg_SplitScreenUser > CNETMsg_SplitScreenUser_t; /////////////////////////////////////////////////////////////////////////////////////// // Client messages: Sent from the client to the server /////////////////////////////////////////////////////////////////////////////////////// typedef CNetMessagePB< clc_SplitPlayerConnect, CCLCMsg_SplitPlayerConnect > CCLCMsg_SplitPlayerConnect_t; typedef CNetMessagePB< clc_Move, CCLCMsg_Move, INetChannelInfo::MOVE, false > CCLCMsg_Move_t; typedef CNetMessagePB< clc_ClientInfo, CCLCMsg_ClientInfo > CCLCMsg_ClientInfo_t; typedef CNetMessagePB< clc_VoiceData, CCLCMsg_VoiceData, INetChannelInfo::VOICE, false > CCLCMsg_VoiceData_t; typedef CNetMessagePB< clc_BaselineAck, CCLCMsg_BaselineAck > CCLCMsg_BaselineAck_t; typedef CNetMessagePB< clc_ListenEvents, CCLCMsg_ListenEvents > CCLCMsg_ListenEvents_t; typedef CNetMessagePB< clc_RespondCvarValue, CCLCMsg_RespondCvarValue > CCLCMsg_RespondCvarValue_t; typedef CNetMessagePB< clc_LoadingProgress, CCLCMsg_LoadingProgress > CCLCMsg_LoadingProgress_t; typedef CNetMessagePB< clc_CmdKeyValues, CCLCMsg_CmdKeyValues > CCLCMsg_CmdKeyValues_t; typedef CNetMessagePB< clc_HltvReplay, CCLCMsg_HltvReplay > CCLCMsg_HltvReplay_t; class CCLCMsg_FileCRCCheck_t : public CNetMessagePB< clc_FileCRCCheck, CCLCMsg_FileCRCCheck > { public: // Warning: These routines may use the va() function... static void SetPath( CCLCMsg_FileCRCCheck& msg, const char *path ); static const char *GetPath( const CCLCMsg_FileCRCCheck& msg ); static void SetFileName( CCLCMsg_FileCRCCheck& msg, const char *fileName ); static const char *GetFileName( const CCLCMsg_FileCRCCheck& msg ); }; /////////////////////////////////////////////////////////////////////////////////////// // Server messages: Sent from the server to the client /////////////////////////////////////////////////////////////////////////////////////// typedef CNetMessagePB< svc_ServerInfo, CSVCMsg_ServerInfo, INetChannelInfo::SIGNON > CSVCMsg_ServerInfo_t; typedef CNetMessagePB< svc_ClassInfo, CSVCMsg_ClassInfo, INetChannelInfo::SIGNON > CSVCMsg_ClassInfo_t; typedef CNetMessagePB< svc_SendTable, CSVCMsg_SendTable, INetChannelInfo::SIGNON > CSVCMsg_SendTable_t; typedef CNetMessagePB< svc_Print, CSVCMsg_Print, INetChannelInfo::GENERIC, false > CSVCMsg_Print_t; typedef CNetMessagePB< svc_SetPause, CSVCMsg_SetPause > CSVCMsg_SetPause_t; typedef CNetMessagePB< svc_SetView, CSVCMsg_SetView > CSVCMsg_SetView_t; typedef CNetMessagePB< svc_CreateStringTable, CSVCMsg_CreateStringTable, INetChannelInfo::SIGNON > CSVCMsg_CreateStringTable_t; typedef CNetMessagePB< svc_UpdateStringTable, CSVCMsg_UpdateStringTable, INetChannelInfo::STRINGTABLE > CSVCMsg_UpdateStringTable_t; typedef CNetMessagePB< svc_VoiceInit, CSVCMsg_VoiceInit, INetChannelInfo::SIGNON > CSVCMsg_VoiceInit_t; typedef CNetMessagePB< svc_VoiceData, CSVCMsg_VoiceData, INetChannelInfo::VOICE, false > CSVCMsg_VoiceData_t; typedef CNetMessagePB< svc_FixAngle, CSVCMsg_FixAngle, INetChannelInfo::GENERIC, false > CSVCMsg_FixAngle_t; typedef CNetMessagePB< svc_Prefetch, CSVCMsg_Prefetch, INetChannelInfo::SOUNDS > CSVCMsg_Prefetch_t; typedef CNetMessagePB< svc_CrosshairAngle, CSVCMsg_CrosshairAngle > CSVCMsg_CrosshairAngle_t; typedef CNetMessagePB< svc_BSPDecal, CSVCMsg_BSPDecal > CSVCMsg_BSPDecal_t; typedef CNetMessagePB< svc_SplitScreen, CSVCMsg_SplitScreen > CSVCMsg_SplitScreen_t; typedef CNetMessagePB< svc_GetCvarValue, CSVCMsg_GetCvarValue > CSVCMsg_GetCvarValue_t; typedef CNetMessagePB< svc_Menu, CSVCMsg_Menu, INetChannelInfo::GENERIC, false > CSVCMsg_Menu_t; typedef CNetMessagePB< svc_UserMessage, CSVCMsg_UserMessage, INetChannelInfo::USERMESSAGES, false > CSVCMsg_UserMessage_t; typedef CNetMessagePB< svc_PaintmapData, CSVCMsg_PaintmapData, INetChannelInfo::PAINTMAP > CSVCMsg_PaintmapData_t; typedef CNetMessagePB< svc_GameEvent, CSVCMsg_GameEvent, INetChannelInfo::EVENTS > CSVCMsg_GameEvent_t; typedef CNetMessagePB< svc_GameEventList, CSVCMsg_GameEventList > CSVCMsg_GameEventList_t; typedef CNetMessagePB< svc_TempEntities, CSVCMsg_TempEntities, INetChannelInfo::TEMPENTS, false > CSVCMsg_TempEntities_t; typedef CNetMessagePB< svc_PacketEntities, CSVCMsg_PacketEntities, INetChannelInfo::ENTITIES > CSVCMsg_PacketEntities_t; typedef CNetMessagePB< svc_Sounds, CSVCMsg_Sounds, INetChannelInfo::SOUNDS > CSVCMsg_Sounds_t; typedef CNetMessagePB< svc_EntityMessage, CSVCMsg_EntityMsg, INetChannelInfo::ENTMESSAGES, false > CSVCMsg_EntityMsg_t; typedef CNetMessagePB< svc_CmdKeyValues, CSVCMsg_CmdKeyValues > CSVCMsg_CmdKeyValues_t; typedef CNetMessagePB< svc_EncryptedData, CSVCMsg_EncryptedData, INetChannelInfo::ENCRYPTED > CSVCMsg_EncryptedData_t; typedef CNetMessagePB< svc_HltvReplay, CSVCMsg_HltvReplay, INetChannelInfo::ENTITIES > CSVCMsg_HltvReplay_t; typedef CNetMessagePB< svc_Broadcast_Command, CSVCMsg_Broadcast_Command, INetChannelInfo::STRINGCMD > CSVCMsg_Broadcast_Command_t; /////////////////////////////////////////////////////////////////////////////////////// // Utility classes /////////////////////////////////////////////////////////////////////////////////////// class CmdKeyValuesHelper { public: static void CLCMsg_SetKeyValues( CCLCMsg_CmdKeyValues& msg, const KeyValues *keyValues ); static KeyValues* CLCMsg_GetKeyValues ( const CCLCMsg_CmdKeyValues& msg ); static void SVCMsg_SetKeyValues( CSVCMsg_CmdKeyValues& msg, const KeyValues *keyValues ); static KeyValues *SVCMsg_GetKeyValues ( const CSVCMsg_CmdKeyValues& msg ); }; class INetChannel; class CmdEncryptedDataMessageCodec { public: static bool SVCMsg_EncryptedData_EncryptMessage( CSVCMsg_EncryptedData_t &msgEncryptedResult, INetMessage *pMsgPlaintextInput, char const *key ); static bool SVCMsg_EncryptedData_Process( CSVCMsg_EncryptedData const &msgEncryptedInput, INetChannel *pProcessingChannel, char const *key ); }; ////////////////////////////////////////////////////////////////////////// // Helper class to share network buffers in the entire process ////////////////////////////////////////////////////////////////////////// class net_scratchbuffer_t { public: net_scratchbuffer_t() { m_pBufferNetMaxMessage = sm_NetScratchBuffers.Get(); if ( !m_pBufferNetMaxMessage ) m_pBufferNetMaxMessage = new buffer_t; } ~net_scratchbuffer_t() { sm_NetScratchBuffers.PutObject( m_pBufferNetMaxMessage ); } byte * GetBuffer() const { return m_pBufferNetMaxMessage->buf; } int Size() const { return NET_MAX_MESSAGE; } private: struct buffer_t { byte buf[ NET_MAX_MESSAGE ]; }; buffer_t *m_pBufferNetMaxMessage; // buffer that is allocated and returned to shared pool private: net_scratchbuffer_t( const net_scratchbuffer_t& ); // FORBID net_scratchbuffer_t& operator=( const net_scratchbuffer_t& ); // FORBID static CTSPool< buffer_t > sm_NetScratchBuffers; }; #endif // NETMESSAGES_H