mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
This code originated from an imgui code editor, where it would make sense to have per-character colors. But for the console, we only ever do 1 color per line. Just store the string and one color instance, this also allows us to just take the string instead of manually reconstructing it which is very slow. The code now also only stores 32bits for color (per line) instead of 128 bits (ImVec4) as we only ever need to convert it once. As a result, a bunch of rendering code could be dropped, and the pass filter is now also very fast as we don't need to reconstruct the string to check for the filter, we just pass a char* in which doesn't cost any perf.
330 lines
9.1 KiB
C++
330 lines
9.1 KiB
C++
#include "tier0/utility.h"
|
|
#include "init.h"
|
|
#include "logdef.h"
|
|
#include "logger.h"
|
|
#ifndef DEDICATED
|
|
#include "vgui/vgui_debugpanel.h"
|
|
#include "gameui/IConsole.h"
|
|
#endif // !DEDICATED
|
|
#ifndef CLIENT_DLL
|
|
#include "engine/server/sv_rcon.h"
|
|
#endif // !CLIENT_DLL
|
|
#ifndef _TOOLS
|
|
#include "vscript/languages/squirrel_re/include/sqstdaux.h"
|
|
#endif // !_TOOLS
|
|
static const std::regex s_AnsiRowRegex("\\\033\\[.*?m");
|
|
std::mutex g_LogMutex;
|
|
|
|
#if !defined (DEDICATED) && !defined (_TOOLS)
|
|
ImVec4 CheckForWarnings(LogType_t type, eDLL_T context, const ImVec4& defaultCol)
|
|
{
|
|
ImVec4 color = defaultCol;
|
|
if (type == LogType_t::LOG_WARNING || context == eDLL_T::SYSTEM_WARNING)
|
|
{
|
|
color = ImVec4(1.00f, 1.00f, 0.00f, 0.80f);
|
|
}
|
|
else if (type == LogType_t::LOG_ERROR || context == eDLL_T::SYSTEM_ERROR)
|
|
{
|
|
color = ImVec4(1.00f, 0.00f, 0.00f, 0.80f);
|
|
}
|
|
|
|
return color;
|
|
}
|
|
|
|
ImVec4 GetColorForContext(LogType_t type, eDLL_T context)
|
|
{
|
|
switch (context)
|
|
{
|
|
case eDLL_T::SCRIPT_SERVER:
|
|
return CheckForWarnings(type, context, ImVec4(0.59f, 0.58f, 0.73f, 1.00f));
|
|
case eDLL_T::SCRIPT_CLIENT:
|
|
return CheckForWarnings(type, context, ImVec4(0.59f, 0.58f, 0.63f, 1.00f));
|
|
case eDLL_T::SCRIPT_UI:
|
|
return CheckForWarnings(type, context, ImVec4(0.59f, 0.48f, 0.53f, 1.00f));
|
|
case eDLL_T::SERVER:
|
|
return CheckForWarnings(type, context, ImVec4(0.23f, 0.47f, 0.85f, 1.00f));
|
|
case eDLL_T::CLIENT:
|
|
return CheckForWarnings(type, context, ImVec4(0.46f, 0.46f, 0.46f, 1.00f));
|
|
case eDLL_T::UI:
|
|
return CheckForWarnings(type, context, ImVec4(0.59f, 0.35f, 0.46f, 1.00f));
|
|
case eDLL_T::ENGINE:
|
|
return CheckForWarnings(type, context, ImVec4(0.70f, 0.70f, 0.70f, 1.00f));
|
|
case eDLL_T::FS:
|
|
return CheckForWarnings(type, context, ImVec4(0.32f, 0.64f, 0.72f, 1.00f));
|
|
case eDLL_T::RTECH:
|
|
return CheckForWarnings(type, context, ImVec4(0.36f, 0.70f, 0.35f, 1.00f));
|
|
case eDLL_T::MS:
|
|
return CheckForWarnings(type, context, ImVec4(0.75f, 0.30f, 0.68f, 1.00f));
|
|
case eDLL_T::AUDIO:
|
|
return CheckForWarnings(type, context, ImVec4(0.93f, 0.42f, 0.12f, 1.00f));
|
|
case eDLL_T::VIDEO:
|
|
return CheckForWarnings(type, context, ImVec4(0.73f, 0.00f, 0.92f, 1.00f));
|
|
case eDLL_T::NETCON:
|
|
return CheckForWarnings(type, context, ImVec4(0.81f, 0.81f, 0.81f, 1.00f));
|
|
case eDLL_T::COMMON:
|
|
return CheckForWarnings(type, context, ImVec4(1.00f, 0.80f, 0.60f, 1.00f));
|
|
default:
|
|
return CheckForWarnings(type, context, ImVec4(0.81f, 0.81f, 0.81f, 1.00f));
|
|
}
|
|
}
|
|
#endif // !DEDICATED && !_TOOLS
|
|
|
|
const char* GetContextNameByIndex(eDLL_T context, const bool ansiColor = false)
|
|
{
|
|
int index = static_cast<int>(context);
|
|
const char* contextName = s_DefaultAnsiColor;
|
|
|
|
switch (context)
|
|
{
|
|
case eDLL_T::SCRIPT_SERVER:
|
|
contextName = s_ScriptAnsiColor[0];
|
|
break;
|
|
case eDLL_T::SCRIPT_CLIENT:
|
|
contextName = s_ScriptAnsiColor[1];
|
|
break;
|
|
case eDLL_T::SCRIPT_UI:
|
|
contextName = s_ScriptAnsiColor[2];
|
|
break;
|
|
case eDLL_T::SERVER:
|
|
case eDLL_T::CLIENT:
|
|
case eDLL_T::UI:
|
|
case eDLL_T::ENGINE:
|
|
case eDLL_T::FS:
|
|
case eDLL_T::RTECH:
|
|
case eDLL_T::MS:
|
|
case eDLL_T::AUDIO:
|
|
case eDLL_T::VIDEO:
|
|
case eDLL_T::NETCON:
|
|
case eDLL_T::COMMON:
|
|
case eDLL_T::SYSTEM_WARNING:
|
|
case eDLL_T::SYSTEM_ERROR:
|
|
contextName = s_DllAnsiColor[index];
|
|
break;
|
|
case eDLL_T::NONE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!ansiColor)
|
|
{
|
|
// Shift # chars to skip ANSI row.
|
|
contextName += sizeof(s_DefaultAnsiColor) - 1;
|
|
}
|
|
|
|
return contextName;
|
|
}
|
|
|
|
bool LoggedFromClient(eDLL_T context)
|
|
{
|
|
#ifndef DEDICATED
|
|
return (context == eDLL_T::CLIENT || context == eDLL_T::SCRIPT_CLIENT
|
|
|| context == eDLL_T::UI || context == eDLL_T::SCRIPT_UI
|
|
|| context == eDLL_T::NETCON);
|
|
#else
|
|
NOTE_UNUSED(context);
|
|
return false;
|
|
#endif // !DEDICATED
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Show logs to all console interfaces (va_list version)
|
|
// Input : logType -
|
|
// logLevel -
|
|
// context -
|
|
// *pszLogger -
|
|
// *pszFormat -
|
|
// args -
|
|
// exitCode -
|
|
// *pszUptimeOverride -
|
|
//-----------------------------------------------------------------------------
|
|
void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context,
|
|
const char* pszLogger, const char* pszFormat, va_list args,
|
|
const UINT exitCode /*= NO_ERROR*/, const char* pszUptimeOverride /*= nullptr*/)
|
|
{
|
|
const char* pszUpTime = pszUptimeOverride ? pszUptimeOverride : Plat_GetProcessUpTime();
|
|
string message(pszUpTime);
|
|
|
|
const bool bToConsole = (logLevel >= LogLevel_t::LEVEL_CONSOLE);
|
|
const bool bUseColor = (bToConsole && g_bSpdLog_UseAnsiClr);
|
|
|
|
const char* pszContext = GetContextNameByIndex(context, bUseColor);
|
|
message.append(pszContext);
|
|
|
|
#if !defined (DEDICATED) && !defined (_TOOLS)
|
|
ImVec4 overlayColor = GetColorForContext(logType, context);
|
|
eDLL_T overlayContext = context;
|
|
#endif // !DEDICATED && !_TOOLS
|
|
|
|
#if !defined (_TOOLS)
|
|
bool bSquirrel = false;
|
|
bool bWarning = false;
|
|
bool bError = false;
|
|
#else
|
|
NOTE_UNUSED(pszLogger);
|
|
#endif // !_TOOLS
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Setup logger and context
|
|
//-------------------------------------------------------------------------
|
|
switch (logType)
|
|
{
|
|
case LogType_t::LOG_WARNING:
|
|
#if !defined (DEDICATED) && !defined (_TOOLS)
|
|
overlayContext = eDLL_T::SYSTEM_WARNING;
|
|
#endif // !DEDICATED && !_TOOLS
|
|
if (bUseColor)
|
|
{
|
|
message.append(g_svYellowF);
|
|
}
|
|
break;
|
|
case LogType_t::LOG_ERROR:
|
|
#if !defined (DEDICATED) && !defined (_TOOLS)
|
|
overlayContext = eDLL_T::SYSTEM_ERROR;
|
|
#endif // !DEDICATED && !_TOOLS
|
|
if (bUseColor)
|
|
{
|
|
message.append(g_svRedF);
|
|
}
|
|
break;
|
|
#ifndef _TOOLS
|
|
case LogType_t::SQ_INFO:
|
|
bSquirrel = true;
|
|
break;
|
|
case LogType_t::SQ_WARNING:
|
|
#ifndef DEDICATED
|
|
overlayContext = eDLL_T::SYSTEM_WARNING;
|
|
overlayColor = ImVec4(1.00f, 1.00f, 0.00f, 0.80f);
|
|
#endif // !DEDICATED
|
|
bSquirrel = true;
|
|
bWarning = true;
|
|
break;
|
|
#endif // !_TOOLS
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Format actual input
|
|
//-------------------------------------------------------------------------
|
|
va_list argsCopy;
|
|
va_copy(argsCopy, args);
|
|
const string formatted = FormatV(pszFormat, argsCopy);
|
|
va_end(argsCopy);
|
|
|
|
#ifndef _TOOLS
|
|
//-------------------------------------------------------------------------
|
|
// Colorize script warnings and errors
|
|
//-------------------------------------------------------------------------
|
|
if (bToConsole && bSquirrel)
|
|
{
|
|
if (bWarning && g_bSQAuxError)
|
|
{
|
|
if (formatted.find("SCRIPT ERROR:") != string::npos ||
|
|
formatted.find(" -> ") != string::npos)
|
|
{
|
|
bError = true;
|
|
}
|
|
}
|
|
else if (g_bSQAuxBadLogic)
|
|
{
|
|
if (formatted.find("There was a problem processing game logic.") != string::npos)
|
|
{
|
|
bError = true;
|
|
g_bSQAuxBadLogic = false;
|
|
}
|
|
}
|
|
|
|
// Append warning/error color before appending the formatted text,
|
|
// so that this gets marked as such while preserving context colors.
|
|
if (bError)
|
|
{
|
|
#ifndef DEDICATED
|
|
overlayContext = eDLL_T::SYSTEM_ERROR;
|
|
overlayColor = ImVec4(1.00f, 0.00f, 0.00f, 0.80f);
|
|
#endif // !DEDICATED
|
|
|
|
if (bUseColor)
|
|
{
|
|
message.append(g_svRedF);
|
|
}
|
|
}
|
|
else if (bUseColor && bWarning)
|
|
{
|
|
message.append(g_svYellowF);
|
|
}
|
|
}
|
|
#endif // !_TOOLS
|
|
message.append(formatted);
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Emit to all interfaces
|
|
//-------------------------------------------------------------------------
|
|
std::lock_guard<std::mutex> lock(g_LogMutex);
|
|
if (bToConsole)
|
|
{
|
|
g_TermLogger->debug(message);
|
|
|
|
if (bUseColor)
|
|
{
|
|
// Remove ANSI rows before emitting to file or over wire.
|
|
message = std::regex_replace(message, s_AnsiRowRegex, "");
|
|
}
|
|
}
|
|
|
|
// If a debugger is attached, emit the text there too
|
|
if (Plat_IsInDebugSession())
|
|
Plat_DebugString(message.c_str());
|
|
|
|
#ifndef _TOOLS
|
|
// Output is always logged to the file.
|
|
std::shared_ptr<spdlog::logger> ntlogger = spdlog::get(pszLogger); // <-- Obtain by 'pszLogger'.
|
|
assert(ntlogger.get() != nullptr);
|
|
ntlogger->debug(message);
|
|
|
|
if (bToConsole)
|
|
{
|
|
#ifndef CLIENT_DLL
|
|
if (!LoggedFromClient(context) && RCONServer()->ShouldSend(sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG))
|
|
{
|
|
RCONServer()->SendEncode(formatted.c_str(), pszUpTime, sv_rcon::response_t::SERVERDATA_RESPONSE_CONSOLE_LOG,
|
|
int(context), int(logType));
|
|
}
|
|
#endif // !CLIENT_DLL
|
|
#ifndef DEDICATED
|
|
g_ImGuiLogger->debug(message);
|
|
|
|
const string logStreamBuf = g_LogStream.str();
|
|
g_Console.AddLog(logStreamBuf.c_str(), ImGui::ColorConvertFloat4ToU32(overlayColor));
|
|
|
|
// We can only log to the in-game overlay console when the SDK has
|
|
// been fully initialized, due to the use of ConVar's.
|
|
if (g_bSdkInitialized && logLevel >= LogLevel_t::LEVEL_NOTIFY)
|
|
{
|
|
// Draw to mini console.
|
|
g_TextOverlay.AddLog(overlayContext, logStreamBuf.c_str());
|
|
}
|
|
#endif // !DEDICATED
|
|
}
|
|
|
|
#ifndef DEDICATED
|
|
g_LogStream.str(string());
|
|
g_LogStream.clear();
|
|
#endif // !DEDICATED
|
|
|
|
#else
|
|
if (g_SuppementalToolsLogger)
|
|
{
|
|
g_SuppementalToolsLogger->debug(message);
|
|
}
|
|
#endif
|
|
|
|
if (exitCode) // Terminate the process if an exit code was passed.
|
|
{
|
|
if (MessageBoxA(NULL, Format("%s- %s", pszUpTime, formatted.c_str()).c_str(),
|
|
"SDK Error", MB_ICONERROR | MB_OK))
|
|
{
|
|
TerminateProcess(GetCurrentProcess(), exitCode);
|
|
}
|
|
}
|
|
}
|