r5sdk/r5dev/tier0/dbg.cpp
Kawe Mazidjatari 75ccebb4b6 Squirrel system restructure
Properly decouple squirrel and game code. This makes it easier to reverse engineer more of this squirrel system, and to compile them as individual libraries later on when moving to CMake to significantly decrease compile times.
2023-05-06 16:23:56 +02:00

491 lines
14 KiB
C++

//==== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. =====//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#include "core/stdafx.h"
#include "core/logdef.h"
#include "tier0/dbg.h"
#include "tier0/platform.h"
#ifndef NETCONSOLE
#include "tier0/threadtools.h"
#include "tier0/commandline.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
#if defined( _X360 )
#include "xbox/xbox_console.h"
#endif
#include "vscript/languages/squirrel_re/include/sqstdaux.h"
#endif // !NETCONSOLE
std::mutex g_LogMutex;
//-----------------------------------------------------------------------------
// True if -hushasserts was passed on command line.
//-----------------------------------------------------------------------------
bool HushAsserts()
{
#if defined (DBGFLAG_ASSERT) && !defined (NETCONSOLE)
static bool s_bHushAsserts = !!CommandLine()->FindParm("-hushasserts");
return s_bHushAsserts;
#else
return true;
#endif
}
//-----------------------------------------------------------------------------
// Templates to assist in validating pointers:
//-----------------------------------------------------------------------------
/*PLATFORM_INTERFACE*/ void _AssertValidReadPtr(void* ptr, int count/* = 1*/)
{
#if defined( _WIN32 ) && !defined( _X360 )
Assert(!IsBadReadPtr(ptr, count));
#else
Assert(!count || ptr);
#endif
#ifdef NDEBUG
NOTE_UNUSED(ptr);
NOTE_UNUSED(count);
#endif // NDEBUG
}
/*PLATFORM_INTERFACE*/ void _AssertValidWritePtr(void* ptr, int count/* = 1*/)
{
#if defined( _WIN32 ) && !defined( _X360 )
Assert(!IsBadWritePtr(ptr, count));
#else
Assert(!count || ptr);
#endif
#ifdef NDEBUG
NOTE_UNUSED(ptr);
NOTE_UNUSED(count);
#endif // NDEBUG
}
/*PLATFORM_INTERFACE*/ void _AssertValidReadWritePtr(void* ptr, int count/* = 1*/)
{
#if defined( _WIN32 ) && !defined( _X360 )
Assert(!(IsBadWritePtr(ptr, count) || IsBadReadPtr(ptr, count)));
#else
Assert(!count || ptr);
#endif
#ifdef NDEBUG
NOTE_UNUSED(ptr);
NOTE_UNUSED(count);
#endif // NDEBUG
}
/*PLATFORM_INTERFACE*/ void _AssertValidStringPtr(const TCHAR* ptr, int maxchar/* = 0xFFFFFF */)
{
#if defined( _WIN32 ) && !defined( _X360 )
#ifdef TCHAR_IS_CHAR
Assert(!IsBadStringPtr(ptr, maxchar));
#else
Assert(!IsBadStringPtrW(ptr, maxchar));
#endif
#else
Assert(ptr);
#endif
#ifdef NDEBUG
NOTE_UNUSED(ptr);
NOTE_UNUSED(maxchar);
#endif // NDEBUG
}
/*PLATFORM_INTERFACE*/ void AssertValidWStringPtr(const wchar_t* ptr, int maxchar/* = 0xFFFFFF */)
{
#if defined( _WIN32 ) && !defined( _X360 )
Assert(!IsBadStringPtrW(ptr, maxchar));
#else
Assert(ptr);
#endif
#ifdef NDEBUG
NOTE_UNUSED(ptr);
NOTE_UNUSED(maxchar);
#endif // NDEBUG
}
#if !defined (DEDICATED) && !defined (NETCONSOLE)
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 && !NETCONSOLE
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:
contextName = s_DllAnsiColor[index];
break;
case eDLL_T::SYSTEM_WARNING:
case eDLL_T::SYSTEM_ERROR:
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 CoreMsgV(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 = g_bSpdLog_PostInit ? 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 (NETCONSOLE)
ImVec4 overlayColor = GetColorForContext(logType, context);
eDLL_T overlayContext = context;
#endif // !DEDICATED && !NETCONSOLE
#if !defined (NETCONSOLE)
bool bSquirrel = false;
bool bWarning = false;
bool bError = false;
#else
NOTE_UNUSED(pszLogger);
#endif // !NETCONSOLE
//-------------------------------------------------------------------------
// Setup logger and context
//-------------------------------------------------------------------------
switch (logType)
{
case LogType_t::LOG_WARNING:
#if !defined (DEDICATED) && !defined (NETCONSOLE)
overlayContext = eDLL_T::SYSTEM_WARNING;
#endif // !DEDICATED && !NETCONSOLE
if (bUseColor)
{
message.append(g_svYellowF);
}
break;
case LogType_t::LOG_ERROR:
#if !defined (DEDICATED) && !defined (NETCONSOLE)
overlayContext = eDLL_T::SYSTEM_ERROR;
#endif // !DEDICATED && !NETCONSOLE
if (bUseColor)
{
message.append(g_svRedF);
}
break;
#ifndef NETCONSOLE
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 // !NETCONSOLE
default:
break;
}
//-------------------------------------------------------------------------
// Format actual input
//-------------------------------------------------------------------------
va_list argsCopy;
va_copy(argsCopy, args);
const string formatted = FormatV(pszFormat, argsCopy);
va_end(argsCopy);
#ifndef NETCONSOLE
//-------------------------------------------------------------------------
// 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 // !NETCONSOLE
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, "");
}
}
#ifndef NETCONSOLE
// 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);
if (g_bSpdLog_PostInit)
{
g_pConsole->AddLog(ConLog_t(g_LogStream.str(), overlayColor));
if (logLevel >= LogLevel_t::LEVEL_NOTIFY) // Draw to mini console.
{
g_pOverlay->AddLog(overlayContext, g_LogStream.str());
}
}
#endif // !DEDICATED
}
#ifndef DEDICATED
g_LogStream.str(string());
g_LogStream.clear();
#endif // !DEDICATED
#endif // !NETCONSOLE
if (exitCode) // Terminate the process if an exit code was passed.
{
if (MessageBoxA(NULL, Format("%s- %s", pszUpTime, message.c_str()).c_str(),
"SDK Error", MB_ICONERROR | MB_OK))
{
TerminateProcess(GetCurrentProcess(), exitCode);
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Show logs to all console interfaces
// Input : logType -
// logLevel -
// context -
// exitCode -
// *pszLogger -
// *pszFormat -
// ... -
//-----------------------------------------------------------------------------
void CoreMsg(LogType_t logType, LogLevel_t logLevel, eDLL_T context,
const UINT exitCode, const char* pszLogger, const char* pszFormat, ...)
{
va_list args;
va_start(args, pszFormat);
CoreMsgV(logType, logLevel, context, pszLogger, pszFormat, args, exitCode);
va_end(args);
}
//-----------------------------------------------------------------------------
// Purpose: Prints general debugging messages
// Input : context -
// *fmt - ... -
//-----------------------------------------------------------------------------
void DevMsg(eDLL_T context, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
CoreMsgV(LogType_t::LOG_INFO, LogLevel_t::LEVEL_NOTIFY, context, "sdk", fmt, args);
va_end(args);
}
//-----------------------------------------------------------------------------
// Purpose: Prints logs from remote console
// Input : context -
// *fmt - ... -
//-----------------------------------------------------------------------------
#ifndef DEDICATED
void NetMsg(LogType_t logType, eDLL_T context, const char* uptime, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
CoreMsgV(logType, LogLevel_t::LEVEL_NOTIFY, context, "netconsole", fmt, args, NO_ERROR, uptime);
va_end(args);
}
#endif // !DEDICATED
//-----------------------------------------------------------------------------
// Purpose: Print engine and SDK warnings
// Input : context -
// *fmt - ... -
//-----------------------------------------------------------------------------
void Warning(eDLL_T context, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
CoreMsgV(LogType_t::LOG_WARNING, LogLevel_t::LEVEL_NOTIFY, context, "sdk(warning)", fmt, args);
va_end(args);
}
//-----------------------------------------------------------------------------
// Purpose: Print engine and SDK errors
// Input : context -
// code -
// *fmt - ... -
//-----------------------------------------------------------------------------
void Error(eDLL_T context, const UINT code, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
CoreMsgV(LogType_t::LOG_ERROR, LogLevel_t::LEVEL_NOTIFY, context, "sdk(error)", fmt, args, code);
va_end(args);
}