From 66cadeb7fdcf4b75fe1d6ce71913f256547bca9a Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Sat, 9 May 2026 15:24:02 -0300 Subject: [PATCH 01/13] A server crash was identified occurring when the system attempted to send log events containing invalid UTF-8 characters (commonly found in player names or chat messages with special symbols). By default, the nlohmann/json library throws an unhandled exception when encountering malformed strings during the serialization process. --- LogApi/LogApi.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/LogApi/LogApi.cpp b/LogApi/LogApi.cpp index c8b212f..70da561 100644 --- a/LogApi/LogApi.cpp +++ b/LogApi/LogApi.cpp @@ -140,11 +140,20 @@ void CLogApi::SendEvent(int EventIndex, nlohmann::ordered_json Event) { if (!Event.empty()) { if (gLogCvar.m_Timeout) { if (gLogCvar.m_Bearer) { - // POST to webserver - gLogCurl.PostJSON(gLogCvar.m_Address->string, - (long)gLogCvar.m_Timeout->value, - gLogCvar.m_Bearer->string, Event.dump(), - EventIndex); + try { + // POST to webserver + // Use error_handler_t::replace to avoid crashes on invalid UTF-8 + gLogCurl.PostJSON( + gLogCvar.m_Address->string, + (long)gLogCvar.m_Timeout->value, + gLogCvar.m_Bearer->string, + Event.dump(-1, ' ', false, + nlohmann::ordered_json::error_handler_t::replace), + EventIndex); + } catch (const std::exception &e) { + LOG_CONSOLE(PLID, "[%s] JSON Serialization error: %s", + __func__, e.what()); + } } } } From 8de9ce84281712754b569e35dc9193380db507da Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Sat, 9 May 2026 15:29:27 -0300 Subject: [PATCH 02/13] I have analyzed the crash reports and implemented a series of fixes to make the LogApi significantly more stable. The crash logs confirmed that the failure occurred during JSON serialization (dump_escaped), typically caused by invalid UTF-8 characters or null pointers. --- LogApi/LogApi.cpp | 10 +-- LogApi/LogApi.h | 2 +- LogApi/LogEvent.cpp | 144 +++++++++++++++++++++---------------------- LogApi/LogEvent.h | 2 +- LogApi/LogPlayer.cpp | 8 ++- LogApi/LogUtil.cpp | 3 +- 6 files changed, 88 insertions(+), 81 deletions(-) diff --git a/LogApi/LogApi.cpp b/LogApi/LogApi.cpp index 70da561..0c06081 100644 --- a/LogApi/LogApi.cpp +++ b/LogApi/LogApi.cpp @@ -125,7 +125,7 @@ int CLogApi::EventEnabled(const char *EventName) { } // Send event -void CLogApi::SendEvent(int EventIndex, nlohmann::ordered_json Event) { +void CLogApi::SendEvent(int EventIndex, const nlohmann::ordered_json& Event) { // Is Running if (this->m_Running) { // If address is set @@ -139,7 +139,7 @@ void CLogApi::SendEvent(int EventIndex, nlohmann::ordered_json Event) { // If JSON is not empty if (!Event.empty()) { if (gLogCvar.m_Timeout) { - if (gLogCvar.m_Bearer) { + if (gLogCvar.m_Bearer && gLogCvar.m_Bearer->string) { try { // POST to webserver // Use error_handler_t::replace to avoid crashes on invalid UTF-8 @@ -506,10 +506,12 @@ nlohmann::ordered_json CLogApi::GetServerInfo() { nlohmann::ordered_json ServerInfo; // Set address - ServerInfo["Address"] = g_engfuncs.pfnCVarGetString("net_address"); + auto NetAddress = g_engfuncs.pfnCVarGetString("net_address"); + ServerInfo["Address"] = NetAddress ? NetAddress : ""; // Set hostname - ServerInfo["Hostname"] = g_engfuncs.pfnCVarGetString("hostname"); + auto Hostname = g_engfuncs.pfnCVarGetString("hostname"); + ServerInfo["Hostname"] = Hostname ? Hostname : ""; // Set map name ServerInfo["Map"] = STRING(gpGlobals->mapname); diff --git a/LogApi/LogApi.h b/LogApi/LogApi.h index e27089b..0ad80d0 100644 --- a/LogApi/LogApi.h +++ b/LogApi/LogApi.h @@ -19,7 +19,7 @@ class CLogApi int EventEnabled(const char* EventName); // Send Event - void SendEvent(int EventIndex, nlohmann::ordered_json EventData); + void SendEvent(int EventIndex, const nlohmann::ordered_json& EventData); // Callback Result void CallbackResult(CURL* ch, size_t Size, const char* Memory, int EventIndex); diff --git a/LogApi/LogEvent.cpp b/LogApi/LogEvent.cpp index 0268be2..a3bce0f 100644 --- a/LogApi/LogEvent.cpp +++ b/LogApi/LogEvent.cpp @@ -6,17 +6,17 @@ void CLogEvent::ServerActivate(edict_t* pEdictList, int edictCount, int clientMa { if (gLogApi.EventEnabled(__func__)) { - this->m_Event.clear(); + nlohmann::ordered_json Event; - this->m_Event["Event"] = __func__; + Event["Event"] = __func__; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + Event["Server"] = gLogApi.GetServerInfo(); - this->m_Event["EdictCount"] = edictCount; + Event["EdictCount"] = edictCount; - this->m_Event["ClientMax"] = clientMax; + Event["ClientMax"] = clientMax; - gLogApi.SendEvent(LogApi::Events::ServerActivate, this->m_Event); + gLogApi.SendEvent(LogApi::Events::ServerActivate, Event); } } @@ -24,13 +24,13 @@ void CLogEvent::ServerDeactivate() { if (gLogApi.EventEnabled(__func__)) { - this->m_Event.clear(); + nlohmann::ordered_json Event; - this->m_Event["Event"] = __func__; + Event["Event"] = __func__; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + Event["Server"] = gLogApi.GetServerInfo(); - gLogApi.SendEvent(LogApi::Events::ServerDeactivate, this->m_Event); + gLogApi.SendEvent(LogApi::Events::ServerDeactivate, Event); } } @@ -42,17 +42,17 @@ void CLogEvent::ServerAlertMessage(ALERT_TYPE aType, const char* szBuffer) { if (szBuffer[0u] != '\0') { - this->m_Event.clear(); + nlohmann::ordered_json Event; - this->m_Event["Event"] = __func__; + Event["Event"] = __func__; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + Event["Server"] = gLogApi.GetServerInfo(); - this->m_Event["Type"] = aType; + Event["Type"] = aType; - this->m_Event["Message"] = szBuffer; + Event["Message"] = szBuffer; - gLogApi.SendEvent(LogApi::Events::ServerAlertMessage, this->m_Event); + gLogApi.SendEvent(LogApi::Events::ServerAlertMessage, Event); } } } @@ -62,13 +62,13 @@ void CLogEvent::ServerInfo() { if (gLogApi.EventEnabled(__func__)) { - this->m_Event.clear(); + nlohmann::ordered_json Event; - this->m_Event["Event"] = __func__; + Event["Event"] = __func__; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + Event["Server"] = gLogApi.GetServerInfo(); - gLogApi.SendEvent(LogApi::Events::ServerInfo, this->m_Event); + gLogApi.SendEvent(LogApi::Events::ServerInfo, Event); } } @@ -76,18 +76,18 @@ void CLogEvent::ClientConnect(edict_t* pEdict, const char* pszName, const char* { if (gLogApi.EventEnabled(__func__)) { - this->m_Event.clear(); + nlohmann::ordered_json Event; if (!FNullEnt(pEdict)) { - this->m_Event["Event"] = __func__; + Event["Event"] = __func__; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + Event["Server"] = gLogApi.GetServerInfo(); - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); } - gLogApi.SendEvent(LogApi::Events::ClientConnect, this->m_Event); + gLogApi.SendEvent(LogApi::Events::ClientConnect, Event); } } @@ -95,18 +95,18 @@ void CLogEvent::ClientPutInServer(edict_t* pEdict) { if (gLogApi.EventEnabled(__func__)) { - this->m_Event.clear(); + nlohmann::ordered_json Event; if (!FNullEnt(pEdict)) { - this->m_Event["Event"] = __func__; + Event["Event"] = __func__; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + Event["Server"] = gLogApi.GetServerInfo(); - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); } - gLogApi.SendEvent(LogApi::Events::ClientPutInServer, this->m_Event); + gLogApi.SendEvent(LogApi::Events::ClientPutInServer, Event); } } @@ -114,22 +114,22 @@ void CLogEvent::ClientDisconnect(edict_t* pEdict, bool Crash, const char* Reason { if (gLogApi.EventEnabled(__func__)) { - this->m_Event.clear(); + nlohmann::ordered_json Event; if (!FNullEnt(pEdict)) { - this->m_Event["Event"] = __func__; + Event["Event"] = __func__; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + Event["Server"] = gLogApi.GetServerInfo(); - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - this->m_Event["Crash"] = Crash; + Event["Crash"] = Crash; - this->m_Event["Reason"] = Reason ? Reason : ""; + Event["Reason"] = Reason ? Reason : ""; } - gLogApi.SendEvent(LogApi::Events::ClientDisconnect, this->m_Event); + gLogApi.SendEvent(LogApi::Events::ClientDisconnect, Event); } } @@ -137,18 +137,18 @@ void CLogEvent::ClientKill(edict_t* pEdict) { if (gLogApi.EventEnabled(__func__)) { - this->m_Event.clear(); + nlohmann::ordered_json Event; if (!FNullEnt(pEdict)) { - this->m_Event["Event"] = __func__; + Event["Event"] = __func__; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + Event["Server"] = gLogApi.GetServerInfo(); - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); } - gLogApi.SendEvent(LogApi::Events::ClientKill, this->m_Event); + gLogApi.SendEvent(LogApi::Events::ClientKill, Event); } } @@ -156,20 +156,20 @@ void CLogEvent::ClientUserInfoChanged(edict_t* pEdict, char* InfoBuffer) { if (gLogApi.EventEnabled(__func__)) { - this->m_Event.clear(); + nlohmann::ordered_json Event; if (!FNullEnt(pEdict)) { - this->m_Event["Event"] = __func__; + Event["Event"] = __func__; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + Event["Server"] = gLogApi.GetServerInfo(); - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - this->m_Event["InfoBuffer"] = InfoBuffer; + Event["InfoBuffer"] = InfoBuffer ? InfoBuffer : ""; } - gLogApi.SendEvent(LogApi::Events::ClientUserInfoChanged, this->m_Event); + gLogApi.SendEvent(LogApi::Events::ClientUserInfoChanged, Event); } } @@ -177,7 +177,7 @@ void CLogEvent::ClientCommand(edict_t* pEdict) { if (gLogApi.EventEnabled(__func__)) { - this->m_Event.clear(); + nlohmann::ordered_json Event; if (!FNullEnt(pEdict)) { @@ -187,15 +187,15 @@ void CLogEvent::ClientCommand(edict_t* pEdict) { if (Command[0u] != '\0') { - this->m_Event["Event"] = __func__; + Event["Event"] = __func__; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + Event["Server"] = gLogApi.GetServerInfo(); - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - this->m_Event["Command"] = Command; + Event["Command"] = Command; - this->m_Event["Args"] = ""; + Event["Args"] = ""; auto Args = g_engfuncs.pfnCmd_Args(); @@ -203,14 +203,14 @@ void CLogEvent::ClientCommand(edict_t* pEdict) { if (Args[0u] != '\0') { - this->m_Event["Args"] = Args; + Event["Args"] = Args; } } } } } - gLogApi.SendEvent(LogApi::Events::ClientCommand, this->m_Event); + gLogApi.SendEvent(LogApi::Events::ClientCommand, Event); } } @@ -218,7 +218,7 @@ void CLogEvent::ClientSay(edict_t* pEdict) { if (gLogApi.EventEnabled(__func__)) { - this->m_Event.clear(); + nlohmann::ordered_json Event; if (!FNullEnt(pEdict)) { @@ -238,15 +238,15 @@ void CLogEvent::ClientSay(edict_t* pEdict) { Message.erase(std::remove(Message.begin(), Message.end(), '\"'),Message.end()); - this->m_Event["Event"] = __func__; + Event["Event"] = __func__; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + Event["Server"] = gLogApi.GetServerInfo(); - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - this->m_Event["Type"] = Type; + Event["Type"] = Type; - this->m_Event["Message"] = Message; + Event["Message"] = Message; } } } @@ -254,7 +254,7 @@ void CLogEvent::ClientSay(edict_t* pEdict) } } - gLogApi.SendEvent(LogApi::Events::ClientSay, this->m_Event); + gLogApi.SendEvent(LogApi::Events::ClientSay, Event); } } @@ -264,26 +264,26 @@ void CLogEvent::ClientMenuHandle(edict_t* pEdict, std::string Callback, P_MENU_I { if (!Callback.empty()) { - this->m_Event.clear(); + nlohmann::ordered_json Event; if (!FNullEnt(pEdict)) { - this->m_Event["Event"] = Callback; + Event["Event"] = Callback; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + Event["Server"] = gLogApi.GetServerInfo(); - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - this->m_Event["Item"]["Info"] = Item.Info; + Event["Item"]["Info"] = Item.Info; - this->m_Event["Item"]["Text"] = Item.Text; + Event["Item"]["Text"] = Item.Text; - this->m_Event["Item"]["Disabled"] = Item.Disabled; + Event["Item"]["Disabled"] = Item.Disabled; - this->m_Event["Item"]["Extra"] = Item.Extra; + Event["Item"]["Extra"] = Item.Extra; } - gLogApi.SendEvent(LogApi::Events::ClientMenuHandle, this->m_Event); + gLogApi.SendEvent(LogApi::Events::ClientMenuHandle, Event); } } } diff --git a/LogApi/LogEvent.h b/LogApi/LogEvent.h index 95b9402..e7c4a59 100644 --- a/LogApi/LogEvent.h +++ b/LogApi/LogEvent.h @@ -35,7 +35,7 @@ class CLogEvent void ClientSay(edict_t* pEdict); void ClientMenuHandle(edict_t* pEdict, std::string Callback, P_MENU_ITEM Item); - nlohmann::ordered_json m_Event; + }; extern CLogEvent gLogEvent; \ No newline at end of file diff --git a/LogApi/LogPlayer.cpp b/LogApi/LogPlayer.cpp index 97c86c6..21399ee 100644 --- a/LogApi/LogPlayer.cpp +++ b/LogApi/LogPlayer.cpp @@ -70,7 +70,8 @@ void CLogPlayer::Update(edict_t *pEdict) { this->m_Players[Auth].Auth = Auth; - this->m_Players[Auth].Name = STRING(pEdict->v.netname); + auto NetName = STRING(pEdict->v.netname); + this->m_Players[Auth].Name = NetName ? NetName : ""; this->m_Players[Auth].UserId = g_engfuncs.pfnGetPlayerUserId(pEdict); @@ -125,8 +126,11 @@ nlohmann::ordered_json CLogPlayer::GetPlayerJson(edict_t *pEdict) { if (!FNullEnt(pEdict)) { auto Auth = g_engfuncs.pfnGetPlayerAuthId(pEdict); + if (!Auth) { + Auth = ""; + } - if (Auth) { + if (Auth[0u] != '\0') { auto Player = this->GetPlayer(Auth); if (Player != nullptr) { diff --git a/LogApi/LogUtil.cpp b/LogApi/LogUtil.cpp index 1c15aef..54decbc 100644 --- a/LogApi/LogUtil.cpp +++ b/LogApi/LogUtil.cpp @@ -376,7 +376,8 @@ const char *CLogUtil::GetAuthId(edict_t *pEntity) { if (Auth) { if (Auth[0u] != '\0') { if (!Q_stricmp(Auth, "BOT")) { - return STRING(pEntity->v.netname); + auto NetName = STRING(pEntity->v.netname); + return NetName ? NetName : "BOT"; } return Auth; From 4bb63cc151b200412dc704efe26e4990988f9221 Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Sat, 9 May 2026 15:46:17 -0300 Subject: [PATCH 03/13] feat: implement cURL-based HTTP client and menu management system for LogApi --- LogApi/LogApi.cpp | 3 ++- LogApi/LogCurl.cpp | 25 +++++++++++++------------ LogApi/LogMenu.cpp | 19 ++++++++----------- LogApi/LogUtil.cpp | 8 +++++--- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/LogApi/LogApi.cpp b/LogApi/LogApi.cpp index 0c06081..963a4bd 100644 --- a/LogApi/LogApi.cpp +++ b/LogApi/LogApi.cpp @@ -514,7 +514,8 @@ nlohmann::ordered_json CLogApi::GetServerInfo() { ServerInfo["Hostname"] = Hostname ? Hostname : ""; // Set map name - ServerInfo["Map"] = STRING(gpGlobals->mapname); + auto MapName = STRING(gpGlobals->mapname); + ServerInfo["Map"] = MapName ? MapName : ""; // Set game ServerInfo["Game"] = "Counter-Strike"; diff --git a/LogApi/LogCurl.cpp b/LogApi/LogCurl.cpp index e3bf192..37cfdc5 100644 --- a/LogApi/LogCurl.cpp +++ b/LogApi/LogCurl.cpp @@ -4,7 +4,7 @@ CLogCurl gLogCurl; void CLogCurl::ServerActivate() { if (!this->m_MultiHandle) { - this->m_RequestIndex = 0; + this->m_RequestIndex = 1; this->m_Data.clear(); @@ -27,21 +27,22 @@ void CLogCurl::ServerFrame() { this->m_MultiHandle, &HandleCount))) { if (MsgInfo->msg == CURLMSG_DONE) { char *pPrivate = nullptr; - curl_easy_getinfo(MsgInfo->easy_handle, CURLINFO_PRIVATE, &pPrivate); - long Index = (long)(intptr_t)pPrivate; + if (curl_easy_getinfo(MsgInfo->easy_handle, CURLINFO_PRIVATE, &pPrivate) == CURLE_OK && pPrivate) { + long Index = (long)(intptr_t)pPrivate; - if (this->m_Data.find(Index) != this->m_Data.end()) { - gLogApi.CallbackResult(MsgInfo->easy_handle, this->m_Data[Index].Size, - this->m_Data[Index].Memory, - this->m_Data[Index].EventIndex); + if (this->m_Data.find(Index) != this->m_Data.end()) { + gLogApi.CallbackResult(MsgInfo->easy_handle, this->m_Data[Index].Size, + this->m_Data[Index].Memory, + this->m_Data[Index].EventIndex); - free(this->m_Data[Index].Memory); + free(this->m_Data[Index].Memory); - if (this->m_Data[Index].Headers) { - curl_slist_free_all(this->m_Data[Index].Headers); - } + if (this->m_Data[Index].Headers) { + curl_slist_free_all(this->m_Data[Index].Headers); + } - this->m_Data.erase(Index); + this->m_Data.erase(Index); + } } curl_multi_remove_handle(this->m_MultiHandle, MsgInfo->easy_handle); diff --git a/LogApi/LogMenu.cpp b/LogApi/LogMenu.cpp index 1ec29c1..3b37c79 100644 --- a/LogApi/LogMenu.cpp +++ b/LogApi/LogMenu.cpp @@ -231,31 +231,28 @@ void CLogMenu::ShowMenu(int EntityIndex, int Slots, int Time, std::string Text) gLogUtil.ReplaceAll(Text, "^R", "\\R"); gLogUtil.ReplaceAll(Text, "^n", "\n"); - char BufferMenu[MAX_BUFFER_MENU * 6] = { 0 }; + char BufferMenu[MAX_BUFFER_MENU * 6 + 1] = { 0 }; - Text.copy(BufferMenu, Text.length() + 1); + auto CopySize = std::min(Text.length(), (size_t)(MAX_BUFFER_MENU * 6)); + Text.copy(BufferMenu, CopySize); + BufferMenu[CopySize] = '\0'; char* pMenuList = BufferMenu; - char* aMenuList = BufferMenu; - - int iCharCount = 0; while (pMenuList && *pMenuList) { char szChunk[MAX_BUFFER_MENU + 1] = { 0 }; strncpy(szChunk, pMenuList, MAX_BUFFER_MENU); + szChunk[MAX_BUFFER_MENU] = '\0'; - szChunk[MAX_BUFFER_MENU] = 0; - - iCharCount += strlen(szChunk); - - pMenuList = aMenuList + iCharCount; + int ChunkLen = strlen(szChunk); + pMenuList += ChunkLen; g_engfuncs.pfnMessageBegin(MSG_ONE, iMsgShowMenu, nullptr, Player->edict()); g_engfuncs.pfnWriteShort(Slots); g_engfuncs.pfnWriteChar(Time); - g_engfuncs.pfnWriteByte(*pMenuList ? TRUE : FALSE); + g_engfuncs.pfnWriteByte((pMenuList && *pMenuList) ? TRUE : FALSE); g_engfuncs.pfnWriteString(szChunk); g_engfuncs.pfnMessageEnd(); } diff --git a/LogApi/LogUtil.cpp b/LogApi/LogUtil.cpp index 54decbc..d4ce151 100644 --- a/LogApi/LogUtil.cpp +++ b/LogApi/LogUtil.cpp @@ -26,9 +26,11 @@ void CLogUtil::ClientPrint(edict_t *pEntity, int msg_dest, const char *Format, Length = 125; } - Buffer[Length++] = '\n'; - Buffer[Length++] = '\n'; - Buffer[Length] = 0; + if (Length < (int)sizeof(Buffer) - 3) { + Buffer[Length++] = '\n'; + Buffer[Length++] = '\n'; + Buffer[Length] = 0; + } } static int iMsgTextMsg; From f71c67b027ee21ab7e14f834932aacd265847e6d Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Sat, 9 May 2026 15:55:03 -0300 Subject: [PATCH 04/13] feat: implement CLogApi module to handle event logging, server communication, and remote command execution --- LogApi/LogApi.cpp | 32 +++++++++++++++++++------------- LogApi/LogApi.h | 14 +++++++------- LogApi/LogCurl.cpp | 6 +++++- LogApi/LogUtil.cpp | 29 +++++++++++++++++------------ 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/LogApi/LogApi.cpp b/LogApi/LogApi.cpp index 963a4bd..ca8870f 100644 --- a/LogApi/LogApi.cpp +++ b/LogApi/LogApi.cpp @@ -211,7 +211,7 @@ void CLogApi::CallbackResult(CURL *ch, size_t Size, const char *Memory, } // Parse event result -void CLogApi::EventResult(int EventIndex, nlohmann::ordered_json Data) { +void CLogApi::EventResult(int EventIndex, const nlohmann::ordered_json& Data) { // Check if has event 'ServerCommand' result from api if (Data.contains("ServerCommand")) { this->ServerCommand(EventIndex, Data); @@ -239,7 +239,7 @@ void CLogApi::EventResult(int EventIndex, nlohmann::ordered_json Data) { } // Execute server command from result -void CLogApi::ServerCommand(int EventIndex, nlohmann::ordered_json Data) { +void CLogApi::ServerCommand(int EventIndex, const nlohmann::ordered_json& Data) { if (gLogCvar.m_ExecCommands) { if (gLogCvar.m_ExecCommands->value <= 0.0f) { return; @@ -289,7 +289,7 @@ void CLogApi::ServerCommand(int EventIndex, nlohmann::ordered_json Data) { } // Open menu from result -void CLogApi::ShowMenu(int EventIndex, nlohmann::ordered_json Data) { +void CLogApi::ShowMenu(int EventIndex, const nlohmann::ordered_json& Data) { if (!Data[__func__].empty()) { if (Data[__func__].is_object()) { if (!Data[__func__]["Items"].empty()) { @@ -339,7 +339,7 @@ void CLogApi::ShowMenu(int EventIndex, nlohmann::ordered_json Data) { // Open menu function void CLogApi::Menu(int EntityIndex, std::string Title, bool Exit, - std::string Callback, nlohmann::ordered_json Items) { + std::string Callback, const nlohmann::ordered_json& Items) { auto Player = UTIL_PlayerByIndexSafe(EntityIndex); if (Player) { @@ -378,7 +378,7 @@ void CLogApi::MenuHandle(int EntityIndex, std::string Callback, } // Print to client from result -void CLogApi::ClientPrint(int EventIndex, nlohmann::ordered_json Data) { +void CLogApi::ClientPrint(int EventIndex, const nlohmann::ordered_json& Data) { // If is not empty if (!Data[__func__].empty()) { try { @@ -402,8 +402,10 @@ void CLogApi::ClientPrint(int EventIndex, nlohmann::ordered_json Data) { // If has entity index if (EntityId > 0) { // Get entity pointer - pEntity = - FNullEnt(INDEXENT(EntityId)) ? INDEXENT(EntityId) : nullptr; + auto Player = UTIL_PlayerByIndexSafe(EntityId); + if (Player) { + pEntity = Player->edict(); + } } // If is not empty @@ -420,7 +422,7 @@ void CLogApi::ClientPrint(int EventIndex, nlohmann::ordered_json Data) { } // Print to player chat from result -void CLogApi::PrintChat(int EventIndex, nlohmann::ordered_json Data) { +void CLogApi::PrintChat(int EventIndex, const nlohmann::ordered_json& Data) { // If is not empty if (!Data[__func__].empty()) { try { @@ -440,8 +442,10 @@ void CLogApi::PrintChat(int EventIndex, nlohmann::ordered_json Data) { // If has entity index if (EntityId > 0) { // Get entity pointer - pEntity = - FNullEnt(INDEXENT(EntityId)) ? INDEXENT(EntityId) : nullptr; + auto Player = UTIL_PlayerByIndexSafe(EntityId); + if (Player) { + pEntity = Player->edict(); + } } // If is not empty @@ -458,7 +462,7 @@ void CLogApi::PrintChat(int EventIndex, nlohmann::ordered_json Data) { } // Print to hudmessage chat from result -void CLogApi::ShowHudMessage(int EventIndex, nlohmann::ordered_json Data) { +void CLogApi::ShowHudMessage(int EventIndex, const nlohmann::ordered_json& Data) { // If is not empty if (!Data[__func__].empty()) { try { @@ -482,8 +486,10 @@ void CLogApi::ShowHudMessage(int EventIndex, nlohmann::ordered_json Data) { // If has entity index if (EntityId > 0) { // Get entity pointer - pEntity = - FNullEnt(INDEXENT(EntityId)) ? INDEXENT(EntityId) : nullptr; + auto Player = UTIL_PlayerByIndexSafe(EntityId); + if (Player) { + pEntity = Player->edict(); + } } // If is not empty diff --git a/LogApi/LogApi.h b/LogApi/LogApi.h index 0ad80d0..f8c7975 100644 --- a/LogApi/LogApi.h +++ b/LogApi/LogApi.h @@ -25,28 +25,28 @@ class CLogApi void CallbackResult(CURL* ch, size_t Size, const char* Memory, int EventIndex); // Parse Event Result - void EventResult(int EventIndex, nlohmann::ordered_json Result); + void EventResult(int EventIndex, const nlohmann::ordered_json& Result); // Server Command - void ServerCommand(int EventIndex, nlohmann::ordered_json Data); + void ServerCommand(int EventIndex, const nlohmann::ordered_json& Data); // Show Menu - void ShowMenu(int EventIndex, nlohmann::ordered_json Data); + void ShowMenu(int EventIndex, const nlohmann::ordered_json& Data); // Open Menu - void Menu(int EntityIndex, std::string Title, bool Exit, std::string Callback, nlohmann::ordered_json Items); + void Menu(int EntityIndex, std::string Title, bool Exit, std::string Callback, const nlohmann::ordered_json& Items); // Menu Handle static void MenuHandle(int EntityIndex, std::string Callback, P_MENU_ITEM Item); // Print to client from result - void ClientPrint(int EventIndex, nlohmann::ordered_json Data); + void ClientPrint(int EventIndex, const nlohmann::ordered_json& Data); // Server Say Text - void PrintChat(int EventIndex, nlohmann::ordered_json Data); + void PrintChat(int EventIndex, const nlohmann::ordered_json& Data); // HUD Message Text - void ShowHudMessage(int EventIndex, nlohmann::ordered_json Data); + void ShowHudMessage(int EventIndex, const nlohmann::ordered_json& Data); // Get Server info nlohmann::ordered_json GetServerInfo(); diff --git a/LogApi/LogCurl.cpp b/LogApi/LogCurl.cpp index 37cfdc5..47d7fe8 100644 --- a/LogApi/LogCurl.cpp +++ b/LogApi/LogCurl.cpp @@ -8,7 +8,11 @@ void CLogCurl::ServerActivate() { this->m_Data.clear(); - curl_global_init(CURL_GLOBAL_ALL); + static bool GlobalInit = false; + if (!GlobalInit) { + curl_global_init(CURL_GLOBAL_ALL); + GlobalInit = true; + } this->m_MultiHandle = curl_multi_init(); } diff --git a/LogApi/LogUtil.cpp b/LogApi/LogUtil.cpp index d4ce151..6ea3ad2 100644 --- a/LogApi/LogUtil.cpp +++ b/LogApi/LogUtil.cpp @@ -4,8 +4,9 @@ CLogUtil gLogUtil; void CLogUtil::ServerExecute(std::string Command) { if (!Command.empty()) { - Command += "\n"; - g_engfuncs.pfnServerCommand(const_cast(Command.c_str())); + char Buffer[1024]; + Q_strncpy(Buffer, Command.c_str(), sizeof(Buffer)); + g_engfuncs.pfnServerCommand(Buffer); } } @@ -204,16 +205,20 @@ CBasePlayer *CLogUtil::FindPlayer(std::string Target) { if (Player) { if (!Player->IsDormant()) { - std::string Name = STRING(Player->edict()->v.netname); - - if (!Name.empty()) { - std::transform(Name.begin(), Name.end(), Name.begin(), - [](unsigned char character) { - return std::tolower(character); - }); - - if (Name.find(Target) != std::string::npos) { - return Player; + if (Player->edict()) { + if (Player->edict()->v.netname) { + std::string Name = STRING(Player->edict()->v.netname); + + if (!Name.empty()) { + std::transform(Name.begin(), Name.end(), Name.begin(), + [](unsigned char character) { + return std::tolower(character); + }); + + if (Name.find(Target) != std::string::npos) { + return Player; + } + } } } } From d84bb1ca983cc2e8187a64c322d2def45fb5be5d Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Sat, 9 May 2026 16:03:41 -0300 Subject: [PATCH 05/13] feat: add CLogCurl class to handle asynchronous JSON POST requests using libcurl --- LogApi/LogApi.cpp | 24 +++++++++++++----------- LogApi/LogCurl.cpp | 5 +++-- LogApi/LogUtil.cpp | 4 ++++ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/LogApi/LogApi.cpp b/LogApi/LogApi.cpp index ca8870f..2cc13e1 100644 --- a/LogApi/LogApi.cpp +++ b/LogApi/LogApi.cpp @@ -33,14 +33,16 @@ void CLogApi::ServerActivate() { // Set file pointer to start of file fseek(fp, 0, SEEK_SET); - // Create empty std::string with file size - std::string buffer(fs, '\0'); + // If read something + if (fs > 0) { + // Create empty std::string with file size + std::string buffer(fs, '\0'); - // Read file to std::string buffer - size_t elements = fread(&buffer[0], 1, fs, fp); + // Read file to std::string buffer + size_t elements = fread(&buffer[0], 1, fs, fp); - // If read something - if (elements > 0) { + // If read something + if (elements > 0) { // Read data auto json = nlohmann::ordered_json::parse(buffer, nullptr, true, true); @@ -59,9 +61,9 @@ void CLogApi::ServerActivate() { LOG_CONSOLE(PLID, "[%s] Failed to open file: %s", __func__, LOG_API_FILE_EVENTS); } - } catch (const nlohmann::ordered_json::parse_error &e) { - // JSON exeption errors - LOG_CONSOLE(PLID, "[%s] %s", __func__, e.what()); + } catch (const std::exception &e) { + // JSON or other exception errors + LOG_CONSOLE(PLID, "[%s] Exception: %s", __func__, e.what()); } } @@ -193,9 +195,9 @@ void CLogApi::CallbackResult(CURL *ch, size_t Size, const char *Memory, gLogApi.EventResult(EventIndex, Result); } } - } catch (const nlohmann::ordered_json::parse_error &e) { + } catch (const std::exception &e) { // Log - LOG_CONSOLE(PLID, "[%s] %s", __func__, e.what()); + LOG_CONSOLE(PLID, "[%s] Exception: %s", __func__, e.what()); } } } diff --git a/LogApi/LogCurl.cpp b/LogApi/LogCurl.cpp index 47d7fe8..135932a 100644 --- a/LogApi/LogCurl.cpp +++ b/LogApi/LogCurl.cpp @@ -27,8 +27,7 @@ void CLogCurl::ServerFrame() { curl_multi_perform(this->m_MultiHandle, &HandleCount); - while (ProcessedThisFrame < 5 && (MsgInfo = curl_multi_info_read( - this->m_MultiHandle, &HandleCount))) { + while ((MsgInfo = curl_multi_info_read(this->m_MultiHandle, &HandleCount))) { if (MsgInfo->msg == CURLMSG_DONE) { char *pPrivate = nullptr; if (curl_easy_getinfo(MsgInfo->easy_handle, CURLINFO_PRIVATE, &pPrivate) == CURLE_OK && pPrivate) { @@ -104,6 +103,8 @@ void CLogCurl::PostJSON(const char *url, long Timeout, std::string BearerToken, curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, 2L); + curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt(ch, CURLOPT_WRITEDATA, (void *)&this->m_Data[this->m_RequestIndex]); diff --git a/LogApi/LogUtil.cpp b/LogApi/LogUtil.cpp index 6ea3ad2..a587f31 100644 --- a/LogApi/LogUtil.cpp +++ b/LogApi/LogUtil.cpp @@ -322,6 +322,10 @@ void CLogUtil::HudMessage(edict_t *pEntity, hudtextparms_t textparms, } void CLogUtil::ShowMotd(edict_t *pEntity, char *Motd, int MotdLength) { + if (!Motd || Motd[0u] == '\0') { + return; + } + static int iMsgMOTD; if (iMsgMOTD || From 957208bee32e0d6f5ad17dd3393387eb146508db Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Sat, 9 May 2026 16:18:02 -0300 Subject: [PATCH 06/13] feat: implement CLogApi class to handle remote event logging and API response processing --- LogApi/LogApi.cpp | 5 +++-- LogApi/Makefile | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/LogApi/LogApi.cpp b/LogApi/LogApi.cpp index 2cc13e1..dbfe91a 100644 --- a/LogApi/LogApi.cpp +++ b/LogApi/LogApi.cpp @@ -53,9 +53,10 @@ void CLogApi::ServerActivate() { std::make_pair(event.key(), event.value().get())); } } + } - // Close file pointer - fclose(fp); + // Close file pointer + fclose(fp); } else { // Failed on error LOG_CONSOLE(PLID, "[%s] Failed to open file: %s", __func__, diff --git a/LogApi/Makefile b/LogApi/Makefile index 7b3111a..afdd2c7 100644 --- a/LogApi/Makefile +++ b/LogApi/Makefile @@ -12,7 +12,7 @@ CLEAN_CMD=rm -rf $(BUILD_DIR) # Common build definitions BUILD_NAME=logapi_mm BUILD_SOURCES=$(wildcard *.cpp) -BUILD_CFLAGS=-std=c++11 -Wall -Wno-unknown-pragmas -fcommon -m32 -mtune=generic -msse3 -msse4.1 -shared -s -pipe -g0 -O3 -fPIC -funroll-loops -fomit-frame-pointer -fno-stack-protector -fvisibility=hidden +BUILD_CFLAGS=-std=c++11 -Wall -Wno-unknown-pragmas -fcommon -m32 -mtune=generic -msse3 -msse4.1 -shared -s -pipe -g0 -O3 -fPIC -funroll-loops -fomit-frame-pointer -fno-stack-protector -fvisibility=hidden -fno-strict-aliasing BUILD_TARGET=linux32 BUILD_EXTENSION=so BUILD_OBJECTS=$(BUILD_SOURCES:%.cpp=$(BUILD_DIR)/%.o) From c62482d2e3ecc810d88bac5491f3a51e730a571d Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Sat, 9 May 2026 16:25:42 -0300 Subject: [PATCH 07/13] feat: initialize LogApi plugin with cURL, JSON support, and event handling logic --- LogApi/LogApi.cpp | 11 ++++++++--- LogApi/LogCurl.cpp | 3 +++ LogApi/MetaMod.cpp | 12 ++++++++++++ LogApi/precompiled.h | 6 +++--- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/LogApi/LogApi.cpp b/LogApi/LogApi.cpp index dbfe91a..d21449d 100644 --- a/LogApi/LogApi.cpp +++ b/LogApi/LogApi.cpp @@ -57,6 +57,8 @@ void CLogApi::ServerActivate() { // Close file pointer fclose(fp); + + LOG_CONSOLE(PLID, "[%s] Loaded %d events from %s", __func__, this->m_Events.size(), LOG_API_FILE_EVENTS); } else { // Failed on error LOG_CONSOLE(PLID, "[%s] Failed to open file: %s", __func__, @@ -66,6 +68,8 @@ void CLogApi::ServerActivate() { // JSON or other exception errors LOG_CONSOLE(PLID, "[%s] Exception: %s", __func__, e.what()); } + + LOG_CONSOLE(PLID, "[%s] Finished", __func__); } // On server deactivate @@ -531,12 +535,13 @@ nlohmann::ordered_json CLogApi::GetServerInfo() { // If CSGameRules is not null if (g_pGameRules) { + auto Rules = CSGameRules(); // If server has game description - if (CSGameRules()->m_GameDesc) { + if (Rules && Rules->m_GameDesc) { // If is not empty - if (CSGameRules()->m_GameDesc[0u] != '\0') { + if (Rules->m_GameDesc[0u] != '\0') { // Set game description name - ServerInfo["Game"] = CSGameRules()->m_GameDesc; + ServerInfo["Game"] = Rules->m_GameDesc; } } } diff --git a/LogApi/LogCurl.cpp b/LogApi/LogCurl.cpp index 135932a..381dcb2 100644 --- a/LogApi/LogCurl.cpp +++ b/LogApi/LogCurl.cpp @@ -143,6 +143,9 @@ size_t CLogCurl::WriteMemoryCallback(void *contents, size_t size, size_t nmemb, mem->Memory[mem->Size] = 0; return realsize; + } else { + // Log memory error + LOG_CONSOLE(PLID, "[%s] Memory allocation failed!", __func__); } } } diff --git a/LogApi/MetaMod.cpp b/LogApi/MetaMod.cpp index 46a401d..f6584ce 100644 --- a/LogApi/MetaMod.cpp +++ b/LogApi/MetaMod.cpp @@ -121,16 +121,28 @@ C_DLLEXPORT int GetEntityAPI2_Post(DLL_FUNCTIONS *pFunctionTable, void DLL_POST_ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) { + LOG_CONSOLE(PLID, "[%s] Start", __func__); + gLogCvar.ServerActivate(); + LOG_CONSOLE(PLID, "[%s] LogCvar initialized", __func__); + gLogApi.ServerActivate(); + LOG_CONSOLE(PLID, "[%s] LogApi initialized", __func__); + gLogCurl.ServerActivate(); + LOG_CONSOLE(PLID, "[%s] LogCurl initialized", __func__); + gLogCommand.ServerActivate(); + LOG_CONSOLE(PLID, "[%s] LogCommand initialized", __func__); + gLogEvent.ServerActivate(pEdictList, edictCount, clientMax); + LOG_CONSOLE(PLID, "[%s] LogEvent initialized", __func__); + RETURN_META(MRES_IGNORED); } diff --git a/LogApi/precompiled.h b/LogApi/precompiled.h index c5f6d9b..65b9d54 100644 --- a/LogApi/precompiled.h +++ b/LogApi/precompiled.h @@ -38,9 +38,9 @@ #include // cURL sources -#ifndef CURL_STATICLIB -#define CURL_STATICLIB -#endif +//#ifndef CURL_STATICLIB +//#define CURL_STATICLIB +//#endif // cURL sources #include From 2e48e0a3e3072687fa124cdf2efadeb5c7c47e92 Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Sat, 9 May 2026 16:27:24 -0300 Subject: [PATCH 08/13] feat: add Makefile for building LogApi on Linux --- LogApi/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LogApi/Makefile b/LogApi/Makefile index afdd2c7..41feb22 100644 --- a/LogApi/Makefile +++ b/LogApi/Makefile @@ -18,7 +18,7 @@ BUILD_EXTENSION=so BUILD_OBJECTS=$(BUILD_SOURCES:%.cpp=$(BUILD_DIR)/%.o) BUILD_COMPILER=c++ BUILD_LINKER=-static-libgcc -static-libstdc++ -lcurl -lssl -lcrypto -ldl -lm -lz -BUILD_CCFLAGS=$(BUILD_CFLAGS) -DNDEBUG -Dlinux -D__linux__ -D__BUILD__ -D_GLIBCXX_USE_CXX11_ABI=0 -DCURL_STATICLIB -DHTTP_ONLY +BUILD_CCFLAGS=$(BUILD_CFLAGS) -DNDEBUG -Dlinux -D__linux__ -D__BUILD__ -D_GLIBCXX_USE_CXX11_ABI=0 -DHTTP_ONLY # # Include directory SRCDIR=. From 45ac12a88f95f8b17008fdf7deacc2658cddbb6c Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Sat, 9 May 2026 16:42:40 -0300 Subject: [PATCH 09/13] feat: implement Metamod plugin skeleton with event and player logging hooks --- LogApi/LogEvent.cpp | 296 +++++++++++++++++++++++-------------------- LogApi/LogPlayer.cpp | 130 ++++++++++--------- LogApi/Makefile | 4 +- LogApi/MetaMod.cpp | 12 ++ LogApi/precompiled.h | 6 +- 5 files changed, 247 insertions(+), 201 deletions(-) diff --git a/LogApi/LogEvent.cpp b/LogApi/LogEvent.cpp index a3bce0f..031e70c 100644 --- a/LogApi/LogEvent.cpp +++ b/LogApi/LogEvent.cpp @@ -74,216 +74,240 @@ void CLogEvent::ServerInfo() void CLogEvent::ClientConnect(edict_t* pEdict, const char* pszName, const char* pszAddress, char szRejectReason[128]) { - if (gLogApi.EventEnabled(__func__)) - { - nlohmann::ordered_json Event; + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - Event["Event"] = __func__; + // Guard: entity must be fully valid with private data allocated + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - Event["Server"] = gLogApi.GetServerInfo(); + nlohmann::ordered_json Event; - Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - } + Event["Event"] = __func__; - gLogApi.SendEvent(LogApi::Events::ClientConnect, Event); - } + Event["Server"] = gLogApi.GetServerInfo(); + + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + + if (Event.empty()) + return; + + gLogApi.SendEvent(LogApi::Events::ClientConnect, Event); } void CLogEvent::ClientPutInServer(edict_t* pEdict) { - if (gLogApi.EventEnabled(__func__)) - { - nlohmann::ordered_json Event; + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - Event["Event"] = __func__; + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - Event["Server"] = gLogApi.GetServerInfo(); + nlohmann::ordered_json Event; - Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - } - - gLogApi.SendEvent(LogApi::Events::ClientPutInServer, Event); - } + Event["Event"] = __func__; + + Event["Server"] = gLogApi.GetServerInfo(); + + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + + if (Event.empty()) + return; + + gLogApi.SendEvent(LogApi::Events::ClientPutInServer, Event); } void CLogEvent::ClientDisconnect(edict_t* pEdict, bool Crash, const char* Reason) { - if (gLogApi.EventEnabled(__func__)) - { - nlohmann::ordered_json Event; + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - Event["Event"] = __func__; + // During disconnect pvPrivateData may already be freed — guard both checks + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - Event["Server"] = gLogApi.GetServerInfo(); + nlohmann::ordered_json Event; - Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + Event["Event"] = __func__; - Event["Crash"] = Crash; + Event["Server"] = gLogApi.GetServerInfo(); - Event["Reason"] = Reason ? Reason : ""; - } + // GetPlayerJson reads from our internal player cache, not directly from pdata + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - gLogApi.SendEvent(LogApi::Events::ClientDisconnect, Event); - } + Event["Crash"] = Crash; + + // Copy reason immediately — engine buffer may be freed after this call returns + std::string SafeReason = (Reason && Reason[0u] != '\0') ? Reason : ""; + Event["Reason"] = SafeReason; + + if (Event.empty()) + return; + + gLogApi.SendEvent(LogApi::Events::ClientDisconnect, Event); } void CLogEvent::ClientKill(edict_t* pEdict) { - if (gLogApi.EventEnabled(__func__)) - { - nlohmann::ordered_json Event; + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - Event["Event"] = __func__; + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - Event["Server"] = gLogApi.GetServerInfo(); + nlohmann::ordered_json Event; - Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - } + Event["Event"] = __func__; - gLogApi.SendEvent(LogApi::Events::ClientKill, Event); - } + Event["Server"] = gLogApi.GetServerInfo(); + + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + + if (Event.empty()) + return; + + gLogApi.SendEvent(LogApi::Events::ClientKill, Event); } void CLogEvent::ClientUserInfoChanged(edict_t* pEdict, char* InfoBuffer) { - if (gLogApi.EventEnabled(__func__)) - { - nlohmann::ordered_json Event; + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - Event["Event"] = __func__; + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - Event["Server"] = gLogApi.GetServerInfo(); + // Copy immediately — InfoBuffer is a transient engine pointer that may be + // invalidated by subsequent engine calls or after this handler returns + std::string SafeInfo = (InfoBuffer && InfoBuffer[0u] != '\0') ? InfoBuffer : ""; - Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + nlohmann::ordered_json Event; - Event["InfoBuffer"] = InfoBuffer ? InfoBuffer : ""; - } + Event["Event"] = __func__; - gLogApi.SendEvent(LogApi::Events::ClientUserInfoChanged, Event); - } + Event["Server"] = gLogApi.GetServerInfo(); + + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + + Event["InfoBuffer"] = SafeInfo; + + if (Event.empty()) + return; + + gLogApi.SendEvent(LogApi::Events::ClientUserInfoChanged, Event); } void CLogEvent::ClientCommand(edict_t* pEdict) { - if (gLogApi.EventEnabled(__func__)) - { - nlohmann::ordered_json Event; + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - auto Command = g_engfuncs.pfnCmd_Argv(0); + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - if (Command) - { - if (Command[0u] != '\0') - { - Event["Event"] = __func__; + // Capture engine argv/args pointers ONCE and deep-copy them immediately. + // These are transient internal engine buffers invalidated by re-entrant calls. + const char* rawCmd = g_engfuncs.pfnCmd_Argv(0); + if (!rawCmd || rawCmd[0u] == '\0') + return; + std::string Command(rawCmd); - Event["Server"] = gLogApi.GetServerInfo(); + const char* rawArgs = g_engfuncs.pfnCmd_Args(); + std::string Args = (rawArgs && rawArgs[0u] != '\0') ? rawArgs : ""; - Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + nlohmann::ordered_json Event; - Event["Command"] = Command; + Event["Event"] = __func__; - Event["Args"] = ""; + Event["Server"] = gLogApi.GetServerInfo(); - auto Args = g_engfuncs.pfnCmd_Args(); + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - if (Args) - { - if (Args[0u] != '\0') - { - Event["Args"] = Args; - } - } - } - } - } + Event["Command"] = Command; - gLogApi.SendEvent(LogApi::Events::ClientCommand, Event); - } + Event["Args"] = Args; + + if (Event.empty()) + return; + + gLogApi.SendEvent(LogApi::Events::ClientCommand, Event); } void CLogEvent::ClientSay(edict_t* pEdict) { - if (gLogApi.EventEnabled(__func__)) - { - nlohmann::ordered_json Event; + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - std::string Type = g_engfuncs.pfnCmd_Argv(0) ? g_engfuncs.pfnCmd_Argv(0) : ""; + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - if (!Type.empty()) - { - if (Type.length() > 0) - { - if (Type.compare("say") == 0 || Type.compare("say_team") == 0) - { - std::string Message = g_engfuncs.pfnCmd_Args() ? g_engfuncs.pfnCmd_Args() : ""; + // Capture argv(0) once and copy immediately — transient engine pointer + const char* rawType = g_engfuncs.pfnCmd_Argv(0); + if (!rawType || rawType[0u] == '\0') + return; + std::string Type(rawType); - if (!Message.empty()) - { - if (Message.length() > 0) - { - Message.erase(std::remove(Message.begin(), Message.end(), '\"'),Message.end()); + if (Type != "say" && Type != "say_team") + return; - Event["Event"] = __func__; + // Capture args once and copy immediately + const char* rawMsg = g_engfuncs.pfnCmd_Args(); + if (!rawMsg || rawMsg[0u] == '\0') + return; + std::string Message(rawMsg); - Event["Server"] = gLogApi.GetServerInfo(); + // Strip enclosing quotes the engine wraps around chat messages + Message.erase(std::remove(Message.begin(), Message.end(), '\"'), Message.end()); - Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + if (Message.empty()) + return; - Event["Type"] = Type; + nlohmann::ordered_json Event; - Event["Message"] = Message; - } - } - } - } - } - } + Event["Event"] = __func__; - gLogApi.SendEvent(LogApi::Events::ClientSay, Event); - } + Event["Server"] = gLogApi.GetServerInfo(); + + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + + Event["Type"] = Type; + + Event["Message"] = Message; + + if (Event.empty()) + return; + + gLogApi.SendEvent(LogApi::Events::ClientSay, Event); } void CLogEvent::ClientMenuHandle(edict_t* pEdict, std::string Callback, P_MENU_ITEM Item) { - if (gLogApi.EventEnabled(__func__)) - { - if (!Callback.empty()) - { - nlohmann::ordered_json Event; + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - Event["Event"] = Callback; + if (Callback.empty()) + return; - Event["Server"] = gLogApi.GetServerInfo(); + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + nlohmann::ordered_json Event; - Event["Item"]["Info"] = Item.Info; + Event["Event"] = Callback; - Event["Item"]["Text"] = Item.Text; + Event["Server"] = gLogApi.GetServerInfo(); - Event["Item"]["Disabled"] = Item.Disabled; + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - Event["Item"]["Extra"] = Item.Extra; - } + // P_MENU_ITEM is passed by value — the std::string members are already safe + // deep copies of whatever the caller had. Assign with empty-string fallback. + Event["Item"]["Info"] = Item.Info.empty() ? "" : Item.Info; + Event["Item"]["Text"] = Item.Text.empty() ? "" : Item.Text; + Event["Item"]["Disabled"] = Item.Disabled; + Event["Item"]["Extra"] = Item.Extra.empty() ? "" : Item.Extra; - gLogApi.SendEvent(LogApi::Events::ClientMenuHandle, Event); - } - } + if (Event.empty()) + return; + + gLogApi.SendEvent(LogApi::Events::ClientMenuHandle, Event); } diff --git a/LogApi/LogPlayer.cpp b/LogApi/LogPlayer.cpp index 21399ee..f3ab47c 100644 --- a/LogApi/LogPlayer.cpp +++ b/LogApi/LogPlayer.cpp @@ -54,59 +54,61 @@ void CLogPlayer::Disconnect(edict_t *pEdict) { } void CLogPlayer::Update(edict_t *pEdict) { - if (!FNullEnt(pEdict)) { - auto Auth = gLogUtil.GetAuthId(pEdict); + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - if (Auth) { - for (auto it = this->m_Players.begin(); it != this->m_Players.end();) { - if (it->second.EntityId == ENTINDEX(pEdict) && it->first != Auth) { - it = this->m_Players.erase(it); - } else { - ++it; - } - } + auto Auth = gLogUtil.GetAuthId(pEdict); - this->m_Players[Auth].EntityId = ENTINDEX(pEdict); + if (!Auth || Auth[0u] == '\0') + return; - this->m_Players[Auth].Auth = Auth; + for (auto it = this->m_Players.begin(); it != this->m_Players.end();) { + if (it->second.EntityId == ENTINDEX(pEdict) && it->first != Auth) { + it = this->m_Players.erase(it); + } else { + ++it; + } + } - auto NetName = STRING(pEdict->v.netname); - this->m_Players[Auth].Name = NetName ? NetName : ""; + this->m_Players[Auth].EntityId = ENTINDEX(pEdict); - this->m_Players[Auth].UserId = g_engfuncs.pfnGetPlayerUserId(pEdict); + this->m_Players[Auth].Auth = Auth; - auto Player = UTIL_PlayerByIndexSafe(ENTINDEX(pEdict)); + auto NetName = STRING(pEdict->v.netname); + this->m_Players[Auth].Name = NetName ? NetName : ""; - if (Player) { - this->m_Players[Auth].Team = static_cast(Player->m_iTeam); + this->m_Players[Auth].UserId = g_engfuncs.pfnGetPlayerUserId(pEdict); - this->m_Players[Auth].Frags = pEdict->v.frags; + auto Player = UTIL_PlayerByIndexSafe(ENTINDEX(pEdict)); - this->m_Players[Auth].Deaths = Player->m_iDeaths; + if (Player) { + this->m_Players[Auth].Team = static_cast(Player->m_iTeam); - int ping = 0, loss = 0; - g_engfuncs.pfnGetPlayerStats(pEdict, &ping, &loss); - this->m_Players[Auth].Ping = ping; + this->m_Players[Auth].Frags = pEdict->v.frags; - if (this->m_Players[Auth].GameTime <= 0.0f) { - if (Player->m_iTeam == UNASSIGNED) { - if (!Player->IsBot()) { - gLogUtil.TeamInfo(Player->edict(), MAX_CLIENTS + TERRORIST + 1, - "TERRORIST"); - gLogUtil.TeamInfo(Player->edict(), MAX_CLIENTS + CT + 1, "CT"); - } - } - } - } + this->m_Players[Auth].Deaths = Player->m_iDeaths; - if (this->m_Players[Auth].ConnectTime <= 0.0f) { - this->m_Players[Auth].ConnectTime = gpGlobals->time; - } + int ping = 0, loss = 0; + g_engfuncs.pfnGetPlayerStats(pEdict, &ping, &loss); + this->m_Players[Auth].Ping = ping; - this->m_Players[Auth].GameTime = - (gpGlobals->time - this->m_Players[Auth].ConnectTime); + if (this->m_Players[Auth].GameTime <= 0.0f) { + if (Player->m_iTeam == UNASSIGNED) { + if (!Player->IsBot()) { + gLogUtil.TeamInfo(Player->edict(), MAX_CLIENTS + TERRORIST + 1, + "TERRORIST"); + gLogUtil.TeamInfo(Player->edict(), MAX_CLIENTS + CT + 1, "CT"); + } + } } } + + if (this->m_Players[Auth].ConnectTime <= 0.0f) { + this->m_Players[Auth].ConnectTime = gpGlobals->time; + } + + this->m_Players[Auth].GameTime = + (gpGlobals->time - this->m_Players[Auth].ConnectTime); } std::map CLogPlayer::GetPlayers() { @@ -124,29 +126,37 @@ LP_PLAYER_INFO CLogPlayer::GetPlayer(std::string Auth) { nlohmann::ordered_json CLogPlayer::GetPlayerJson(edict_t *pEdict) { nlohmann::ordered_json PlayerJson; - if (!FNullEnt(pEdict)) { - auto Auth = g_engfuncs.pfnGetPlayerAuthId(pEdict); - if (!Auth) { - Auth = ""; - } - - if (Auth[0u] != '\0') { - auto Player = this->GetPlayer(Auth); - - if (Player != nullptr) { - PlayerJson = {{"EntityId", Player->EntityId}, - {"Auth", Player->Auth}, - {"Name", Player->Name}, - {"Address", Player->Address}, - {"UserId", Player->UserId}, - {"Team", Player->Team}, - {"Frags", Player->Frags}, - {"Deaths", Player->Deaths}, - {"Ping", Player->Ping}, - {"GameTime", Player->GameTime}, - {"ConnectTime", Player->ConnectTime}}; - } + // Reject null, invalid, or partially-destroyed edicts + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return PlayerJson; + + // Copy the auth string immediately — pfnGetPlayerAuthId returns an internal + // engine pointer that can be invalidated by subsequent engine calls + const char* rawAuth = g_engfuncs.pfnGetPlayerAuthId(pEdict); + if (!rawAuth || rawAuth[0u] == '\0') + return PlayerJson; + + std::string AuthStr(rawAuth); + + try { + auto Player = this->GetPlayer(AuthStr); + + if (Player != nullptr) { + PlayerJson = {{"EntityId", Player->EntityId}, + {"Auth", Player->Auth}, + {"Name", Player->Name}, + {"Address", Player->Address}, + {"UserId", Player->UserId}, + {"Team", Player->Team}, + {"Frags", Player->Frags}, + {"Deaths", Player->Deaths}, + {"Ping", Player->Ping}, + {"GameTime", Player->GameTime}, + {"ConnectTime", Player->ConnectTime}}; } + } catch (const std::exception &e) { + LOG_CONSOLE(PLID, "[%s] Exception building player JSON: %s", __func__, e.what()); + PlayerJson = nlohmann::ordered_json{}; } return PlayerJson; diff --git a/LogApi/Makefile b/LogApi/Makefile index 41feb22..19c6e10 100644 --- a/LogApi/Makefile +++ b/LogApi/Makefile @@ -17,8 +17,8 @@ BUILD_TARGET=linux32 BUILD_EXTENSION=so BUILD_OBJECTS=$(BUILD_SOURCES:%.cpp=$(BUILD_DIR)/%.o) BUILD_COMPILER=c++ -BUILD_LINKER=-static-libgcc -static-libstdc++ -lcurl -lssl -lcrypto -ldl -lm -lz -BUILD_CCFLAGS=$(BUILD_CFLAGS) -DNDEBUG -Dlinux -D__linux__ -D__BUILD__ -D_GLIBCXX_USE_CXX11_ABI=0 -DHTTP_ONLY +BUILD_LINKER=-lcurl -lssl -lcrypto -ldl -lm -lz +BUILD_CCFLAGS=$(BUILD_CFLAGS) -DNDEBUG -Dlinux -D__linux__ -D__BUILD__ -D_GLIBCXX_USE_CXX11_ABI=0 -DCURL_STATICLIB -DHTTP_ONLY # # Include directory SRCDIR=. diff --git a/LogApi/MetaMod.cpp b/LogApi/MetaMod.cpp index f6584ce..8af8c47 100644 --- a/LogApi/MetaMod.cpp +++ b/LogApi/MetaMod.cpp @@ -30,6 +30,10 @@ C_DLLEXPORT void WINAPI GiveFnptrsToDll(enginefuncs_t *pengfuncsFromEngine, C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) { + if (gpMetaUtilFuncs) { + gpMetaUtilFuncs->pfnLogConsole(PLID, "[%s] Meta_Attach Start", Plugin_info.logtag); + } + gpMetaGlobals = pMGlobals; gpGamedllFuncs = pGamedllFuncs; @@ -54,6 +58,10 @@ C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, ReGameDLL_Init(); + if (gpMetaUtilFuncs) { + gpMetaUtilFuncs->pfnLogConsole(PLID, "[%s] Meta_Attach Finished", Plugin_info.logtag); + } + return TRUE; } @@ -71,6 +79,10 @@ C_DLLEXPORT int Meta_Query(char *interfaceVersion, plugin_info_t **pPlugInfo, gpMetaUtilFuncs = pMetaUtilFuncs; + if (gpMetaUtilFuncs) { + gpMetaUtilFuncs->pfnLogConsole(PLID, "[%s] Meta_Query Finished", Plugin_info.logtag); + } + return TRUE; } #pragma endregion diff --git a/LogApi/precompiled.h b/LogApi/precompiled.h index 65b9d54..c5f6d9b 100644 --- a/LogApi/precompiled.h +++ b/LogApi/precompiled.h @@ -38,9 +38,9 @@ #include // cURL sources -//#ifndef CURL_STATICLIB -//#define CURL_STATICLIB -//#endif +#ifndef CURL_STATICLIB +#define CURL_STATICLIB +#endif // cURL sources #include From 2515c0e05b102b29acdd11a752fcd88d8ad6a418 Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Fri, 15 May 2026 16:10:58 -0300 Subject: [PATCH 10/13] remove console logs --- LogApi/LogApi.cpp | 6 +----- LogApi/LogCommand.cpp | 2 -- LogApi/MetaMod.cpp | 24 ------------------------ LogApi/ReAPI.cpp | 4 ---- LogApi/ReGameDLL.cpp | 4 ---- 5 files changed, 1 insertion(+), 39 deletions(-) diff --git a/LogApi/LogApi.cpp b/LogApi/LogApi.cpp index d21449d..545546f 100644 --- a/LogApi/LogApi.cpp +++ b/LogApi/LogApi.cpp @@ -57,8 +57,6 @@ void CLogApi::ServerActivate() { // Close file pointer fclose(fp); - - LOG_CONSOLE(PLID, "[%s] Loaded %d events from %s", __func__, this->m_Events.size(), LOG_API_FILE_EVENTS); } else { // Failed on error LOG_CONSOLE(PLID, "[%s] Failed to open file: %s", __func__, @@ -69,7 +67,7 @@ void CLogApi::ServerActivate() { LOG_CONSOLE(PLID, "[%s] Exception: %s", __func__, e.what()); } - LOG_CONSOLE(PLID, "[%s] Finished", __func__); + } } // On server deactivate @@ -336,8 +334,6 @@ void CLogApi::ShowMenu(int EventIndex, const nlohmann::ordered_json& Data) { } catch (const nlohmann::ordered_json::exception &e) { LOG_CONSOLE(PLID, "[%s] %s", __func__, e.what()); } - } else { - LOG_CONSOLE(PLID, "[%s] Menu is empty", __func__); } } } diff --git a/LogApi/LogCommand.cpp b/LogApi/LogCommand.cpp index 7c40329..fe58aeb 100644 --- a/LogApi/LogCommand.cpp +++ b/LogApi/LogCommand.cpp @@ -187,8 +187,6 @@ void CLogCommand::OpenMotd() { // Send Server Information void CLogCommand::ServerInfo() { gLogEvent.ServerInfo(); - - LOG_CONSOLE(PLID, "[%s] Server info sent to webserver.", Plugin_info.logtag); } // Get Hudmessage Parameters diff --git a/LogApi/MetaMod.cpp b/LogApi/MetaMod.cpp index 8af8c47..46a401d 100644 --- a/LogApi/MetaMod.cpp +++ b/LogApi/MetaMod.cpp @@ -30,10 +30,6 @@ C_DLLEXPORT void WINAPI GiveFnptrsToDll(enginefuncs_t *pengfuncsFromEngine, C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) { - if (gpMetaUtilFuncs) { - gpMetaUtilFuncs->pfnLogConsole(PLID, "[%s] Meta_Attach Start", Plugin_info.logtag); - } - gpMetaGlobals = pMGlobals; gpGamedllFuncs = pGamedllFuncs; @@ -58,10 +54,6 @@ C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, ReGameDLL_Init(); - if (gpMetaUtilFuncs) { - gpMetaUtilFuncs->pfnLogConsole(PLID, "[%s] Meta_Attach Finished", Plugin_info.logtag); - } - return TRUE; } @@ -79,10 +71,6 @@ C_DLLEXPORT int Meta_Query(char *interfaceVersion, plugin_info_t **pPlugInfo, gpMetaUtilFuncs = pMetaUtilFuncs; - if (gpMetaUtilFuncs) { - gpMetaUtilFuncs->pfnLogConsole(PLID, "[%s] Meta_Query Finished", Plugin_info.logtag); - } - return TRUE; } #pragma endregion @@ -133,28 +121,16 @@ C_DLLEXPORT int GetEntityAPI2_Post(DLL_FUNCTIONS *pFunctionTable, void DLL_POST_ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) { - LOG_CONSOLE(PLID, "[%s] Start", __func__); - gLogCvar.ServerActivate(); - LOG_CONSOLE(PLID, "[%s] LogCvar initialized", __func__); - gLogApi.ServerActivate(); - LOG_CONSOLE(PLID, "[%s] LogApi initialized", __func__); - gLogCurl.ServerActivate(); - LOG_CONSOLE(PLID, "[%s] LogCurl initialized", __func__); - gLogCommand.ServerActivate(); - LOG_CONSOLE(PLID, "[%s] LogCommand initialized", __func__); - gLogEvent.ServerActivate(pEdictList, edictCount, clientMax); - LOG_CONSOLE(PLID, "[%s] LogEvent initialized", __func__); - RETURN_META(MRES_IGNORED); } diff --git a/LogApi/ReAPI.cpp b/LogApi/ReAPI.cpp index 831a485..0f34c62 100644 --- a/LogApi/ReAPI.cpp +++ b/LogApi/ReAPI.cpp @@ -44,10 +44,6 @@ bool ReAPI_Init() if (g_RehldsHookchains) { g_RehldsHookchains->SV_DropClient()->registerHook(ReAPI_SV_DropClient); - } - - gpMetaUtilFuncs->pfnLogConsole(PLID, "[%s] ReHLDS API Loaded: %d.%d", Plugin_info.logtag, REHLDS_API_VERSION_MAJOR, REHLDS_API_VERSION_MINOR); - return true; } } diff --git a/LogApi/ReGameDLL.cpp b/LogApi/ReGameDLL.cpp index cc1a7b8..5416aa6 100644 --- a/LogApi/ReGameDLL.cpp +++ b/LogApi/ReGameDLL.cpp @@ -42,10 +42,6 @@ bool ReGameDLL_Init() g_ReGameHookchains->InstallGameRules()->registerHook(ReGameDLL_InstallGameRules); g_ReGameHookchains->InternalCommand()->registerHook(ReGameDLL_InternalCommand); - } - - gpMetaUtilFuncs->pfnLogConsole(PLID, "[%s] ReGameDLL API Loaded: %d.%d", Plugin_info.logtag, REGAMEDLL_API_VERSION_MAJOR, REGAMEDLL_API_VERSION_MINOR); - return true; } } From cc5ac564f10d28ec6e30282d1818e6b35699fb0a Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Fri, 15 May 2026 16:15:48 -0300 Subject: [PATCH 11/13] feat: implement ReHLDS and ReGameDLL API integration with hookchains --- LogApi/ReAPI.cpp | 2 ++ LogApi/ReGameDLL.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/LogApi/ReAPI.cpp b/LogApi/ReAPI.cpp index 0f34c62..e7c48fd 100644 --- a/LogApi/ReAPI.cpp +++ b/LogApi/ReAPI.cpp @@ -44,6 +44,8 @@ bool ReAPI_Init() if (g_RehldsHookchains) { g_RehldsHookchains->SV_DropClient()->registerHook(ReAPI_SV_DropClient); + } + return true; } } diff --git a/LogApi/ReGameDLL.cpp b/LogApi/ReGameDLL.cpp index 5416aa6..1926cd6 100644 --- a/LogApi/ReGameDLL.cpp +++ b/LogApi/ReGameDLL.cpp @@ -42,6 +42,8 @@ bool ReGameDLL_Init() g_ReGameHookchains->InstallGameRules()->registerHook(ReGameDLL_InstallGameRules); g_ReGameHookchains->InternalCommand()->registerHook(ReGameDLL_InternalCommand); + } + return true; } } From e7006d2c225e34bc218b87d8710f390442302c8e Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Fri, 15 May 2026 16:21:22 -0300 Subject: [PATCH 12/13] feat: implement LogApi class for event management and server communication --- LogApi/LogApi.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/LogApi/LogApi.cpp b/LogApi/LogApi.cpp index 545546f..9503e14 100644 --- a/LogApi/LogApi.cpp +++ b/LogApi/LogApi.cpp @@ -67,7 +67,6 @@ void CLogApi::ServerActivate() { LOG_CONSOLE(PLID, "[%s] Exception: %s", __func__, e.what()); } - } } // On server deactivate From d85af10d31f23fbcd375741484443728dbca2320 Mon Sep 17 00:00:00 2001 From: Celio Lozatto Date: Fri, 15 May 2026 16:52:00 -0300 Subject: [PATCH 13/13] feat: implement ReHLDS and ReGameDLL API integration with hookchains --- LogApi/ReAPI.cpp | 2 ++ LogApi/ReGameDLL.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/LogApi/ReAPI.cpp b/LogApi/ReAPI.cpp index e7c48fd..831a485 100644 --- a/LogApi/ReAPI.cpp +++ b/LogApi/ReAPI.cpp @@ -46,6 +46,8 @@ bool ReAPI_Init() g_RehldsHookchains->SV_DropClient()->registerHook(ReAPI_SV_DropClient); } + gpMetaUtilFuncs->pfnLogConsole(PLID, "[%s] ReHLDS API Loaded: %d.%d", Plugin_info.logtag, REHLDS_API_VERSION_MAJOR, REHLDS_API_VERSION_MINOR); + return true; } } diff --git a/LogApi/ReGameDLL.cpp b/LogApi/ReGameDLL.cpp index 1926cd6..cc1a7b8 100644 --- a/LogApi/ReGameDLL.cpp +++ b/LogApi/ReGameDLL.cpp @@ -44,6 +44,8 @@ bool ReGameDLL_Init() g_ReGameHookchains->InternalCommand()->registerHook(ReGameDLL_InternalCommand); } + gpMetaUtilFuncs->pfnLogConsole(PLID, "[%s] ReGameDLL API Loaded: %d.%d", Plugin_info.logtag, REGAMEDLL_API_VERSION_MAJOR, REGAMEDLL_API_VERSION_MINOR); + return true; } }