diff --git a/LogApi/LogApi.cpp b/LogApi/LogApi.cpp index fe82bf2..9503e14 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); @@ -51,18 +53,20 @@ 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__, 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()); } + } // On server deactivate @@ -125,7 +129,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,12 +143,21 @@ 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) { - // POST to webserver - gLogCurl.PostJSON(gLogCvar.m_Address->string, - (long)gLogCvar.m_Timeout->value, - gLogCvar.m_Bearer->string, Event.dump(), - EventIndex); + if (gLogCvar.m_Bearer && gLogCvar.m_Bearer->string) { + 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()); + } } } } @@ -184,9 +197,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()); } } } @@ -202,7 +215,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); @@ -230,7 +243,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; @@ -280,7 +293,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()) { @@ -320,8 +333,6 @@ void CLogApi::ShowMenu(int EventIndex, 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__); } } } @@ -330,7 +341,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) { @@ -369,7 +380,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 { @@ -393,8 +404,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 @@ -411,7 +424,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 { @@ -431,8 +444,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 @@ -449,7 +464,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 { @@ -473,8 +488,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 @@ -497,25 +514,29 @@ 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); + auto MapName = STRING(gpGlobals->mapname); + ServerInfo["Map"] = MapName ? MapName : ""; // Set game ServerInfo["Game"] = "Counter-Strike"; // 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/LogApi.h b/LogApi/LogApi.h index e27089b..f8c7975 100644 --- a/LogApi/LogApi.h +++ b/LogApi/LogApi.h @@ -19,34 +19,34 @@ 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); // 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/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/LogCurl.cpp b/LogApi/LogCurl.cpp index e3bf192..381dcb2 100644 --- a/LogApi/LogCurl.cpp +++ b/LogApi/LogCurl.cpp @@ -4,11 +4,15 @@ CLogCurl gLogCurl; void CLogCurl::ServerActivate() { if (!this->m_MultiHandle) { - this->m_RequestIndex = 0; + this->m_RequestIndex = 1; 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(); } @@ -23,25 +27,25 @@ 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; - 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); @@ -99,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]); @@ -137,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/LogEvent.cpp b/LogApi/LogEvent.cpp index 0268be2..031e70c 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,228 +62,252 @@ 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); } } void CLogEvent::ClientConnect(edict_t* pEdict, const char* pszName, const char* pszAddress, char szRejectReason[128]) { - if (gLogApi.EventEnabled(__func__)) - { - this->m_Event.clear(); + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - this->m_Event["Event"] = __func__; + // Guard: entity must be fully valid with private data allocated + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + nlohmann::ordered_json Event; - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - } + Event["Event"] = __func__; - gLogApi.SendEvent(LogApi::Events::ClientConnect, this->m_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__)) - { - this->m_Event.clear(); + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - this->m_Event["Event"] = __func__; + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + nlohmann::ordered_json Event; - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - } - - gLogApi.SendEvent(LogApi::Events::ClientPutInServer, this->m_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__)) - { - this->m_Event.clear(); + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - this->m_Event["Event"] = __func__; + // During disconnect pvPrivateData may already be freed — guard both checks + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + nlohmann::ordered_json Event; - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + Event["Event"] = __func__; - this->m_Event["Crash"] = Crash; + Event["Server"] = gLogApi.GetServerInfo(); - this->m_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, this->m_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__)) - { - this->m_Event.clear(); + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - this->m_Event["Event"] = __func__; + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + nlohmann::ordered_json Event; - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - } + Event["Event"] = __func__; - gLogApi.SendEvent(LogApi::Events::ClientKill, this->m_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__)) - { - this->m_Event.clear(); + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - this->m_Event["Event"] = __func__; + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - this->m_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 : ""; - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + nlohmann::ordered_json Event; - this->m_Event["InfoBuffer"] = InfoBuffer; - } + Event["Event"] = __func__; - gLogApi.SendEvent(LogApi::Events::ClientUserInfoChanged, this->m_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__)) - { - this->m_Event.clear(); + 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') - { - this->m_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); - this->m_Event["Server"] = gLogApi.GetServerInfo(); + const char* rawArgs = g_engfuncs.pfnCmd_Args(); + std::string Args = (rawArgs && rawArgs[0u] != '\0') ? rawArgs : ""; - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + nlohmann::ordered_json Event; - this->m_Event["Command"] = Command; + Event["Event"] = __func__; - this->m_Event["Args"] = ""; + Event["Server"] = gLogApi.GetServerInfo(); - auto Args = g_engfuncs.pfnCmd_Args(); + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - if (Args) - { - if (Args[0u] != '\0') - { - this->m_Event["Args"] = Args; - } - } - } - } - } + Event["Command"] = Command; - gLogApi.SendEvent(LogApi::Events::ClientCommand, this->m_Event); - } + Event["Args"] = Args; + + if (Event.empty()) + return; + + gLogApi.SendEvent(LogApi::Events::ClientCommand, Event); } void CLogEvent::ClientSay(edict_t* pEdict) { - if (gLogApi.EventEnabled(__func__)) - { - this->m_Event.clear(); + 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; - this->m_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); - this->m_Event["Server"] = gLogApi.GetServerInfo(); + // Strip enclosing quotes the engine wraps around chat messages + Message.erase(std::remove(Message.begin(), Message.end(), '\"'), Message.end()); - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + if (Message.empty()) + return; - this->m_Event["Type"] = Type; + nlohmann::ordered_json Event; - this->m_Event["Message"] = Message; - } - } - } - } - } - } + Event["Event"] = __func__; - gLogApi.SendEvent(LogApi::Events::ClientSay, this->m_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()) - { - this->m_Event.clear(); + if (!gLogApi.EventEnabled(__func__)) + return; - if (!FNullEnt(pEdict)) - { - this->m_Event["Event"] = Callback; + if (Callback.empty()) + return; - this->m_Event["Server"] = gLogApi.GetServerInfo(); + if (FNullEnt(pEdict) || !pEdict->pvPrivateData) + return; - this->m_Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); + nlohmann::ordered_json Event; - this->m_Event["Item"]["Info"] = Item.Info; + Event["Event"] = Callback; - this->m_Event["Item"]["Text"] = Item.Text; + Event["Server"] = gLogApi.GetServerInfo(); - this->m_Event["Item"]["Disabled"] = Item.Disabled; + Event["Player"] = gLogPlayer.GetPlayerJson(pEdict); - this->m_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, this->m_Event); - } - } + if (Event.empty()) + return; + + 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/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/LogPlayer.cpp b/LogApi/LogPlayer.cpp index 97c86c6..f3ab47c 100644 --- a/LogApi/LogPlayer.cpp +++ b/LogApi/LogPlayer.cpp @@ -54,58 +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; + } + } - this->m_Players[Auth].Name = STRING(pEdict->v.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() { @@ -123,26 +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) { - 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/LogUtil.cpp b/LogApi/LogUtil.cpp index 1c15aef..a587f31 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); } } @@ -26,9 +27,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; @@ -202,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; + } + } } } } @@ -315,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 || @@ -376,7 +387,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; diff --git a/LogApi/Makefile b/LogApi/Makefile index 1d85c26..19c6e10 100644 --- a/LogApi/Makefile +++ b/LogApi/Makefile @@ -1,38 +1,51 @@ -# Makefile - LogApi (Linux 32-bit Build) +# Makefile settings MAKEFLAGS += --warn-undefined-variables --silent - +# +# Build directory BUILD_DIR=Release +# +# System based commands +PRE_CMD=mkdir -p $(BUILD_DIR) +POS_CMD=sh PostBuild.sh +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 -fno-strict-aliasing +BUILD_TARGET=linux32 +BUILD_EXTENSION=so BUILD_OBJECTS=$(BUILD_SOURCES:%.cpp=$(BUILD_DIR)/%.o) -BUILD_COMPILER=g++ - -# ✅ FLAGS DE COMPILAÇÃO (inclui -m32) -BUILD_CFLAGS=-std=c++11 -Wall -fcommon -m32 -O3 -fPIC -msse4.1 -march=i686 \ - -D_GLIBCXX_USE_CXX11_ABI=0 -fno-stack-protector -fno-strict-aliasing \ - -DCURL_STATICLIB -DHTTP_ONLY -DNDEBUG -Dlinux - -# ✅ FLAGS DE LINK (inclui -m32 e bibliotecas) -BUILD_LDFLAGS=-m32 -shared -static-libstdc++ -static-libgcc -BUILD_LIBS=-L/usr/local/curl32/lib -lcurl -lz -lpthread -lrt -ldl -lm - -# ✅ INCLUDES -INCLUDE=-I. -Iinclude/metamod -Iinclude/cssdk/common -Iinclude/cssdk/dlls \ - -Iinclude/cssdk/engine -Iinclude/cssdk/game_shared -Iinclude/cssdk/pm_shared \ - -Iinclude/cssdk/public -I/usr/local/curl32/include - -default: prebuild linux32 - +BUILD_COMPILER=c++ +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=. +METAMOD=include/metamod +SDK=include/cssdk +INCLUDE=-I$(SRCDIR) -I$(METAMOD) -I$(SDK)/common -I$(SDK)/dlls -I$(SDK)/engine -I$(SDK)/game_shared -I$(SDK)/pm_shared -I$(SDK)/public +# +# Defult: prebuild, linux32, posbuild +default: prebuild $(BUILD_TARGET) posbuild +# +# Linux: prebuild, linux32, posbuild +linux: $(BUILD_TARGET) +# +# Pre-build: Execute command prebuild: - mkdir -p $(BUILD_DIR) - -# ✅ REGRA DE COMPILAÇÃO (usa BUILD_CFLAGS corretamente) + $(PRE_CMD) +# +# Pos-build: Execute command +posbuild: + $(POS_CMD) +# +# Linux target build +$(BUILD_TARGET): $(BUILD_OBJECTS) + $(BUILD_COMPILER) $(INCLUDE) $(BUILD_CCFLAGS) $(BUILD_OBJECTS) $(BUILD_LINKER) -o$(BUILD_DIR)/$(BUILD_NAME).$(BUILD_EXTENSION) $(BUILD_DIR)/%.o: %.cpp - $(BUILD_COMPILER) $(INCLUDE) $(BUILD_CFLAGS) -o $@ -c $< - -# ✅ REGRA DE LINK (usa BUILD_LDFLAGS e BUILD_LIBS) -linux32: $(BUILD_OBJECTS) - $(BUILD_COMPILER) $(BUILD_LDFLAGS) -o $(BUILD_DIR)/$(BUILD_NAME).so $(BUILD_OBJECTS) $(BUILD_LIBS) - + $(BUILD_COMPILER) $(INCLUDE) $(BUILD_CCFLAGS) -o $@ -c $< +# +# Clean clean: - rm -rf $(BUILD_DIR) + $(CLEAN_CMD)