diff --git a/addons/sourcemod/gamedata/ssf.games.txt b/addons/sourcemod/gamedata/ssf.games.txt index 0041998..728bb9b 100644 --- a/addons/sourcemod/gamedata/ssf.games.txt +++ b/addons/sourcemod/gamedata/ssf.games.txt @@ -10,28 +10,22 @@ "Signatures" { - "CBaseClient__SendSnapshot" - { - "library" "engine" - "linux" "@_ZN11CBaseClient12SendSnapshotEP12CClientFrame" - } - "CBaseServer__WriteTempEntities" { "library" "engine" "linux" "@_ZN11CBaseServer17WriteTempEntitiesEP11CBaseClientP14CFrameSnapshotS3_R8bf_writei" } - "CGameClient_FillSoundsMessage" + "CFrameSnapshot__ReleaseReference" { "library" "engine" - "linux" "@_ZN11CGameClient17FillSoundsMessageER10SVC_Sounds" + "linux" "@_ZN14CFrameSnapshot16ReleaseReferenceEv" } - "CGameClient__WriteGameSounds" + "CFrameSnapshot__CreateEmptySnapshot" { "library" "engine" - "linux" "@_ZN11CGameClient15WriteGameSoundsER8bf_write" + "linux" "@_ZN21CFrameSnapshotManager19CreateEmptySnapshotEii" } } } diff --git a/extension.cpp b/extension.cpp index 8ed71b6..4f3c90b 100644 --- a/extension.cpp +++ b/extension.cpp @@ -38,158 +38,16 @@ #include #include #include +#include class CFrameSnapshot; class CClientFrame; -// #define NETMSG_TYPE_BITS 6 // must be 2^NETMSG_TYPE_BITS > SVC_LASTMSG - -// #define NET_MAX_PAYLOAD 4000 - -// #define svc_Sounds 17 // starts playing sound - -// #define DECLARE_BASE_MESSAGE( msgtype ) \ -// public: \ -// bool ReadFromBuffer( bf_read &buffer ); \ -// bool WriteToBuffer( bf_write &buffer ); \ -// const char *ToString() const; \ -// int GetType() const { return msgtype; } \ -// const char *GetName() const { return #msgtype;}\ - -// #define DECLARE_SVC_MESSAGE( name ) \ -// DECLARE_BASE_MESSAGE( svc_##name ); \ -// IServerMessageHandler *m_pMessageHandler;\ -// bool Process() { return m_pMessageHandler->Process##name( this ); }\ - -// class INetChannelInfo -// { -// public: - -// enum { -// GENERIC = 0, // must be first and is default group -// LOCALPLAYER, // bytes for local player entity update -// OTHERPLAYERS, // bytes for other players update -// ENTITIES, // all other entity bytes -// SOUNDS, // game sounds -// EVENTS, // event messages -// USERMESSAGES, // user messages -// ENTMESSAGES, // entity messages -// VOICE, // voice data -// STRINGTABLE, // a stringtable update -// MOVE, // client move cmds -// STRINGCMD, // string command -// SIGNON, // various signondata -// TOTAL, // must be last and is not a real group -// }; -// }; - -// class INetMessage -// { -// public: -// virtual ~INetMessage() {}; - -// // Use these to setup who can hear whose voice. -// // Pass in client indices (which are their ent indices - 1). - -// virtual void SetNetChannel(INetChannel * netchan) = 0; // netchannel this message is from/for -// virtual void SetReliable( bool state ) = 0; // set to true if it's a reliable message - -// virtual bool Process( void ) = 0; // calles the recently set handler to process this message - -// virtual bool ReadFromBuffer( bf_read &buffer ) = 0; // returns true if parsing was OK -// virtual bool WriteToBuffer( bf_write &buffer ) = 0; // returns true if writing was OK - -// virtual bool IsReliable( void ) const = 0; // true, if message needs reliable handling - -// virtual int GetType( void ) const = 0; // returns module specific header tag eg svc_serverinfo -// virtual int GetGroup( void ) const = 0; // returns net message group of this message -// virtual const char *GetName( void ) const = 0; // returns network message name, eg "svc_serverinfo" -// virtual INetChannel *GetNetChannel( void ) const = 0; -// virtual const char *ToString( void ) const = 0; // returns a human readable string about message content -// }; - -// class CNetMessage : public INetMessage -// { -// public: -// CNetMessage() { m_bReliable = true; -// m_NetChannel = NULL; } - -// virtual ~CNetMessage() {}; - -// virtual int GetGroup() const { return INetChannelInfo::GENERIC; } -// INetChannel *GetNetChannel() const { return m_NetChannel; } - -// virtual void SetReliable( bool state) {m_bReliable = state;}; -// virtual bool IsReliable() const { return m_bReliable; }; -// virtual void SetNetChannel(INetChannel * netchan) { m_NetChannel = netchan; } -// virtual bool Process() { Assert( 0 ); return false; }; // no handler set - -// protected: -// bool m_bReliable; // true if message should be send reliable -// INetChannel *m_NetChannel; // netchannel this message is from/for -// }; - -// class SVC_Sounds : public CNetMessage -// { -// DECLARE_SVC_MESSAGE( Sounds ); - -// int GetGroup() const { return INetChannelInfo::SOUNDS; } - -// SVC_Sounds() : CNetMessage() -// { - -// } - -// ~SVC_Sounds() -// { - -// } - -// public: - -// bool m_bReliableSound; -// int m_nNumSounds; -// int m_nLength; -// bf_read m_DataIn; -// bf_write m_DataOut; -// }; - class CBaseClient : public IGameEventListener2, public IClient { }; -// class CGameClient: public CBaseClient -// { -// public: -// CUtlVector m_Sounds; // game sounds -// }; - -// bool SVC_Sounds::WriteToBuffer( bf_write &buffer ) -// { -// m_nLength = m_DataOut.GetNumBitsWritten(); - -// buffer.WriteUBitLong( GetType(), NETMSG_TYPE_BITS ); - -// Assert( m_nNumSounds > 0 ); - -// if ( m_bReliableSound ) -// { -// // as single sound message is 32 bytes long maximum -// buffer.WriteOneBit( 1 ); -// buffer.WriteUBitLong( m_nLength, 8 ); -// } -// else -// { -// // a bunch of unreliable messages -// buffer.WriteOneBit( 0 ); -// buffer.WriteUBitLong( m_nNumSounds, 8 ); -// buffer.WriteUBitLong( m_nLength, 16 ); -// } - -// return buffer.WriteBits( m_DataOut.GetData(), m_nLength ); -// } - SSF g_SSF; /**< Global singleton for extension's main interface */ SMEXT_LINK(&g_SSF); @@ -197,118 +55,46 @@ SMEXT_LINK(&g_SSF); IGameConfig *g_pGameConf = NULL; CGlobalVars *gpGlobals = NULL; -CDetour *g_Detour_CBaseClient__SendSnapshot = NULL; CDetour *g_Detour_CBaseServer__WriteTempEntities = NULL; -// CDetour *g_Detour_CGameClient__FillSoundsMessage = NULL; -// CDetour *g_Detour_CGameClient__WriteGameSounds = NULL; +CDetour *g_Detour_CFrameSnapshot__ReleaseReference = NULL; +CDetour *g_Detour_CFrameSnapshot__CreateEmptySnapshot = NULL; + +// Mutex for m_FrameSnapshots array +CThreadFastMutex m_FrameSnapshotsWriteMutex; ConVar *g_SvSSFLog = CreateConVar("sv_ssf_log", "0", FCVAR_NOTIFY, "Log ssf debug print statements."); ConVar *g_sv_multiplayer_maxtempentities = CreateConVar("sv_multiplayer_maxtempentities", "64"); -// ConVar *g_sv_multiplayer_maxsounds = CreateConVar("sv_multiplayer_sounds", "32"); -// ConVar *g_sv_sound_discardextraunreliable = CreateConVar( "sv_sound_discardextraunreliable", "1" ); -// int Custom_CGameClient__FillSoundsMessage(CGameClient *pGameClient, SVC_Sounds &msg) -// { -// int nMaxSounds = pGameClient->GetServer()->IsMultiplayer() ? g_sv_multiplayer_maxsounds->GetInt() : 255; -// int i, count = pGameClient->m_Sounds.Count(); +DETOUR_DECL_MEMBER2(CFrameSnapshot__CreateEmptySnapshot, CFrameSnapshot *, int, tickcount, int, maxEntities ) +{ + if (g_SvSSFLog->GetBool()) + { + g_pSM->LogMessage(myself, "SSF:CFrameSnapshot__CreateEmptySnapshot locking 1"); + } -// if (g_SvSSFLog->GetBool()) -// { -// g_pSM->LogMessage(myself, "SSF:CGameClient__FillSoundsMessage maxsounds before: %d, count: %d", nMaxSounds, pGameClient->m_Sounds.Count()); -// } - -// // Discard events if we have too many to signal with 8 bits -// if ( count > nMaxSounds ) -// count = nMaxSounds; - -// // Nothing to send -// if ( !count ) -// return 0; - -// SoundInfo_t defaultSound; defaultSound.SetDefault(); -// SoundInfo_t *pDeltaSound = &defaultSound; - -// msg.m_nNumSounds = count; -// msg.m_bReliableSound = false; -// msg.SetReliable( false ); - -// if (g_SvSSFLog->GetBool()) -// { -// g_pSM->LogMessage(myself, "SSF:CGameClient__FillSoundsMessage getnumbitsleft: %d", msg.m_DataOut.GetNumBitsLeft()); -// } - -// Assert( msg.m_DataOut.GetNumBitsLeft() > 0 ); - -// for ( i = 0 ; i < count; i++ ) -// { -// SoundInfo_t &sound = pGameClient->m_Sounds[ i ]; -// sound.WriteDelta( pDeltaSound, msg.m_DataOut ); -// pDeltaSound = &pGameClient->m_Sounds[ i ]; -// } - -// // remove added events from list -// if ( g_sv_sound_discardextraunreliable->GetBool() ) -// { -// if ( pGameClient->m_Sounds.Count() != count ) -// { -// DevMsg( 2, "Warning! Dropped %i unreliable sounds for client %s.\n" , pGameClient->m_Sounds.Count() - count, pGameClient->GetClientName() ); -// } -// pGameClient->m_Sounds.RemoveAll(); -// } -// else -// { -// int remove = pGameClient->m_Sounds.Count() - ( count + nMaxSounds ); -// if ( remove > 0 ) -// { -// DevMsg( 2, "Warning! Dropped %i unreliable sounds for client %s.\n" , remove, pGameClient->GetClientName() ); -// count+= remove; -// } - -// if ( count > 0 ) -// { -// pGameClient->m_Sounds.RemoveMultiple( 0, count ); -// } -// } - -// if (g_SvSSFLog->GetBool()) -// { -// g_pSM->LogMessage(myself, "SSF:CGameClient__FillSoundsMessage maxsounds after: %d count: %d, NumSounds: %d", nMaxSounds, pGameClient->m_Sounds.Count(), msg.m_nNumSounds); -// } - -// Assert( pGameClient->m_Sounds.Count() <= nMaxSounds ); // keep ev_max temp ent for next update - -// return msg.m_nNumSounds; -// } - -// DETOUR_DECL_MEMBER1(CGameClient__FillSoundsMessage, int, SVC_Sounds &, msg) -// { -// CGameClient *pGameClient = (CGameClient *)this; - -// int nResult = Custom_CGameClient__FillSoundsMessage(pGameClient, msg); - -// RETURN_META_VALUE(MRES_SUPERCEDE, nResult); -// } - -// DETOUR_DECL_MEMBER1(CGameClient__WriteGameSounds, void, bf_write, &buf) -// { -// CGameClient *pGameClient = (CGameClient *)this; - -// if ( pGameClient->m_Sounds.Count() <= 0 ) -// return; - -// char data[NET_MAX_PAYLOAD]; -// SVC_Sounds msg; -// msg.m_DataOut.StartWriting( data, sizeof(data) ); + AUTO_LOCK_FM(m_FrameSnapshotsWriteMutex); -// msg.SetReliable( false ); -// int nSoundCount = Custom_CGameClient__FillSoundsMessage( pGameClient, msg ); -// msg.WriteToBuffer( buf ); + CFrameSnapshot* snap = DETOUR_MEMBER_CALL(CFrameSnapshot__CreateEmptySnapshot)(tickcount, maxEntities); -// // if ( pGameClient->IsTracing() ) -// // { -// // pGameClient->TraceNetworkData( buf, "Sounds [count=%d]", nSoundCount ); -// // } -// } + return snap; +} + +// Keep list building thread-safe +// This lock was moved to to fix bug https://bugbait.valvesoftware.com/show_bug.cgi?id=53403 +// Crash in CFrameSnapshotManager::GetPackedEntity where a CBaseClient's m_pBaseline snapshot could be removed the CReferencedSnapshotList destructor +// for another client that is in WriteTempEntities + +DETOUR_DECL_MEMBER0(CFrameSnapshot__ReleaseReference, void) +{ + if (g_SvSSFLog->GetBool()) + { + g_pSM->LogMessage(myself, "SSF:CFrameSnapshot__ReleaseReference locking"); + } + + AUTO_LOCK_FM(m_FrameSnapshotsWriteMutex); + + DETOUR_MEMBER_CALL(CFrameSnapshot__ReleaseReference)(); +} DETOUR_DECL_MEMBER5(CBaseServer__WriteTempEntities, void, CBaseClient *, client, CFrameSnapshot *, pCurrentSnapshot, CFrameSnapshot *, pLastSnapshot, bf_write &, buf, int, ev_max) { @@ -329,6 +115,13 @@ DETOUR_DECL_MEMBER5(CBaseServer__WriteTempEntities, void, CBaseClient *, client, } } + if (g_SvSSFLog->GetBool()) + { + g_pSM->LogMessage(myself, "SSF:CBaseServer__WriteTempEntities locking"); + } + + AUTO_LOCK_FM(m_FrameSnapshotsWriteMutex); + if (g_SvSSFLog->GetBool()) { g_pSM->LogMessage(myself, "SSF:CBaseServer__WriteTempEntities maxentities: %d", ev_max); @@ -337,13 +130,6 @@ DETOUR_DECL_MEMBER5(CBaseServer__WriteTempEntities, void, CBaseClient *, client, DETOUR_MEMBER_CALL(CBaseServer__WriteTempEntities)(client, pCurrentSnapshot, pLastSnapshot, buf, ev_max); } -DETOUR_DECL_MEMBER1(CBaseClient__SendSnapshot, void, CClientFrame *, pFrame) -{ - CBaseClient *pBaseClient = (CBaseClient *)this; - - DETOUR_MEMBER_CALL(CBaseClient__SendSnapshot)(pFrame); -} - bool SSF::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late) { GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION); @@ -369,14 +155,6 @@ bool SSF::SDK_OnLoad(char *error, size_t maxlen, bool late) CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf); - g_Detour_CBaseClient__SendSnapshot = DETOUR_CREATE_MEMBER(CBaseClient__SendSnapshot, "CBaseClient__SendSnapshot"); - if(!g_Detour_CBaseClient__SendSnapshot) - { - snprintf(error, maxlen, "Failed to detour CBaseClient__SendSnapshot.\n"); - return false; - } - g_Detour_CBaseClient__SendSnapshot->EnableDetour(); - g_Detour_CBaseServer__WriteTempEntities = DETOUR_CREATE_MEMBER(CBaseServer__WriteTempEntities, "CBaseServer__WriteTempEntities"); if(!g_Detour_CBaseServer__WriteTempEntities) { @@ -385,21 +163,21 @@ bool SSF::SDK_OnLoad(char *error, size_t maxlen, bool late) } g_Detour_CBaseServer__WriteTempEntities->EnableDetour(); - // g_Detour_CGameClient__FillSoundsMessage = DETOUR_CREATE_MEMBER(CGameClient__FillSoundsMessage, "CGameClient__FillSoundsMessage"); - // if(!g_Detour_CGameClient__FillSoundsMessage) - // { - // snprintf(error, maxlen, "Failed to detour CGameClient__FillSoundsMessage.\n"); - // return false; - // } - // g_Detour_CGameClient__FillSoundsMessage->EnableDetour(); + g_Detour_CFrameSnapshot__ReleaseReference = DETOUR_CREATE_MEMBER(CFrameSnapshot__ReleaseReference, "CFrameSnapshot__ReleaseReference"); + if(!g_Detour_CFrameSnapshot__ReleaseReference) + { + snprintf(error, maxlen, "Failed to detour CFrameSnapshot__ReleaseReference.\n"); + return false; + } + g_Detour_CFrameSnapshot__ReleaseReference->EnableDetour(); - // g_Detour_CGameClient__WriteGameSounds = DETOUR_CREATE_MEMBER(CGameClient__WriteGameSounds, "CGameClient__WriteGameSounds"); - // if(!g_Detour_CGameClient__WriteGameSounds) - // { - // snprintf(error, maxlen, "Failed to detour CGameClient__WriteGameSounds.\n"); - // return false; - // } - // g_Detour_CGameClient__WriteGameSounds->EnableDetour(); + g_Detour_CFrameSnapshot__CreateEmptySnapshot = DETOUR_CREATE_MEMBER(CFrameSnapshot__CreateEmptySnapshot, "CFrameSnapshot__CreateEmptySnapshot"); + if(!g_Detour_CFrameSnapshot__CreateEmptySnapshot) + { + snprintf(error, maxlen, "Failed to detour CFrameSnapshot__CreateEmptySnapshot.\n"); + return false; + } + g_Detour_CFrameSnapshot__CreateEmptySnapshot->EnableDetour(); AutoExecConfig(g_pCVar, true); @@ -408,29 +186,23 @@ bool SSF::SDK_OnLoad(char *error, size_t maxlen, bool late) void SSF::SDK_OnUnload() { - if(g_Detour_CBaseClient__SendSnapshot) - { - g_Detour_CBaseClient__SendSnapshot->Destroy(); - g_Detour_CBaseClient__SendSnapshot = NULL; - } - if(g_Detour_CBaseServer__WriteTempEntities) { g_Detour_CBaseServer__WriteTempEntities->Destroy(); g_Detour_CBaseServer__WriteTempEntities = NULL; } - // if(g_Detour_CGameClient__FillSoundsMessage) - // { - // g_Detour_CGameClient__FillSoundsMessage->Destroy(); - // g_Detour_CGameClient__FillSoundsMessage = NULL; - // } + if (g_Detour_CFrameSnapshot__ReleaseReference) + { + g_Detour_CFrameSnapshot__ReleaseReference->Destroy(); + g_Detour_CFrameSnapshot__ReleaseReference = NULL; + } - // if(g_Detour_CGameClient__WriteGameSounds) - // { - // g_Detour_CGameClient__WriteGameSounds->Destroy(); - // g_Detour_CGameClient__WriteGameSounds = NULL; - // } + if (g_Detour_CFrameSnapshot__CreateEmptySnapshot) + { + g_Detour_CFrameSnapshot__CreateEmptySnapshot->Destroy(); + g_Detour_CFrameSnapshot__CreateEmptySnapshot = NULL; + } gameconfs->CloseGameConfigFile(g_pGameConf); }