Avoid losing console messages.
Buffers up to 16k bytes of SVC_Print if buffer would overflow, then sends chunks every frame. Sends up to 2048 bytes per frame and does not split messages.
This commit is contained in:
		
							parent
							
								
									690b3c5a28
								
							
						
					
					
						commit
						82628cfc5a
					
				| @ -30,6 +30,7 @@ | ||||
|  */ | ||||
| 
 | ||||
| #include "PlayerManager.h" | ||||
| #include "sourcemod.h" | ||||
| #include "IAdminSystem.h" | ||||
| #include "ConCmdManager.h" | ||||
| #include "MenuStyle_Valve.h" | ||||
| @ -92,6 +93,12 @@ SH_DECL_EXTERN1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &) | ||||
| #else | ||||
| 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 PrintfBuffer_FrameAction(void *data) | ||||
| { | ||||
| 	g_Players.OnPrintfFrameAction(reinterpret_cast<unsigned int>(data)); | ||||
| } | ||||
| 
 | ||||
| ConCommand *maxplayersCmd = NULL; | ||||
| 
 | ||||
| @ -172,6 +179,7 @@ void PlayerManager::OnSourceModAllInitialized() | ||||
| #elif SOURCE_ENGINE > SE_EYE // 2013/orangebox, but not original orangebox.
 | ||||
| 	SH_ADD_HOOK(IServerGameDLL, SetServerHibernation, gamedll, SH_MEMBER(this, &PlayerManager::OnServerHibernationUpdate), true); | ||||
| #endif | ||||
| 	SH_ADD_HOOK(IVEngineServer, ClientPrintf, engine, SH_MEMBER(this, &PlayerManager::OnClientPrintf), false); | ||||
| 
 | ||||
| 	sharesys->AddInterface(NULL, this); | ||||
| 
 | ||||
| @ -225,6 +233,7 @@ void PlayerManager::OnSourceModShutdown() | ||||
| #elif SOURCE_ENGINE > SE_EYE // 2013/orangebox, but not original orangebox.
 | ||||
| 	SH_REMOVE_HOOK(IServerGameDLL, SetServerHibernation, gamedll, SH_MEMBER(this, &PlayerManager::OnServerHibernationUpdate), true); | ||||
| #endif | ||||
| 	SH_REMOVE_HOOK(IVEngineServer, ClientPrintf, engine, SH_MEMBER(this, &PlayerManager::OnClientPrintf), false); | ||||
| 
 | ||||
| 	/* Release forwards */ | ||||
| 	forwardsys->ReleaseForward(m_clconnect); | ||||
| @ -846,6 +855,88 @@ void PlayerManager::OnClientDisconnect_Post(edict_t *pEntity) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void PlayerManager::OnClientPrintf(edict_t *pEdict, const char *szMsg) | ||||
| { | ||||
| 	int client = IndexOfEdict(pEdict); | ||||
| 
 | ||||
| 	CPlayer &player = m_Players[client]; | ||||
| 	if (!player.IsConnected()) | ||||
| 		RETURN_META(MRES_IGNORED); | ||||
| 
 | ||||
| 	INetChannel *pNetChan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(client)); | ||||
| 	if (pNetChan == NULL) | ||||
| 		RETURN_META(MRES_IGNORED); | ||||
| 
 | ||||
| 	size_t nMsgLen = strlen(szMsg); | ||||
| #if SOURCE_ENGINE == SE_EPISODEONE | ||||
| 	static const int nNumBitsWritten = 0; | ||||
| #else | ||||
| 	int nNumBitsWritten = pNetChan->GetNumBitsWritten(false); // SVC_Print uses unreliable netchan
 | ||||
| #endif | ||||
| 
 | ||||
| 	// if the msg is bigger than allowed then just let it fail
 | ||||
| 	if (nMsgLen + 1 >= SVC_Print_BufferSize) // +1 for NETMSG_TYPE_BITS
 | ||||
| 		RETURN_META(MRES_IGNORED); | ||||
| 
 | ||||
| 	// enqueue msgs if we'd overflow the SVC_Print buffer (+7 as ceil)
 | ||||
| 	if (!player.m_PrintfBuffer.empty() || (nNumBitsWritten + NETMSG_TYPE_BITS + 7) / 8 + nMsgLen >= SVC_Print_BufferSize) | ||||
| 	{ | ||||
| 		// Don't send any more messages for this player until the buffer is empty.
 | ||||
| 		// Queue up a gameframe hook to empty the buffer (if we haven't already)
 | ||||
| 		if (player.m_PrintfBuffer.empty()) | ||||
| 			g_SourceMod.AddFrameAction(PrintfBuffer_FrameAction, (void *)(uintptr_t)player.GetSerial()); | ||||
| 
 | ||||
| 		player.m_PrintfBuffer.append(szMsg); | ||||
| 
 | ||||
| 		RETURN_META(MRES_SUPERCEDE); | ||||
| 	} | ||||
| 
 | ||||
| 	RETURN_META(MRES_IGNORED); | ||||
| } | ||||
| 
 | ||||
| void PlayerManager::OnPrintfFrameAction(unsigned int serial) | ||||
| { | ||||
| 	int client = GetClientFromSerial(serial); | ||||
| 	CPlayer &player = m_Players[client]; | ||||
| 	if (!player.IsConnected()) | ||||
| 	{ | ||||
| 		player.ClearNetchannelQueue(); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	INetChannel *pNetChan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(client)); | ||||
| 	if (pNetChan == NULL) | ||||
| 	{ | ||||
| 		player.ClearNetchannelQueue(); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	while (!player.m_PrintfBuffer.empty()) | ||||
| 	{ | ||||
| #if SOURCE_ENGINE == SE_EPISODEONE | ||||
| 		static const int nNumBitsWritten = 0; | ||||
| #else | ||||
| 		int nNumBitsWritten = pNetChan->GetNumBitsWritten(false); // SVC_Print uses unreliable netchan
 | ||||
| #endif | ||||
| 
 | ||||
| 		ke::AString &string = player.m_PrintfBuffer.front(); | ||||
| 
 | ||||
| 		// stop if we'd overflow the SVC_Print buffer  (+7 as ceil)
 | ||||
| 		if ((nNumBitsWritten + NETMSG_TYPE_BITS + 7) / 8 + string.length() >= SVC_Print_BufferSize) | ||||
| 			break; | ||||
| 
 | ||||
| 		SH_CALL(engine, &IVEngineServer::ClientPrintf)(player.m_pEdict, string.chars()); | ||||
| 
 | ||||
| 		player.m_PrintfBuffer.popFront(); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!player.m_PrintfBuffer.empty()) | ||||
| 	{ | ||||
| 		// continue processing it on the next gameframe as buffer is not empty
 | ||||
| 		g_SourceMod.AddFrameAction(PrintfBuffer_FrameAction, (void *)(uintptr_t)player.GetSerial()); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ClientConsolePrint(edict_t *e, const char *fmt, ...) | ||||
| { | ||||
| 	char buffer[512]; | ||||
| @ -2145,6 +2236,13 @@ void CPlayer::Disconnect() | ||||
| #if SOURCE_ENGINE == SE_CSGO | ||||
| 	m_LanguageCookie = InvalidQueryCvarCookie; | ||||
| #endif | ||||
| 	ClearNetchannelQueue(); | ||||
| } | ||||
| 
 | ||||
| void CPlayer::ClearNetchannelQueue(void) | ||||
| { | ||||
| 	while (!m_PrintfBuffer.empty()) | ||||
| 		m_PrintfBuffer.popFront(); | ||||
| } | ||||
| 
 | ||||
| void CPlayer::SetName(const char *name) | ||||
|  | ||||
| @ -43,6 +43,7 @@ | ||||
| #include <sh_list.h> | ||||
| #include <sh_vector.h> | ||||
| #include <am-string.h> | ||||
| #include <am-deque.h> | ||||
| #include "ConVarManager.h" | ||||
| 
 | ||||
| #include <steam/steamclientpublic.h> | ||||
| @ -123,6 +124,7 @@ private: | ||||
| 	bool IsAuthStringValidated(); | ||||
| 	bool SetEngineString(); | ||||
| 	bool SetCSteamID(); | ||||
| 	void ClearNetchannelQueue(void); | ||||
| private: | ||||
| 	bool m_IsConnected = false; | ||||
| 	bool m_IsInGame = false; | ||||
| @ -152,6 +154,7 @@ private: | ||||
| #if SOURCE_ENGINE == SE_CSGO | ||||
| 	QueryCvarCookie_t m_LanguageCookie = InvalidQueryCvarCookie; | ||||
| #endif | ||||
| 	ke::Deque<ke::AString> m_PrintfBuffer; | ||||
| }; | ||||
| 
 | ||||
| class PlayerManager :  | ||||
| @ -190,6 +193,8 @@ public: | ||||
| 	void OnClientSettingsChanged(edict_t *pEntity); | ||||
| 	//void OnClientSettingsChanged_Pre(edict_t *pEntity);
 | ||||
| 	void OnServerHibernationUpdate(bool bHibernating); | ||||
| 	void OnClientPrintf(edict_t *pEdict, const char *szMsg); | ||||
| 	void OnPrintfFrameAction(unsigned int serial); | ||||
| public: //IPlayerManager
 | ||||
| 	void AddClientListener(IClientListener *listener); | ||||
| 	void RemoveClientListener(IClientListener *listener); | ||||
| @ -267,6 +272,9 @@ private: | ||||
| 	int m_SourceTVUserId; | ||||
| 	int m_ReplayUserId; | ||||
| 	bool m_bInCCKVHook; | ||||
| private: | ||||
| 	static const int NETMSG_TYPE_BITS = 5; // SVC_Print overhead for netmsg type
 | ||||
| 	static const int SVC_Print_BufferSize = 2048 - 1; // -1 for terminating \0
 | ||||
| }; | ||||
| 
 | ||||
| #if SOURCE_ENGINE >= SE_ORANGEBOX | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user