Engine: strong optimizations for RCON system

- All serializations now expect exact buffer lengths (which we knew for every call, but didn't make use of).
- The prefix is now also constructed directly into the send buffer
- The send buffer copies have been completely removed, the data that has been rendered into the only existing buffer will be used directly.
This commit is contained in:
Kawe Mazidjatari 2025-02-09 01:23:05 +01:00
parent e223cec9c2
commit b1d81e2dc5
9 changed files with 77 additions and 59 deletions

View File

@ -50,7 +50,6 @@ public:
m_bValidated = false;
m_bAuthorized = false;
m_bInputOnly = true;
m_RecvBuffer.resize(sizeof(u_long)); // Reserve enough for length-prefix.
}
};

View File

@ -340,7 +340,7 @@ void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context,
#ifndef CLIENT_DLL
if (!LoggedFromClient(context) && RCONServer()->ShouldSend(netcon::response_e::SERVERDATA_RESPONSE_CONSOLE_LOG))
{
RCONServer()->SendEncoded(formatted.c_str(), pszUpTime, netcon::response_e::SERVERDATA_RESPONSE_CONSOLE_LOG,
RCONServer()->SendEncoded(formatted.c_str(), formatted.length(), pszUpTime, contextTextStartIndex, netcon::response_e::SERVERDATA_RESPONSE_CONSOLE_LOG,
int(context), int(logType));
}
#endif // !CLIENT_DLL

View File

@ -162,11 +162,11 @@ void CRConClient::RequestConsoleLog(const bool bWantLog)
// sending logs will cause the print func to get called recursively forever.
Assert(!(bWantLog && IsRemoteLocal()));
const char* szEnable = bWantLog ? "1" : "0";
const char* const szEnable = bWantLog ? "1" : "0";
const SocketHandle_t hSocket = GetSocket();
vector<char> vecMsg;
bool ret = Serialize(vecMsg, "", szEnable, netcon::request_e::SERVERDATA_REQUEST_SEND_CONSOLE_LOG);
const bool ret = Serialize(vecMsg, "", 0, szEnable, 1, netcon::request_e::SERVERDATA_REQUEST_SEND_CONSOLE_LOG);
if (ret && !Send(hSocket, vecMsg.data(), int(vecMsg.size())))
{
@ -177,14 +177,16 @@ void CRConClient::RequestConsoleLog(const bool bWantLog)
//-----------------------------------------------------------------------------
// Purpose: serializes input
// Input : *svReqBuf -
// nReqMsgLen -
// *svReqVal -
// nReqValLen -
// request_t -
// Output : serialized results as string
//-----------------------------------------------------------------------------
bool CRConClient::Serialize(vector<char>& vecBuf, const char* szReqBuf,
const char* szReqVal, const netcon::request_e requestType) const
bool CRConClient::Serialize(vector<char>& vecBuf, const char* szReqBuf, const size_t nReqMsgLen,
const char* szReqVal, const size_t nReqValLen, const netcon::request_e requestType) const
{
return NetconClient_Serialize(this, vecBuf, szReqBuf, szReqVal, requestType,
return NetconClient_Serialize(this, vecBuf, szReqBuf, nReqMsgLen, szReqVal, nReqValLen, requestType,
rcon_encryptframes.GetBool(), rcon_debug.GetBool());
}
@ -329,7 +331,10 @@ static void RCON_CmdQuery_f(const CCommand& args)
{
if (argCount > 2)
{
bSuccess = RCONClient()->Serialize(vecMsg, args.Arg(2), "", netcon::request_e::SERVERDATA_REQUEST_AUTH);
const char* const pass = args.Arg(2);
const size_t passLen = strlen(pass);
bSuccess = RCONClient()->Serialize(vecMsg, pass, passLen, "", 0, netcon::request_e::SERVERDATA_REQUEST_AUTH);
}
else // Need at least 3 arguments for a password in PASS command (rcon PASS <password>)
{
@ -350,7 +355,13 @@ static void RCON_CmdQuery_f(const CCommand& args)
return;
}
bSuccess = RCONClient()->Serialize(vecMsg, args.Arg(1), args.ArgS(), netcon::request_e::SERVERDATA_REQUEST_EXECCOMMAND);
const char* const request = args.Arg(1);
const size_t requestLen = strlen(request);
const char* const value = args.ArgS();
const size_t valueLen = strlen(value);
bSuccess = RCONClient()->Serialize(vecMsg, request, requestLen, value, valueLen, netcon::request_e::SERVERDATA_REQUEST_EXECCOMMAND);
if (bSuccess)
{
RCONClient()->Send(hSocket, vecMsg.data(), int(vecMsg.size()));

View File

@ -17,8 +17,8 @@ public:
virtual void Disconnect(const char* szReason = nullptr) override;
virtual bool ProcessMessage(const char* pMsgBuf, const int nMsgLen) override;
bool Serialize(vector<char>& vecBuf, const char* szReqBuf,
const char* szReqVal, const netcon::request_e requestType) const;
bool Serialize(vector<char>& vecBuf, const char* szReqBuf, const size_t nReqMsgLen,
const char* szReqVal, const size_t nReqValLen, const netcon::request_e requestType) const;
void RequestConsoleLog(const bool bWantLog);
bool ShouldReceive(void);

View File

@ -252,7 +252,7 @@ void CRConServer::RunFrame(void)
if (CheckForBan(data))
{
SendEncoded(data.m_hSocket, s_BannedMessage, "",
SendEncoded(data.m_hSocket, s_BannedMessage, sizeof(s_BannedMessage)-1, "", 0,
netcon::response_e::SERVERDATA_RESPONSE_AUTH, int(eDLL_T::NETCON));
Disconnect("banned");
@ -272,23 +272,16 @@ void CRConServer::RunFrame(void)
//-----------------------------------------------------------------------------
bool CRConServer::SendToAll(const char* pMsgBuf, const int nMsgLen) const
{
ostringstream sendbuf;
const u_long nLen = htonl(u_long(nMsgLen));
const int nCount = m_Socket.GetAcceptedSocketCount();
bool bSuccess = true;
sendbuf.write(reinterpret_cast<const char*>(&nLen), sizeof(u_long));
sendbuf.write(pMsgBuf, nMsgLen);
const int nCount = m_Socket.GetAcceptedSocketCount();
for (int i = nCount - 1; i >= 0; i--)
{
const CConnectedNetConsoleData& data = m_Socket.GetAcceptedSocketData(i);
if (data.m_bAuthorized && !data.m_bInputOnly)
{
int ret = ::send(data.m_hSocket, sendbuf.str().data(),
int(sendbuf.str().size()), MSG_NOSIGNAL);
const int ret = ::send(data.m_hSocket, pMsgBuf, nMsgLen, MSG_NOSIGNAL);
if (ret == SOCKET_ERROR)
{
@ -306,17 +299,19 @@ bool CRConServer::SendToAll(const char* pMsgBuf, const int nMsgLen) const
//-----------------------------------------------------------------------------
// Purpose: encode and send message to all connected sockets
// Input : *pResponseMsg -
// nResponseMsgLen -
// *pResponseVal -
// nResponseValLen -
// responseType -
// nMessageId -
// nMessageType -
// Output: true on success, false otherwise
//-----------------------------------------------------------------------------
bool CRConServer::SendEncoded(const char* pResponseMsg, const char* pResponseVal,
bool CRConServer::SendEncoded(const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen,
const netcon::response_e responseType, const int nMessageId, const int nMessageType) const
{
vector<char> vecMsg;
if (!Serialize(vecMsg, pResponseMsg, pResponseVal,
if (!Serialize(vecMsg, pResponseMsg, nResponseMsgLen, pResponseVal, nResponseValLen,
responseType, nMessageId, nMessageType))
{
return false;
@ -334,17 +329,20 @@ bool CRConServer::SendEncoded(const char* pResponseMsg, const char* pResponseVal
// Purpose: encode and send message to specific socket
// Input : hSocket -
// *pResponseMsg -
// nResponseMsgLen -
// *pResponseVal -
// nResponseValLen -
// responseType -
// nMessageId -
// nMessageType -
// Output: true on success, false otherwise
//-----------------------------------------------------------------------------
bool CRConServer::SendEncoded(const SocketHandle_t hSocket, const char* pResponseMsg, const char* pResponseVal,
bool CRConServer::SendEncoded(const SocketHandle_t hSocket,
const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen,
const netcon::response_e responseType, const int nMessageId, const int nMessageType) const
{
vector<char> vecMsg;
if (!Serialize(vecMsg, pResponseMsg, pResponseVal,
if (!Serialize(vecMsg, pResponseMsg, nResponseMsgLen, pResponseVal, nResponseValLen,
responseType, nMessageId, nMessageType))
{
return false;
@ -362,16 +360,19 @@ bool CRConServer::SendEncoded(const SocketHandle_t hSocket, const char* pRespons
// Purpose: serializes input
// Input : &vecBuf -
// *responseMsg -
// nResponseMsgLen -
// *responseVal -
// nResponseValLen -
// responseType -
// nMessageId -
// nMessageType -
// Output : serialized results as string
//-----------------------------------------------------------------------------
bool CRConServer::Serialize(vector<char>& vecBuf, const char* pResponseMsg, const char* pResponseVal,
bool CRConServer::Serialize(vector<char>& vecBuf,
const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen,
const netcon::response_e responseType, const int nMessageId, const int nMessageType) const
{
return NetconServer_Serialize(this, vecBuf, pResponseMsg, pResponseVal, responseType, nMessageId, nMessageType,
return NetconServer_Serialize(this, vecBuf, pResponseMsg, nResponseMsgLen, pResponseVal, nResponseValLen, responseType, nMessageId, nMessageType,
rcon_encryptframes.GetBool(), rcon_debug.GetBool());
}
@ -399,7 +400,7 @@ void CRConServer::Authenticate(const netcon::request& request, CConnectedNetCons
const char* pSendLogs = (!sv_rcon_sendlogs.GetBool() || data.m_bInputOnly) ? "0" : "1";
SendEncoded(data.m_hSocket, s_AuthMessage, pSendLogs,
SendEncoded(data.m_hSocket, s_AuthMessage, sizeof(s_AuthMessage)-1, pSendLogs, 1,
netcon::response_e::SERVERDATA_RESPONSE_AUTH, static_cast<int>(eDLL_T::NETCON));
}
else // Bad password.
@ -410,7 +411,7 @@ void CRConServer::Authenticate(const netcon::request& request, CConnectedNetCons
Msg(eDLL_T::SERVER, "Bad RCON password attempt from '%s'\n", netAdr.ToString());
}
SendEncoded(data.m_hSocket, s_WrongPwMessage, "",
SendEncoded(data.m_hSocket, s_WrongPwMessage, sizeof(s_WrongPwMessage)-1, "", 0,
netcon::response_e::SERVERDATA_RESPONSE_AUTH, static_cast<int>(eDLL_T::NETCON));
data.m_bAuthorized = false;
@ -460,7 +461,7 @@ bool CRConServer::ProcessMessage(const char* pMsgBuf, const int nMsgLen)
request.requesttype() != netcon::request_e::SERVERDATA_REQUEST_AUTH)
{
// Notify netconsole that authentication is required.
SendEncoded(data.m_hSocket, s_NoAuthMessage, "",
SendEncoded(data.m_hSocket, s_NoAuthMessage, sizeof(s_NoAuthMessage)-1, "", 0,
netcon::response_e::SERVERDATA_RESPONSE_AUTH, static_cast<int>(eDLL_T::NETCON));
data.m_bValidated = false;

View File

@ -25,19 +25,19 @@ public:
void Think(void);
void RunFrame(void);
bool SendEncoded(const char* pResponseMsg, const char* pResponseVal,
bool SendEncoded(const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen,
const netcon::response_e responseType,
const int nMessageId = static_cast<int>(eDLL_T::NETCON),
const int nMessageType = static_cast<int>(LogType_t::LOG_NET)) const;
bool SendEncoded(const SocketHandle_t hSocket, const char* pResponseMsg,
const char* pResponseVal, const netcon::response_e responseType,
bool SendEncoded(const SocketHandle_t hSocket, const char* pResponseMsg, const size_t nResponseMsgLen,
const char* pResponseVal, const size_t nResponseValLen, const netcon::response_e responseType,
const int nMessageId = static_cast<int>(eDLL_T::NETCON),
const int nMessageType = static_cast<int>(LogType_t::LOG_NET)) const;
bool SendToAll(const char* pMsgBuf, const int nMsgLen) const;
bool Serialize(vector<char>& vecBuf, const char* pResponseMsg, const char* pResponseVal, const netcon::response_e responseType,
const int nMessageId = static_cast<int>(eDLL_T::NETCON), const int nMessageType = static_cast<int>(LogType_t::LOG_NET)) const;
bool Serialize(vector<char>& vecBuf, const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen,
const netcon::response_e responseType, const int nMessageId = static_cast<int>(eDLL_T::NETCON), const int nMessageType = static_cast<int>(LogType_t::LOG_NET)) const;
void Authenticate(const netcon::request& request, CConnectedNetConsoleData& data);
bool Comparator(const string& svPassword) const;

View File

@ -148,6 +148,7 @@ bool CNetConBase::ProcessBuffer(CConnectedNetConsoleData& data,
const char* pRecvBuf, int nRecvLen, const int nMaxLen)
{
bool bSuccess = true;
byte prefix[sizeof(u_long)] = {};
while (nRecvLen > 0)
{
@ -173,18 +174,22 @@ bool CNetConBase::ProcessBuffer(CConnectedNetConsoleData& data,
data.m_nPayloadRead = 0;
}
}
else if (data.m_nPayloadRead < sizeof(int)) // Read size field.
else if (data.m_nPayloadRead < sizeof(u_long)) // Read size field.
{
data.m_RecvBuffer[data.m_nPayloadRead++] = *pRecvBuf;
prefix[data.m_nPayloadRead++] = *pRecvBuf;
pRecvBuf++;
nRecvLen--;
}
else // Build prefix.
{
data.m_nPayloadLen = int(ntohl(*reinterpret_cast<u_long*>(&data.m_RecvBuffer[0])));
u_long* const pPrefix = reinterpret_cast<u_long*>(&prefix[0]);
data.m_nPayloadLen = int(ntohl(*pPrefix));
data.m_nPayloadRead = 0;
*pPrefix = 0;
if (!data.m_bAuthorized && nMaxLen > -1)
{
if (data.m_nPayloadLen > nMaxLen)
@ -281,15 +286,7 @@ bool CNetConBase::Decode(google::protobuf::MessageLite* pMsg,
bool CNetConBase::Send(const SocketHandle_t hSocket, const char* pMsgBuf,
const int nMsgLen) const
{
std::ostringstream sendbuf;
const u_long nLen = htonl(u_long(nMsgLen));
sendbuf.write(reinterpret_cast<const char*>(&nLen), sizeof(u_long));
sendbuf.write(pMsgBuf, nMsgLen);
int ret = ::send(hSocket, sendbuf.str().data(), int(sendbuf.str().size()),
MSG_NOSIGNAL);
const int ret = ::send(hSocket, pMsgBuf, nMsgLen, MSG_NOSIGNAL);
return (ret != SOCKET_ERROR);
}

View File

@ -13,7 +13,9 @@
// Input : *pBase -
// &vecBuf -
// *pResponseMsg -
// nResponseMsgLen -
// *pResponseVal -
// nResponseValLen -
// responseType -
// nMessageId -
// nMessageType -
@ -21,7 +23,8 @@
// bDebug -
// Output : true on success, false otherwise
//-----------------------------------------------------------------------------
bool NetconServer_Serialize(const CNetConBase* pBase, vector<char>& vecBuf, const char* pResponseMsg, const char* pResponseVal,
bool NetconServer_Serialize(const CNetConBase* pBase, vector<char>& vecBuf,
const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen,
const netcon::response_e responseType, const int nMessageId, const int nMessageType, const bool bEncrypt, const bool bDebug)
{
netcon::response response;
@ -29,8 +32,8 @@ bool NetconServer_Serialize(const CNetConBase* pBase, vector<char>& vecBuf, cons
response.set_messageid(nMessageId);
response.set_messagetype(nMessageType);
response.set_responsetype(responseType);
response.set_responsemsg(pResponseMsg);
response.set_responseval(pResponseVal);
response.set_responsemsg(pResponseMsg, nResponseMsgLen);
response.set_responseval(pResponseVal, nResponseValLen);
if (!NetconShared_PackEnvelope(pBase, vecBuf, response.ByteSizeLong(), &response, bEncrypt, bDebug))
{
@ -45,21 +48,23 @@ bool NetconServer_Serialize(const CNetConBase* pBase, vector<char>& vecBuf, cons
// Input : *pBase -
// &vecBuf -
// *szReqBuf -
// nReqMsgLen -
// *szReqVal -
// nReqValLen -
// *requestType -
// bEncrypt -
// bDebug -
// Output : true on success, false otherwise
//-----------------------------------------------------------------------------
bool NetconClient_Serialize(const CNetConBase* pBase, vector<char>& vecBuf, const char* szReqBuf,
const char* szReqVal, const netcon::request_e requestType, const bool bEncrypt, const bool bDebug)
bool NetconClient_Serialize(const CNetConBase* pBase, vector<char>& vecBuf, const char* szReqBuf, const size_t nReqMsgLen,
const char* szReqVal, const size_t nReqValLen, const netcon::request_e requestType, const bool bEncrypt, const bool bDebug)
{
netcon::request request;
request.set_messageid(-1);
request.set_requesttype(requestType);
request.set_requestmsg(szReqBuf);
request.set_requestval(szReqVal);
request.set_requestmsg(szReqBuf, nReqMsgLen);
request.set_requestval(szReqVal, nReqValLen);
if (!NetconShared_PackEnvelope(pBase, vecBuf, request.ByteSizeLong(), &request, bEncrypt, bDebug))
{
@ -169,9 +174,13 @@ bool NetconShared_PackEnvelope(const CNetConBase* pBase, vector<char>& outMsgBuf
envelope.set_data(dataBuf, nMsgLen);
const size_t envelopeSize = envelope.ByteSizeLong();
outMsgBuf.resize(envelopeSize);
outMsgBuf.resize(envelopeSize + sizeof(u_long));
char* const scratch = outMsgBuf.data();
if (!pBase->Encode(&envelope, &outMsgBuf[0], envelopeSize))
// Write out frame size in network byte order.
*reinterpret_cast<u_long*>(scratch) = htonl(u_long(envelopeSize));
if (!pBase->Encode(&envelope, &scratch[sizeof(u_long)], envelopeSize))
{
if (bDebug)
{

View File

@ -16,11 +16,12 @@ extern void RCON_InitClientAndTrySyncKeys();
#endif // !DEDICATED
#endif // _TOOLS
bool NetconServer_Serialize(const CNetConBase* pBase, vector<char>& vecBuf, const char* pResponseMsg, const char* pResponseVal,
bool NetconServer_Serialize(const CNetConBase* pBase, vector<char>& vecBuf,
const char* pResponseMsg, const size_t nResponseMsgLen, const char* pResponseVal, const size_t nResponseValLen,
const netcon::response_e responseType, const int nMessageId, const int nMessageType, const bool bEncrypt, const bool bDebug);
bool NetconClient_Serialize(const CNetConBase* pBase, vector<char>& vecBuf, const char* szReqBuf,
const char* szReqVal, const netcon::request_e requestType, const bool bEncrypt, const bool bDebug);
bool NetconClient_Serialize(const CNetConBase* pBase, vector<char>& vecBuf, const char* szReqBuf, const size_t nReqMsgLen,
const char* szReqVal, const size_t nReqValLen, const netcon::request_e requestType, const bool bEncrypt, const bool bDebug);
bool NetconClient_Connect(CNetConBase* pBase, const char* pHostAdr, const int nHostPort);
bool NetconShared_PackEnvelope(const CNetConBase* pBase, vector<char>& outMsgBuf, const size_t nMsgLen, google::protobuf::MessageLite* inMsg, const bool bEncrypt, const bool bDebug);