diff --git a/sp/src/game/shared/mapbase/vscript_singletons.cpp b/sp/src/game/shared/mapbase/vscript_singletons.cpp index bf28a31b..f1b72c1d 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.cpp +++ b/sp/src/game/shared/mapbase/vscript_singletons.cpp @@ -1263,6 +1263,24 @@ CNetMsgScriptHelper *g_ScriptNetMsg = &scriptnetmsg; #define DLL_LOC_STR "[Client]" #endif +#ifdef GAME_DLL +#define SCRIPT_NETMSG_WRITE_FUNC +#else +#define SCRIPT_NETMSG_WRITE_FUNC if ( m_bWriteIgnore ) { return; } +#endif + +#ifdef _DEBUG +#ifdef GAME_DLL +#define DebugNetMsg( l, ... ) do { extern ConVar developer; if (developer.GetInt() >= l) ConColorMsg( Color(100, 225, 255, 255), __VA_ARGS__ ); } while (0); +#else +#define DebugNetMsg( l, ... ) do { extern ConVar developer; if (developer.GetInt() >= l) ConColorMsg( Color(100, 225, 175, 255), __VA_ARGS__ ); } while (0); +#endif +#define DebugWarning(...) Warning( __VA_ARGS__ ) +#else +#define DebugNetMsg(...) (void)(0) +#define DebugWarning(...) (void)(0) +#endif + // Keep track of message names to print on failure #ifdef _DEBUG @@ -1270,7 +1288,7 @@ struct NetMsgHook_t { void Set( const char *s ) { - hash = (int)HashStringCaseless( s ); + hash = CNetMsgScriptHelper::Hash( s ); name = strdup(s); } @@ -1310,8 +1328,23 @@ static const char *HasNetMsgCollision( int hash, const char *ignore ) +inline int CNetMsgScriptHelper::Hash( const char *key ) +{ + int hash = HashStringCaseless( key ); + Assert( hash < (1 << SCRIPT_NETMSG_HEADER_BITS) ); + return hash; +} + void CNetMsgScriptHelper::WriteToBuffer( bf_write *bf ) { +#ifdef CLIENT_DLL + Assert( m_nQueueCount < ( 1 << SCRIPT_NETMSG_QUEUE_BITS ) ); + bf->WriteUBitLong( m_nQueueCount, SCRIPT_NETMSG_QUEUE_BITS ); + + DebugNetMsg( 2, DLL_LOC_STR " CNetMsgScriptHelper::WriteToBuffer() count(%d) size(%d)\n", + m_nQueueCount, m_MsgOut.GetNumBitsWritten() + SCRIPT_NETMSG_QUEUE_BITS ); +#endif + bf->WriteBits( m_MsgOut.GetData(), m_MsgOut.GetNumBitsWritten() ); } @@ -1324,8 +1357,7 @@ void CNetMsgScriptHelper::Reset() #ifdef GAME_DLL m_filter.Reset(); #else - m_MsgIn_()Reset(); - m_bWriteReady = false; + m_iLastBit = 0; #endif } @@ -1344,11 +1376,16 @@ void CNetMsgScriptHelper::LevelShutdownPreVM() { Reset(); if ( m_Hooks ) - { g_pScriptVM->ReleaseScript( m_Hooks ); - } m_Hooks = NULL; +#ifdef CLIENT_DLL + m_bWriteReady = m_bWriteIgnore = false; + m_MsgIn.Reset(); +#else + m_MsgIn = NULL; +#endif + #ifdef _DEBUG g_NetMsgHooks.Purge(); #endif @@ -1385,7 +1422,7 @@ void CNetMsgScriptHelper::ReceiveMessage( bf_read &msg ) m_MsgIn.StartReading( msg.m_pData, msg.m_nDataBytes ); #endif - word hash = m_MsgIn_()ReadWord(); + DebugNetMsg( 2, DLL_LOC_STR " " __FUNCTION__ "()\n" ); // Don't do anything if there's no VM here. This can happen if a message from the server goes to a VM-less client, or vice versa. if ( !g_pScriptVM ) @@ -1394,26 +1431,42 @@ void CNetMsgScriptHelper::ReceiveMessage( bf_read &msg ) return; } - ScriptVariant_t hfn; - if ( g_pScriptVM->GetValue( m_Hooks, hash, &hfn ) ) - { #ifdef GAME_DLL - if ( g_pScriptVM->Call( hfn, NULL, true, NULL, pPlayer->m_hScriptInstance ) == SCRIPT_ERROR ) -#else - if ( g_pScriptVM->ExecuteFunction( hfn, NULL, 0, NULL, NULL, true ) == SCRIPT_ERROR ) + int count = m_MsgIn_()ReadUBitLong( SCRIPT_NETMSG_QUEUE_BITS ); + DebugNetMsg( 2, " msg count %d\n", count ); + while ( count-- ) #endif - { -#ifdef _DEBUG - DevWarning( 1, DLL_LOC_STR " NetMsg: invalid callback '%s'\n", GetNetMsgName( hash ) ); -#else - DevWarning( 1, DLL_LOC_STR " NetMsg: invalid callback [%d]\n", hash ); -#endif - } - g_pScriptVM->ReleaseValue( hfn ); - } - else { - DevWarning( 1, DLL_LOC_STR " NetMsg hook not found [%d]\n", hash ); + int hash = m_MsgIn_()ReadWord(); + +#ifdef _DEBUG + const char *msgName = GetNetMsgName( hash ); + DebugNetMsg( 2, " -- begin msg [%d]%s\n", hash, msgName ); +#endif + + ScriptVariant_t hfn; + if ( g_pScriptVM->GetValue( m_Hooks, hash, &hfn ) ) + { +#ifdef GAME_DLL + if ( g_pScriptVM->Call( hfn, NULL, true, NULL, pPlayer->m_hScriptInstance ) == SCRIPT_ERROR ) +#else + if ( g_pScriptVM->ExecuteFunction( hfn, NULL, 0, NULL, NULL, true ) == SCRIPT_ERROR ) +#endif + { +#ifdef _DEBUG + DevWarning( 1, DLL_LOC_STR " NetMsg: invalid callback '%s'\n", GetNetMsgName( hash ) ); +#else + DevWarning( 1, DLL_LOC_STR " NetMsg: invalid callback [%d]\n", hash ); +#endif + } + g_pScriptVM->ReleaseValue( hfn ); + } + else + { + DevWarning( 1, DLL_LOC_STR " NetMsg hook not found [%d]\n", hash ); + } + + DebugNetMsg( 2, " -- end msg\n" ); } } @@ -1422,18 +1475,50 @@ void CNetMsgScriptHelper::ReceiveMessage( bf_read &msg ) //----------------------------------------------------------------------------- void CNetMsgScriptHelper::Start( const char *msg ) { + if ( !msg || !msg[0] ) + { + g_pScriptVM->RaiseException( DLL_LOC_STR "NetMsg: invalid message name" ); + return; + } + + DebugNetMsg( 1, DLL_LOC_STR " " __FUNCTION__ "() [%d]%s\n", Hash( msg ), msg ); + +#ifdef CLIENT_DLL + // Client can write multiple messages in a frame before the usercmd is sent, + // this queue system ensures client messages are written to the cmd all at once. + // NOTE: All messages share the same buffer. + if ( !m_bWriteReady ) + { + Reset(); + m_nQueueCount = 0; + m_bWriteIgnore = false; + } + else if ( m_nQueueCount == ((1< client // // Sends an exclusive usermessage. //----------------------------------------------------------------------------- -#ifdef GAME_DLL void CNetMsgScriptHelper::Send( HSCRIPT player, bool bReliable ) { + DebugNetMsg( 1, DLL_LOC_STR " " __FUNCTION__ "() size(%d)\n", GetNumBitsWritten() ); + CBaseEntity *pPlayer = ToEnt(player); if ( pPlayer ) { @@ -1457,6 +1542,8 @@ void CNetMsgScriptHelper::Send( HSCRIPT player, bool bReliable ) //----------------------------------------------------------------------------- void CNetMsgScriptHelper::Send() { + DebugNetMsg( 1, DLL_LOC_STR " " __FUNCTION__ "() size(%d)\n", m_bWriteIgnore ? 0 : GetNumBitsWritten() ); + m_bWriteReady = true; } #endif @@ -1466,17 +1553,29 @@ void CNetMsgScriptHelper::Send() //----------------------------------------------------------------------------- void CNetMsgScriptHelper::Receive( const char *msg, HSCRIPT func ) { + if ( !msg || !msg[0] ) + { + g_pScriptVM->RaiseException( DLL_LOC_STR "NetMsg: invalid message name" ); + return; + } + +#ifdef _DEBUG + int hash = Hash( msg ); + + const char *psz = HasNetMsgCollision( hash, msg ); + AssertMsg3( !psz, DLL_LOC_STR " NetMsg hash collision! [%d] '%s', '%s'\n", hash, msg, psz ); + + NetMsgHook_t &hook = g_NetMsgHooks[ g_NetMsgHooks.AddToTail() ]; + hook.Set( msg ); +#endif + if ( func ) { -#ifdef _DEBUG - NetMsgHook_t &hook = g_NetMsgHooks[ g_NetMsgHooks.AddToTail() ]; - hook.Set( msg ); -#endif - g_pScriptVM->SetValue( m_Hooks, int( HashStringCaseless(msg) ), func ); + g_pScriptVM->SetValue( m_Hooks, Hash( msg ), func ); } else { - g_pScriptVM->ClearValue( m_Hooks, int( HashStringCaseless(msg) ) ); + g_pScriptVM->ClearValue( m_Hooks, Hash( msg ) ); } } @@ -1572,86 +1671,107 @@ void CNetMsgScriptHelper::AddAllPlayers() void CNetMsgScriptHelper::WriteInt( int iValue, int bits ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteSBitLong( iValue, bits ); } void CNetMsgScriptHelper::WriteUInt( int iValue, int bits ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteUBitLong( iValue, bits ); } void CNetMsgScriptHelper::WriteByte( int iValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteByte( iValue ); } void CNetMsgScriptHelper::WriteChar( int iValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteChar( iValue ); } void CNetMsgScriptHelper::WriteShort( int iValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteShort( iValue ); } void CNetMsgScriptHelper::WriteWord( int iValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteWord( iValue ); } void CNetMsgScriptHelper::WriteLong( int iValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteLong( iValue ); } void CNetMsgScriptHelper::WriteFloat( float flValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteFloat( flValue ); } void CNetMsgScriptHelper::WriteNormal( float flValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteBitNormal( flValue ); } void CNetMsgScriptHelper::WriteAngle( float flValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteBitAngle( flValue, 8 ); } void CNetMsgScriptHelper::WriteCoord( float flValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteBitCoord( flValue ); } void CNetMsgScriptHelper::WriteVec3Coord( const Vector& rgflValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteBitVec3Coord( rgflValue ); } void CNetMsgScriptHelper::WriteVec3Normal( const Vector& rgflValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteBitVec3Normal( rgflValue ); } void CNetMsgScriptHelper::WriteAngles( const QAngle& rgflValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteBitAngles( rgflValue ); } void CNetMsgScriptHelper::WriteString( const char *sz ) { + SCRIPT_NETMSG_WRITE_FUNC + + // Larger strings can be written but cannot be read + Assert( V_strlen(sz) < SCRIPT_NETMSG_STRING_SIZE ); + m_MsgOut.WriteString( sz ); } void CNetMsgScriptHelper::WriteBool( bool bValue ) { + SCRIPT_NETMSG_WRITE_FUNC m_MsgOut.WriteOneBit( bValue ? 1 : 0 ); } void CNetMsgScriptHelper::WriteEntity( HSCRIPT hEnt ) { + SCRIPT_NETMSG_WRITE_FUNC CBaseEntity *p = ToEnt(hEnt); int i = p ? p->entindex() : -1; m_MsgOut.WriteSBitLong( i, MAX_EDICT_BITS ); @@ -1659,6 +1779,7 @@ void CNetMsgScriptHelper::WriteEntity( HSCRIPT hEnt ) void CNetMsgScriptHelper::WriteEHandle( HSCRIPT hEnt ) { + SCRIPT_NETMSG_WRITE_FUNC CBaseEntity *pEnt = ToEnt( hEnt ); long iEncodedEHandle; if ( pEnt ) @@ -1752,7 +1873,7 @@ const QAngle& CNetMsgScriptHelper::ReadAngles() const char* CNetMsgScriptHelper::ReadString() { - static char buf[512]; + static char buf[ SCRIPT_NETMSG_STRING_SIZE ]; m_MsgIn_()ReadString( buf, sizeof(buf) ); return buf; } @@ -1790,12 +1911,15 @@ HSCRIPT CNetMsgScriptHelper::ReadEHandle() return ToHScript( EHANDLE( iEntry, iSerialNum ) ); } -int CNetMsgScriptHelper::GetNumBitsWritten() +inline int CNetMsgScriptHelper::GetNumBitsWritten() { - return m_MsgOut.GetNumBitsWritten(); +#ifdef GAME_DLL + return m_MsgOut.GetNumBitsWritten() - SCRIPT_NETMSG_HEADER_BITS; +#else + return m_MsgOut.m_iCurBit - m_iLastBit - SCRIPT_NETMSG_HEADER_BITS; +#endif } -#undef m_MsgIn_ BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "Network messages" ) @@ -1818,7 +1942,7 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "N #ifdef GAME_DLL DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the server to the client (max 252 bytes)" ) #else - DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the client to the server (max 2045 bytes)" ) + DEFINE_SCRIPTFUNC( Send, "Send a custom network message from the client to the server (max 2044 bytes)" ) #endif DEFINE_SCRIPTFUNC( WriteInt, "variable bit signed int" ) @@ -1835,7 +1959,7 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "N DEFINE_SCRIPTFUNC( WriteVec3Coord, "" ) DEFINE_SCRIPTFUNC( WriteVec3Normal, "27 bit" ) DEFINE_SCRIPTFUNC( WriteAngles, "" ) - DEFINE_SCRIPTFUNC( WriteString, "" ) + DEFINE_SCRIPTFUNC( WriteString, "max 512 bytes at once" ) DEFINE_SCRIPTFUNC( WriteBool, "1 bit" ) DEFINE_SCRIPTFUNC( WriteEntity, "11 bit (entindex)" ) DEFINE_SCRIPTFUNC( WriteEHandle, "32 bit long" ) @@ -1854,7 +1978,7 @@ BEGIN_SCRIPTDESC_ROOT_NAMED( CNetMsgScriptHelper, "CNetMsg", SCRIPT_SINGLETON "N DEFINE_SCRIPTFUNC( ReadVec3Coord, "" ) DEFINE_SCRIPTFUNC( ReadVec3Normal, "" ) DEFINE_SCRIPTFUNC( ReadAngles, "" ) - DEFINE_SCRIPTFUNC( ReadString, "max 512 bytes at once" ) + DEFINE_SCRIPTFUNC( ReadString, "" ) DEFINE_SCRIPTFUNC( ReadBool, "" ) DEFINE_SCRIPTFUNC( ReadEntity, "" ) DEFINE_SCRIPTFUNC( ReadEHandle, "" ) diff --git a/sp/src/game/shared/mapbase/vscript_singletons.h b/sp/src/game/shared/mapbase/vscript_singletons.h index a18d4a38..825320c0 100644 --- a/sp/src/game/shared/mapbase/vscript_singletons.h +++ b/sp/src/game/shared/mapbase/vscript_singletons.h @@ -17,12 +17,17 @@ void RegisterScriptSingletons(); #ifdef CLIENT_DLL // usercmd -#define SCRIPT_NETMSG_DATA_SIZE ( ( 1 << 11 ) - 1 ) +#define SCRIPT_NETMSG_DATA_SIZE ( ( 1 << 11 ) - 1 ) #else // usermsg -#define SCRIPT_NETMSG_DATA_SIZE MAX_USER_MSG_DATA +#define SCRIPT_NETMSG_DATA_SIZE MAX_USER_MSG_DATA #endif +#define SCRIPT_NETMSG_QUEUE_BITS 3 // determines the number of custom messages client can write to a usercmd +#define SCRIPT_NETMSG_HEADER_BITS (sizeof(word) << 3) +#define SCRIPT_NETMSG_STRING_SIZE 512 + + #ifdef CLIENT_DLL class CNetMsgScriptHelper : public CAutoGameSystem #else @@ -40,18 +45,28 @@ private: CRecipientFilter m_filter; #else bf_read m_MsgIn; + unsigned int m_nQueueCount; + bool m_bWriteIgnore; #endif HSCRIPT m_Hooks; bf_write m_MsgOut; byte m_MsgData[ PAD_NUMBER( SCRIPT_NETMSG_DATA_SIZE, 4 ) ]; -public: #ifdef CLIENT_DLL - CNetMsgScriptHelper() : m_Hooks(NULL), m_bWriteReady(false) {} -#else - CNetMsgScriptHelper() : m_Hooks(NULL) {} + int m_iLastBit; #endif +public: + CNetMsgScriptHelper() : m_Hooks(NULL) + +#ifdef CLIENT_DLL + , m_bWriteReady(0), m_bWriteIgnore(0), m_nQueueCount(0), m_iLastBit(0) +#else + , m_MsgIn(0) +#endif + + {} + public: #ifdef CLIENT_DLL bool Init(); // IGameSystem @@ -135,6 +150,8 @@ public: //int GetNumBitsLeft(); // unreliable on server because of usercmds. so just do away with it int GetNumBitsWritten(); +public: + static inline int Hash( const char *key ); }; extern CNetMsgScriptHelper *g_ScriptNetMsg;