Fix writing multiple messages on client script NetMsg

This commit is contained in:
samisalreadytaken 2022-02-18 17:00:00 +03:00
parent 4b8f386c94
commit 6d3c53fe0f
2 changed files with 185 additions and 44 deletions

View File

@ -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,6 +1431,19 @@ void CNetMsgScriptHelper::ReceiveMessage( bf_read &msg )
return;
}
#ifdef GAME_DLL
int count = m_MsgIn_()ReadUBitLong( SCRIPT_NETMSG_QUEUE_BITS );
DebugNetMsg( 2, " msg count %d\n", count );
while ( count-- )
#endif
{
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 ) )
{
@ -1415,6 +1465,9 @@ void CNetMsgScriptHelper::ReceiveMessage( bf_read &msg )
{
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 )
{
Reset();
m_MsgOut.WriteWord( HashStringCaseless(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<<SCRIPT_NETMSG_QUEUE_BITS)-1) )
{
Warning( DLL_LOC_STR " NetMsg queue is full, cannot write '%s'!\n", msg );
m_bWriteIgnore = true;
return;
}
++m_nQueueCount;
#else
Reset();
#endif
m_MsgOut.WriteWord( Hash( msg ) );
}
#ifdef GAME_DLL
//-----------------------------------------------------------------------------
// server -> 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 ( 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
g_pScriptVM->SetValue( m_Hooks, int( HashStringCaseless(msg) ), func );
if ( 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, "" )

View File

@ -23,6 +23,11 @@ void RegisterScriptSingletons();
#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;