From f1ca3e8c7fdf392e5c50cfc1783297200e5e0499 Mon Sep 17 00:00:00 2001 From: BotoX Date: Tue, 3 Sep 2019 11:42:02 +0200 Subject: [PATCH] UNTESTED! Switch to ke::LinkedList for PrintfBuffer. Switch from OnGameFrame to FramAction. Fix compiling on Episode1 by essentially disabling the feature. --- core/PlayerManager.cpp | 172 +++++++++++++---------------------------- core/PlayerManager.h | 32 +------- 2 files changed, 58 insertions(+), 146 deletions(-) diff --git a/core/PlayerManager.cpp b/core/PlayerManager.cpp index 18fa1986..5e76578c 100644 --- a/core/PlayerManager.cpp +++ b/core/PlayerManager.cpp @@ -95,9 +95,10 @@ SH_DECL_EXTERN0_void(ConCommand, Dispatch, SH_NOATTRIB, false); #endif SH_DECL_HOOK2_void(IVEngineServer, ClientPrintf, SH_NOATTRIB, 0, edict_t *, const char *); -static void FrameHook(bool simulating) +static void PrintfBuffer_FrameAction(void *data) { - g_Players.OnGameFrame(); + int client = (int)(intptr_t)data; + g_Players.OnPrintfFrameAction(client); } ConCommand *maxplayersCmd = NULL; @@ -212,12 +213,10 @@ void PlayerManager::OnSourceModAllInitialized() SH_ADD_HOOK(ConCommand, Dispatch, pCmd, SH_STATIC(CmdMaxplayersCallback), true); maxplayersCmd = pCmd; } - g_SourceMod.AddGameFrameHook(&FrameHook); } void PlayerManager::OnSourceModShutdown() { - g_SourceMod.RemoveGameFrameHook(&FrameHook); SH_REMOVE_HOOK(IServerGameClients, ClientConnect, serverClients, SH_MEMBER(this, &PlayerManager::OnClientConnect), false); SH_REMOVE_HOOK(IServerGameClients, ClientConnect, serverClients, SH_MEMBER(this, &PlayerManager::OnClientConnect_Post), true); SH_REMOVE_HOOK(IServerGameClients, ClientPutInServer, serverClients, SH_MEMBER(this, &PlayerManager::OnClientPutInServer), true); @@ -857,143 +856,80 @@ void PlayerManager::OnClientDisconnect_Post(edict_t *pEntity) } } -/* CPrintfBuffer */ -CPrintfBuffer::CPrintfBuffer(size_t MaxLength) -{ - m_pFirstChunk = NULL; - m_pLastChunk = NULL; - m_MaxLength = MaxLength; - m_Length = 0; -} - -bool CPrintfBuffer::Append(const char *pString, size_t Length) -{ - if(Length <= 0 || Length >= 1023) - return false; - - if(m_Length >= m_MaxLength) - return false; - - CChunk *pChunk = new CChunk(); - pChunk->m_pMessage = strdup(pString); - pChunk->m_pMem = pChunk->m_pMessage; - pChunk->m_Length = Length; - pChunk->m_pNext = NULL; - - if(m_pLastChunk) - m_pLastChunk->m_pNext = pChunk; - else - m_pFirstChunk = m_pLastChunk = pChunk; - - m_pLastChunk = pChunk; - m_Length += Length; - - return true; -} - -const CPrintfBuffer::CChunk *CPrintfBuffer::Get() -{ - return m_pFirstChunk; -} - -void CPrintfBuffer::Consumed(size_t Length) -{ - if(Length <= 0 || !m_pFirstChunk) - return; - - if(Length > m_pFirstChunk->m_Length) - Length = m_pFirstChunk->m_Length; - - m_pFirstChunk->m_Length -= Length; - - if(m_pFirstChunk->m_Length == 0) - { - free(m_pFirstChunk->m_pMem); - - if(m_pFirstChunk == m_pLastChunk) - { - delete m_pFirstChunk; - m_pFirstChunk = m_pLastChunk = NULL; - } - else - { - CChunk *pTmp = m_pFirstChunk; - m_pFirstChunk = m_pFirstChunk->m_pNext; - delete pTmp; - } - } - else - { - m_pFirstChunk->m_pMessage += Length; - } - - m_Length -= Length; -} -/* CPrintfBuffer */ - void PlayerManager::OnClientPrintf(edict_t *pEdict, const char *szMsg) { int client = IndexOfEdict(pEdict); INetChannel *pNetChan = static_cast(engine->GetPlayerNetInfo(client)); - if(pNetChan == NULL) - return; - - CPrintfBuffer *pPrintfBuffer = &m_Players[client].m_PrintfBuffer; + if (pNetChan == NULL) + RETURN_META(MRES_IGNORED); size_t nMsgLen = strlen(szMsg); - int nNumBitsWritten = pNetChan->GetNumBitsWritten(false); +#if SOURCE_ENGINE == SE_EPISODEONE + int nNumBitsWritten = 0; +#else + int nNumBitsWritten = pNetChan->GetNumBitsWritten(false); // SVC_Print uses unreliable netchan +#endif - // 2048 = SVC_Print buffersize (-1 for \0) - // 6 = NETMSG_TYPE_BITS, +1 for \0 - if(pPrintfBuffer->m_StopSend || (nNumBitsWritten + 6) / 8 + nMsgLen + 1 >= 2048) + const int NETMSG_TYPE_BITS = 6; // SVC_Print overhead for netmsg type + const int SVC_Print_BufferSize = 2048 - 1; // -1 for terminating \0 + + // enqueue msgs if we'd overflow the SVC_Print buffer + if (m_Players[client].m_PrintfStop || (nNumBitsWritten + NETMSG_TYPE_BITS) / 8 + nMsgLen >= SVC_Print_BufferSize) { // Don't send any more messages for this player until the buffer is empty. - pPrintfBuffer->m_StopSend = true; + // Queue up a gameframe hook to empty the buffer (if we haven't already) + if(!m_Players[client].m_PrintfStop) + g_pSM->AddFrameAction(PrintfBuffer_FrameAction, (void *)(intptr_t)client); + + m_Players[client].m_PrintfStop = true; + + m_Players[client].m_PrintfBuffer.append(szMsg); - pPrintfBuffer->Append(szMsg, nMsgLen); RETURN_META(MRES_SUPERCEDE); - return; } - SH_CALL(engine, &IVEngineServer::ClientPrintf)(pEdict, szMsg); - - RETURN_META(MRES_SUPERCEDE); + RETURN_META(MRES_IGNORED); } -void PlayerManager::OnGameFrame() +void PlayerManager::OnPrintfFrameAction(int client) { - for(int client = 1; client <= m_maxClients; client++) + CPlayer *pPlayer = &m_Players[client]; + if (!pPlayer->IsInGame()) + return; + + INetChannel *pNetChan = static_cast(engine->GetPlayerNetInfo(client)); + if (pNetChan == NULL) + return; + + ke::LinkedList::iterator iter = m_Players[client].m_PrintfBuffer.begin(); + while (iter != m_Players[client].m_PrintfBuffer.end()) { - CPlayer *pPlayer = &m_Players[client]; - if(!pPlayer->IsInGame()) - continue; + int nMsgLen = (*iter).length(); +#if SOURCE_ENGINE == SE_EPISODEONE + int nNumBitsWritten = 0; +#else + int nNumBitsWritten = pNetChan->GetNumBitsWritten(false); // SVC_Print uses unreliable netchan +#endif - INetChannel *pNetChan = static_cast(engine->GetPlayerNetInfo(client)); - if(pNetChan == NULL) - continue; + const int NETMSG_TYPE_BITS = 6; // SVC_Print overhead for netmsg type + const int SVC_Print_BufferSize = 2048 - 1; // -1 for terminating \0 - CPrintfBuffer *pPrintfBuffer = &pPlayer->m_PrintfBuffer; + // stop if we'd overflow the SVC_Print buffer + if((nNumBitsWritten + NETMSG_TYPE_BITS) / 8 + nMsgLen >= SVC_Print_BufferSize) + break; - const CPrintfBuffer::CChunk *pChunk; - while((pChunk = pPrintfBuffer->Get())) - { - int nMsgLen = pChunk->m_Length; - int nNumBitsWritten = pNetChan->GetNumBitsWritten(false); + SH_CALL(engine, &IVEngineServer::ClientPrintf)(pPlayer->m_pEdict, (*iter).chars()); - // 2048 = SVC_Print buffersize (-1 for \0) - // 6 = NETMSG_TYPE_BITS, +1 for \0 - if((nNumBitsWritten + 6) / 8 + nMsgLen + 1 >= 2048) - break; + iter = m_Players[client].m_PrintfBuffer.erase(iter); + } - SH_CALL(engine, &IVEngineServer::ClientPrintf)(pPlayer->m_pEdict, pChunk->m_pMessage); - - pPrintfBuffer->Consumed(nMsgLen); - } - - // Buffer is empty, send new messages without enqueing again. - if(pChunk == NULL) - pPrintfBuffer->m_StopSend = false; + // Buffer is empty, send new messages without enqueuing again. + if (iter == m_Players[client].m_PrintfBuffer.end()) + m_Players[client].m_PrintfStop = false; + else + { // buffer not empty yet, continue processing it on the next gameframe. + g_pSM->AddFrameAction(PrintfBuffer_FrameAction, (void *)(intptr_t)client); } } diff --git a/core/PlayerManager.h b/core/PlayerManager.h index f2627472..376c630b 100644 --- a/core/PlayerManager.h +++ b/core/PlayerManager.h @@ -43,6 +43,7 @@ #include #include #include +#include #include "ConVarManager.h" #include @@ -67,32 +68,6 @@ union serial_t } bits; }; -class CPrintfBuffer -{ -public: - struct CChunk - { - void *m_pMem; - char *m_pMessage; - size_t m_Length; - struct CChunk *m_pNext; - }; - - bool m_StopSend; - -private: - CChunk *m_pFirstChunk; - CChunk *m_pLastChunk; - size_t m_MaxLength; - size_t m_Length; - -public: - CPrintfBuffer(size_t MaxLength=16384); - bool Append(const char *pString, size_t Length); - const CChunk *Get(); - void Consumed(size_t Length); -}; - class CPlayer : public IGamePlayer { friend class PlayerManager; @@ -178,7 +153,8 @@ private: #if SOURCE_ENGINE == SE_CSGO QueryCvarCookie_t m_LanguageCookie = InvalidQueryCvarCookie; #endif - CPrintfBuffer m_PrintfBuffer; + ke::LinkedList m_PrintfBuffer; + bool m_PrintfStop = false; }; class PlayerManager : @@ -218,7 +194,7 @@ public: //void OnClientSettingsChanged_Pre(edict_t *pEntity); void OnServerHibernationUpdate(bool bHibernating); void OnClientPrintf(edict_t *pEdict, const char *szMsg); - void OnGameFrame(); + void OnPrintfFrameAction(int client); public: //IPlayerManager void AddClientListener(IClientListener *listener); void RemoveClientListener(IClientListener *listener);