mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
Merge #5013
5013: Unify logging infrastructure between `IConsole` and `dbg_msg` r=def- a=heinrich5991 This makes the "black console window" less important on Windows (or anywhere else, for that matter), lets you see logs from other threads in the f1 console, and removes the distinction between `IConsole::Print` and `dbg_msg`. Co-authored-by: heinrich5991 <heinrich5991@gmail.com>
This commit is contained in:
commit
ca4b6222da
|
@ -1594,6 +1594,9 @@ set_src(BASE GLOB_RECURSE src/base
|
|||
hash_ctxt.h
|
||||
hash_libtomcrypt.cpp
|
||||
hash_openssl.cpp
|
||||
log.cpp
|
||||
log.h
|
||||
logger.h
|
||||
math.h
|
||||
system.cpp
|
||||
system.h
|
||||
|
|
418
src/base/log.cpp
Normal file
418
src/base/log.cpp
Normal file
|
@ -0,0 +1,418 @@
|
|||
#include "logger.h"
|
||||
|
||||
#include "color.h"
|
||||
#include "system.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdio>
|
||||
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0501 /* required for mingw to get getaddrinfo to work */
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
|
||||
std::atomic<ILogger *> global_logger = nullptr;
|
||||
thread_local ILogger *scope_logger = nullptr;
|
||||
thread_local bool in_logger = false;
|
||||
|
||||
void log_set_global_logger(ILogger *logger)
|
||||
{
|
||||
ILogger *null = nullptr;
|
||||
if(!global_logger.compare_exchange_strong(null, logger, std::memory_order_acq_rel))
|
||||
{
|
||||
dbg_assert(false, "global logger has already been set and can only be set once");
|
||||
}
|
||||
atexit(log_global_logger_finish);
|
||||
}
|
||||
|
||||
void log_global_logger_finish()
|
||||
{
|
||||
global_logger.load(std::memory_order_acquire)->GlobalFinish();
|
||||
}
|
||||
|
||||
void log_set_global_logger_default()
|
||||
{
|
||||
std::unique_ptr<ILogger> logger;
|
||||
#if defined(CONF_PLATFORM_ANDROID)
|
||||
logger = log_logger_android();
|
||||
#else
|
||||
logger = log_logger_stdout();
|
||||
#endif
|
||||
log_set_global_logger(logger.release());
|
||||
}
|
||||
|
||||
ILogger *log_get_scope_logger()
|
||||
{
|
||||
if(!scope_logger)
|
||||
{
|
||||
scope_logger = global_logger.load(std::memory_order_acquire);
|
||||
}
|
||||
return scope_logger;
|
||||
}
|
||||
|
||||
void log_set_scope_logger(ILogger *logger)
|
||||
{
|
||||
scope_logger = logger;
|
||||
if(!scope_logger)
|
||||
{
|
||||
scope_logger = global_logger.load(std::memory_order_acquire);
|
||||
}
|
||||
}
|
||||
|
||||
void log_log_impl(LEVEL level, bool have_color, LOG_COLOR color, const char *sys, const char *fmt, va_list args)
|
||||
{
|
||||
// Make sure we're not logging recursively.
|
||||
if(in_logger)
|
||||
{
|
||||
return;
|
||||
}
|
||||
in_logger = true;
|
||||
if(!scope_logger)
|
||||
{
|
||||
scope_logger = global_logger.load(std::memory_order_acquire);
|
||||
}
|
||||
if(!scope_logger)
|
||||
{
|
||||
in_logger = false;
|
||||
return;
|
||||
}
|
||||
|
||||
CLogMessage Msg;
|
||||
Msg.m_Level = level;
|
||||
Msg.m_HaveColor = have_color;
|
||||
Msg.m_Color = color;
|
||||
str_timestamp_format(Msg.m_aTimestamp, sizeof(Msg.m_aTimestamp), FORMAT_SPACE);
|
||||
Msg.m_TimestampLength = str_length(Msg.m_aTimestamp);
|
||||
str_copy(Msg.m_aSystem, sys, sizeof(Msg.m_aSystem));
|
||||
Msg.m_SystemLength = str_length(Msg.m_aSystem);
|
||||
|
||||
// TODO: Add level?
|
||||
str_format(Msg.m_aLine, sizeof(Msg.m_aLine), "[%s][%s]: ", Msg.m_aTimestamp, Msg.m_aSystem);
|
||||
Msg.m_LineMessageOffset = str_length(Msg.m_aLine);
|
||||
|
||||
char *pMessage = Msg.m_aLine + Msg.m_LineMessageOffset;
|
||||
int MessageSize = sizeof(Msg.m_aLine) - Msg.m_LineMessageOffset;
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||
#endif
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
_vsnprintf(pMessage, MessageSize, fmt, args);
|
||||
#else
|
||||
vsnprintf(pMessage, MessageSize, fmt, args);
|
||||
#endif
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
Msg.m_LineLength = str_length(Msg.m_aLine);
|
||||
scope_logger->Log(&Msg);
|
||||
in_logger = false;
|
||||
}
|
||||
|
||||
void log_log_v(LEVEL level, const char *sys, const char *fmt, va_list args)
|
||||
{
|
||||
log_log_impl(level, false, LOG_COLOR{0, 0, 0}, sys, fmt, args);
|
||||
}
|
||||
|
||||
void log_log(LEVEL level, const char *sys, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
log_log_impl(level, false, LOG_COLOR{0, 0, 0}, sys, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void log_log_color_v(LEVEL level, LOG_COLOR color, const char *sys, const char *fmt, va_list args)
|
||||
{
|
||||
log_log_impl(level, true, color, sys, fmt, args);
|
||||
}
|
||||
|
||||
void log_log_color(LEVEL level, LOG_COLOR color, const char *sys, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
log_log_impl(level, true, color, sys, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONF_PLATFORM_ANDROID)
|
||||
class CLoggerAndroid : public ILogger
|
||||
{
|
||||
public:
|
||||
void Log(const CLogMessage *pMessage) override
|
||||
{
|
||||
int AndroidLevel;
|
||||
switch(Level)
|
||||
{
|
||||
case LEVEL_TRACE: AndroidLevel = ANDROID_LOG_VERBOSE; break;
|
||||
case LEVEL_DEBUG: AndroidLevel = ANDROID_LOG_DEBUG; break;
|
||||
case LEVEL_INFO: AndroidLevel = ANDROID_LOG_INFO; break;
|
||||
case LEVEL_WARN: AndroidLevel = ANDROID_LOG_WARN; break;
|
||||
case LEVEL_ERROR: AndroidLevel = ANDROID_LOG_ERROR; break;
|
||||
}
|
||||
__android_log_write(AndroidLevel, pMessage->m_aSystem, pMessage->Message());
|
||||
}
|
||||
};
|
||||
std::unique_ptr<ILogger> log_logger_android()
|
||||
{
|
||||
return std::unique_ptr<ILogger>(new CLoggerAndroid());
|
||||
}
|
||||
#else
|
||||
std::unique_ptr<ILogger> log_logger_android()
|
||||
{
|
||||
dbg_assert(0, "Android logger on non-Android");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
class CLoggerCollection : public ILogger
|
||||
{
|
||||
std::vector<std::shared_ptr<ILogger>> m_apLoggers;
|
||||
|
||||
public:
|
||||
CLoggerCollection(std::vector<std::shared_ptr<ILogger>> &&apLoggers) :
|
||||
m_apLoggers(std::move(apLoggers))
|
||||
{
|
||||
}
|
||||
void Log(const CLogMessage *pMessage) override
|
||||
{
|
||||
for(auto &pLogger : m_apLoggers)
|
||||
{
|
||||
pLogger->Log(pMessage);
|
||||
}
|
||||
}
|
||||
void GlobalFinish() override
|
||||
{
|
||||
for(auto &pLogger : m_apLoggers)
|
||||
{
|
||||
pLogger->GlobalFinish();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ILogger> log_logger_collection(std::vector<std::shared_ptr<ILogger>> &&loggers)
|
||||
{
|
||||
return std::unique_ptr<ILogger>(new CLoggerCollection(std::move(loggers)));
|
||||
}
|
||||
|
||||
class CLoggerAsync : public ILogger
|
||||
{
|
||||
ASYNCIO *m_pAio;
|
||||
bool m_AnsiTruecolor;
|
||||
bool m_Close;
|
||||
|
||||
public:
|
||||
CLoggerAsync(IOHANDLE File, bool AnsiTruecolor, bool Close) :
|
||||
m_pAio(aio_new(File)),
|
||||
m_AnsiTruecolor(AnsiTruecolor),
|
||||
m_Close(Close)
|
||||
{
|
||||
}
|
||||
void Log(const CLogMessage *pMessage) override
|
||||
{
|
||||
aio_lock(m_pAio);
|
||||
if(m_AnsiTruecolor)
|
||||
{
|
||||
// https://en.wikipedia.org/w/index.php?title=ANSI_escape_code&oldid=1077146479#24-bit
|
||||
char aAnsi[32];
|
||||
if(pMessage->m_HaveColor)
|
||||
{
|
||||
str_format(aAnsi, sizeof(aAnsi),
|
||||
"\x1b[38;2;%d;%d;%dm",
|
||||
pMessage->m_Color.r,
|
||||
pMessage->m_Color.g,
|
||||
pMessage->m_Color.b);
|
||||
}
|
||||
else
|
||||
{
|
||||
str_copy(aAnsi, "\x1b[39m", sizeof(aAnsi));
|
||||
}
|
||||
aio_write_unlocked(m_pAio, aAnsi, str_length(aAnsi));
|
||||
}
|
||||
aio_write_unlocked(m_pAio, pMessage->m_aLine, pMessage->m_LineLength);
|
||||
aio_write_newline_unlocked(m_pAio);
|
||||
aio_unlock(m_pAio);
|
||||
}
|
||||
~CLoggerAsync()
|
||||
{
|
||||
if(m_Close)
|
||||
{
|
||||
aio_close(m_pAio);
|
||||
}
|
||||
aio_wait(m_pAio);
|
||||
aio_free(m_pAio);
|
||||
}
|
||||
void GlobalFinish() override
|
||||
{
|
||||
if(m_Close)
|
||||
{
|
||||
aio_close(m_pAio);
|
||||
}
|
||||
aio_wait(m_pAio);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ILogger> log_logger_file(IOHANDLE logfile)
|
||||
{
|
||||
return std::unique_ptr<ILogger>(new CLoggerAsync(logfile, false, true));
|
||||
}
|
||||
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
static int color_hsv_to_windows_console_color(const ColorHSVA &Hsv)
|
||||
{
|
||||
int h = Hsv.h * 255.0f;
|
||||
int s = Hsv.s * 255.0f;
|
||||
int v = Hsv.v * 255.0f;
|
||||
if(s >= 0 && s <= 10)
|
||||
{
|
||||
if(v <= 150)
|
||||
return 8;
|
||||
return 15;
|
||||
}
|
||||
else if(h >= 0 && h < 15)
|
||||
return 12;
|
||||
else if(h >= 15 && h < 30)
|
||||
return 6;
|
||||
else if(h >= 30 && h < 60)
|
||||
return 14;
|
||||
else if(h >= 60 && h < 110)
|
||||
return 10;
|
||||
else if(h >= 110 && h < 140)
|
||||
return 11;
|
||||
else if(h >= 140 && h < 170)
|
||||
return 9;
|
||||
else if(h >= 170 && h < 195)
|
||||
return 5;
|
||||
else if(h >= 195 && h < 240)
|
||||
return 13;
|
||||
else if(h >= 240)
|
||||
return 12;
|
||||
else
|
||||
return 15;
|
||||
}
|
||||
|
||||
class CWindowsConsoleLogger : public ILogger
|
||||
{
|
||||
public:
|
||||
void Log(const CLogMessage *pMessage) override
|
||||
{
|
||||
wchar_t *pWide = (wchar_t *)malloc((pMessage->m_LineLength + 1) * sizeof(*pWide));
|
||||
const char *p = pMessage->m_aLine;
|
||||
int WLen = 0;
|
||||
|
||||
mem_zero(pWide, pMessage->m_LineLength * sizeof(*pWide));
|
||||
|
||||
for(int Codepoint = 0; (Codepoint = str_utf8_decode(&p)); WLen++)
|
||||
{
|
||||
char aU16[4] = {0};
|
||||
|
||||
if(Codepoint < 0)
|
||||
{
|
||||
free(pWide);
|
||||
return;
|
||||
}
|
||||
|
||||
if(str_utf16le_encode(aU16, Codepoint) != 2)
|
||||
{
|
||||
free(pWide);
|
||||
return;
|
||||
}
|
||||
|
||||
mem_copy(&pWide[WLen], aU16, 2);
|
||||
}
|
||||
pWide[WLen] = '\n';
|
||||
HANDLE pConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
int Color = 15;
|
||||
if(pMessage->m_HaveColor)
|
||||
{
|
||||
ColorRGBA Rgba(1.0, 1.0, 1.0, 1.0);
|
||||
Rgba.r = pMessage->m_Color.r / 255.0;
|
||||
Rgba.g = pMessage->m_Color.g / 255.0;
|
||||
Rgba.b = pMessage->m_Color.b / 255.0;
|
||||
Color = color_hsv_to_windows_console_color(color_cast<ColorHSVA>(Rgba));
|
||||
}
|
||||
SetConsoleTextAttribute(pConsole, Color);
|
||||
WriteConsoleW(pConsole, pWide, WLen + 1, NULL, NULL);
|
||||
free(pWide);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
std::unique_ptr<ILogger> log_logger_stdout()
|
||||
{
|
||||
#if !defined(CONF_FAMILY_WINDOWS)
|
||||
// TODO: Only enable true color when COLORTERM contains "truecolor".
|
||||
// https://github.com/termstandard/colors/tree/65bf0cd1ece7c15fa33a17c17528b02c99f1ae0b#checking-for-colorterm
|
||||
return std::unique_ptr<ILogger>(new CLoggerAsync(io_stdout(), getenv("NO_COLOR") == nullptr, false));
|
||||
#else
|
||||
return std::unique_ptr<ILogger>(new CWindowsConsoleLogger());
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
class CLoggerWindowsDebugger : public ILogger
|
||||
{
|
||||
public:
|
||||
void Log(const CLogMessage *pMessage) override
|
||||
{
|
||||
WCHAR aWBuffer[4096];
|
||||
MultiByteToWideChar(CP_UTF8, 0, pMessage->m_aLine, -1, aWBuffer, sizeof(aWBuffer) / sizeof(WCHAR));
|
||||
OutputDebugStringW(aWBuffer);
|
||||
}
|
||||
};
|
||||
std::unique_ptr<ILogger> log_logger_windows_debugger()
|
||||
{
|
||||
return std::unique_ptr<ILogger>(new CLoggerWindowsDebugger());
|
||||
}
|
||||
#else
|
||||
std::unique_ptr<ILogger> log_logger_windows_debugger()
|
||||
{
|
||||
dbg_assert(0, "Windows Debug logger on non-Windows");
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
void CFutureLogger::Set(std::unique_ptr<ILogger> &&pLogger)
|
||||
{
|
||||
ILogger *null = nullptr;
|
||||
m_PendingLock.lock();
|
||||
ILogger *pLoggerRaw = pLogger.release();
|
||||
if(!m_pLogger.compare_exchange_strong(null, pLoggerRaw, std::memory_order_acq_rel))
|
||||
{
|
||||
dbg_assert(false, "future logger has already been set and can only be set once");
|
||||
}
|
||||
for(const auto &Pending : m_aPending)
|
||||
{
|
||||
pLoggerRaw->Log(&Pending);
|
||||
}
|
||||
m_aPending.clear();
|
||||
m_aPending.shrink_to_fit();
|
||||
m_PendingLock.unlock();
|
||||
}
|
||||
|
||||
void CFutureLogger::Log(const CLogMessage *pMessage)
|
||||
{
|
||||
ILogger *pLogger = m_pLogger.load(std::memory_order_acquire);
|
||||
if(pLogger)
|
||||
{
|
||||
pLogger->Log(pMessage);
|
||||
return;
|
||||
}
|
||||
m_PendingLock.lock();
|
||||
m_aPending.push_back(*pMessage);
|
||||
m_PendingLock.unlock();
|
||||
}
|
||||
|
||||
void CFutureLogger::GlobalFinish()
|
||||
{
|
||||
ILogger *pLogger = m_pLogger.load(std::memory_order_acquire);
|
||||
if(pLogger)
|
||||
{
|
||||
pLogger->GlobalFinish();
|
||||
}
|
||||
}
|
94
src/base/log.h
Normal file
94
src/base/log.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
#ifndef BASE_LOG_H
|
||||
#define BASE_LOG_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define GNUC_ATTRIBUTE(x) __attribute__(x)
|
||||
#else
|
||||
#define GNUC_ATTRIBUTE(x)
|
||||
#endif
|
||||
|
||||
enum LEVEL : char
|
||||
{
|
||||
LEVEL_ERROR,
|
||||
LEVEL_WARN,
|
||||
LEVEL_INFO,
|
||||
LEVEL_DEBUG,
|
||||
LEVEL_TRACE,
|
||||
};
|
||||
|
||||
struct LOG_COLOR
|
||||
{
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
/**
|
||||
* @defgroup Log
|
||||
*
|
||||
* Methods for outputting log messages and way of handling them.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Prints a log message.
|
||||
*
|
||||
* @param level Severity of the log message.
|
||||
* @param sys A string that describes what system the message belongs to.
|
||||
* @param fmt A printf styled format string.
|
||||
*/
|
||||
void log_log(LEVEL level, const char *sys, const char *fmt, ...)
|
||||
GNUC_ATTRIBUTE((format(printf, 3, 4)));
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Prints a log message with a given color.
|
||||
*
|
||||
* @param level Severity of the log message.
|
||||
* @param color Requested color for the log message output.
|
||||
* @param sys A string that describes what system the message belongs to.
|
||||
* @param fmt A printf styled format string.
|
||||
*/
|
||||
void log_log_color(LEVEL level, LOG_COLOR color, const char *sys, const char *fmt, ...)
|
||||
GNUC_ATTRIBUTE((format(printf, 4, 5)));
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Same as `log_log`, but takes a `va_list` instead.
|
||||
*
|
||||
* @param level Severity of the log message.
|
||||
* @param sys A string that describes what system the message belongs to.
|
||||
* @param fmt A printf styled format string.
|
||||
* @param args The variable argument list.
|
||||
*/
|
||||
void log_log_v(LEVEL level, const char *sys, const char *fmt, va_list args)
|
||||
GNUC_ATTRIBUTE((format(printf, 3, 0)));
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Same as `log_log_color`, but takes a `va_list` instead.
|
||||
*
|
||||
* @param level Severity of the log message.
|
||||
* @param color Requested color for the log message output.
|
||||
* @param sys A string that describes what system the message belongs to.
|
||||
* @param fmt A printf styled format string.
|
||||
* @param args The variable argument list.
|
||||
*/
|
||||
void log_log_color_v(LEVEL level, LOG_COLOR color, const char *sys, const char *fmt, va_list args)
|
||||
GNUC_ATTRIBUTE((format(printf, 4, 0)));
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
#endif // BASE_LOG_H
|
237
src/base/logger.h
Normal file
237
src/base/logger.h
Normal file
|
@ -0,0 +1,237 @@
|
|||
#ifndef BASE_LOGGER_H
|
||||
#define BASE_LOGGER_H
|
||||
|
||||
#include "log.h"
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
extern "C" {
|
||||
|
||||
typedef struct IOINTERNAL *IOHANDLE;
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Metadata and actual content of a log message.
|
||||
*/
|
||||
class CLogMessage
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Severity
|
||||
*/
|
||||
LEVEL m_Level;
|
||||
bool m_HaveColor;
|
||||
/**
|
||||
* The requested color of the log message. Only useful if `m_HaveColor`
|
||||
* is set.
|
||||
*/
|
||||
LOG_COLOR m_Color;
|
||||
char m_aTimestamp[80];
|
||||
char m_aSystem[32];
|
||||
/**
|
||||
* The actual log message including the timestamp and the system.
|
||||
*/
|
||||
char m_aLine[4096];
|
||||
int m_TimestampLength;
|
||||
int m_SystemLength;
|
||||
/**
|
||||
* Length of the log message including timestamp and the system.
|
||||
*/
|
||||
int m_LineLength;
|
||||
int m_LineMessageOffset;
|
||||
|
||||
/**
|
||||
* The actual log message excluding timestamp and the system.
|
||||
*/
|
||||
const char *Message() const
|
||||
{
|
||||
return m_aLine + m_LineMessageOffset;
|
||||
}
|
||||
};
|
||||
|
||||
class ILogger
|
||||
{
|
||||
public:
|
||||
virtual ~ILogger() {}
|
||||
|
||||
/**
|
||||
* Send the specified message to the logging backend.
|
||||
*
|
||||
* @param pMessage Struct describing the log message.
|
||||
*/
|
||||
virtual void Log(const CLogMessage *pMessage) = 0;
|
||||
/**
|
||||
* Flushes output buffers and shuts down.
|
||||
* Global loggers cannot be destroyed because they might be accessed
|
||||
* from multiple threads concurrently.
|
||||
*
|
||||
* This function is called on the global logger by
|
||||
* `log_global_logger_finish` when the program is about to shut down
|
||||
* and loggers are supposed to finish writing the log messages they
|
||||
* have received so far.
|
||||
*
|
||||
* The destructor of this `ILogger` instance will not be called if this
|
||||
* function is called.
|
||||
*
|
||||
* @see log_global_logger_finish
|
||||
*/
|
||||
virtual void GlobalFinish() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Registers a logger instance as the default logger for all current and future
|
||||
* threads. It will only be used if no thread-local logger is set via
|
||||
* `log_set_scope_logger`.
|
||||
*
|
||||
* This function can only be called once. The passed logger instance will never
|
||||
* be freed.
|
||||
*
|
||||
* @param logger The global logger default.
|
||||
*/
|
||||
void log_set_global_logger(ILogger *logger);
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Registers a sane default as the default logger for all current and future
|
||||
* threads.
|
||||
*
|
||||
* This is logging to stdout on most platforms and to the system log on
|
||||
* Android.
|
||||
*
|
||||
* @see log_set_global_logger
|
||||
*/
|
||||
void log_set_global_logger_default();
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Notify global loggers of impending abnormal exit.
|
||||
*
|
||||
* This function is automatically called on normal exit. It notifies the global
|
||||
* logger of the impending shutdown via `GlobalFinish`, the logger is supposed
|
||||
* to flush its buffers and shut down.
|
||||
*
|
||||
* Don't call this except right before an abnormal exit.
|
||||
*/
|
||||
void log_global_logger_finish();
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Get the logger active in the current scope. This might be the global default
|
||||
* logger or some other logger set via `log_set_scope_logger`.
|
||||
*/
|
||||
ILogger *log_get_scope_logger();
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Set the logger for the current thread. The logger isn't managed by the
|
||||
* logging system, it still needs to be kept alive or freed by the caller.
|
||||
*
|
||||
* Consider using `CLogScope` if you only want to set the logger temporarily.
|
||||
*
|
||||
* @see CLogScope
|
||||
*/
|
||||
void log_set_scope_logger(ILogger *logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Logger for sending logs to the Android system log.
|
||||
*
|
||||
* Should only be called when targeting the Android platform.
|
||||
*/
|
||||
std::unique_ptr<ILogger> log_logger_android();
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Logger combining a vector of other loggers.
|
||||
*/
|
||||
std::unique_ptr<ILogger> log_logger_collection(std::vector<std::shared_ptr<ILogger>> &&loggers);
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Logger for writing logs to the given file.
|
||||
*
|
||||
* @param file File to write to, must be opened for writing.
|
||||
*/
|
||||
std::unique_ptr<ILogger> log_logger_file(IOHANDLE file);
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Logger for writing logs to the standard output (stdout).
|
||||
*/
|
||||
std::unique_ptr<ILogger> log_logger_stdout();
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Logger for sending logs to the debugger on Windows via `OutputDebugStringW`.
|
||||
*
|
||||
* Should only be called when targeting the Windows platform.
|
||||
*/
|
||||
std::unique_ptr<ILogger> log_logger_windows_debugger();
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* Logger that collects log messages in memory until it is replaced by another
|
||||
* logger.
|
||||
*
|
||||
* Useful when you want to set a global logger without all logging targets
|
||||
* being configured.
|
||||
*/
|
||||
class CFutureLogger : public ILogger
|
||||
{
|
||||
private:
|
||||
std::atomic<ILogger *> m_pLogger;
|
||||
std::vector<CLogMessage> m_aPending;
|
||||
std::mutex m_PendingLock;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Replace the `CFutureLogger` instance with the given logger. It'll
|
||||
* receive all log messages sent to the `CFutureLogger` so far.
|
||||
*/
|
||||
void Set(std::unique_ptr<ILogger> &&pLogger);
|
||||
void Log(const CLogMessage *pMessage) override;
|
||||
void GlobalFinish() override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup Log
|
||||
*
|
||||
* RAII guard for temporarily changing the logger via `log_set_scope_logger`.
|
||||
*
|
||||
* @see log_set_scope_logger
|
||||
*/
|
||||
class CLogScope
|
||||
{
|
||||
ILogger *old_scope_logger;
|
||||
ILogger *new_scope_logger;
|
||||
|
||||
public:
|
||||
CLogScope(ILogger *logger) :
|
||||
old_scope_logger(log_get_scope_logger()),
|
||||
new_scope_logger(logger)
|
||||
{
|
||||
log_set_scope_logger(new_scope_logger);
|
||||
}
|
||||
~CLogScope()
|
||||
{
|
||||
//dbg_assert(log_get_scope_logger() == new_scope_logger, "loggers weren't properly scoped");
|
||||
log_set_scope_logger(old_scope_logger);
|
||||
}
|
||||
};
|
||||
#endif // BASE_LOGGER_H
|
|
@ -1,6 +1,7 @@
|
|||
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
#include <array> // std::size
|
||||
#include <atomic>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
|
@ -10,6 +11,9 @@
|
|||
#include <ctime>
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#if !defined(CONF_PLATFORM_MACOS)
|
||||
#include <base/color.h>
|
||||
#endif
|
||||
|
@ -133,22 +137,6 @@ IOHANDLE io_current_exe()
|
|||
#endif
|
||||
}
|
||||
|
||||
struct DBG_LOGGER_DATA
|
||||
{
|
||||
DBG_LOGGER logger;
|
||||
DBG_LOGGER_FINISH finish;
|
||||
DBG_LOGGER_ASSERTION on_assert = nullptr;
|
||||
void *user;
|
||||
};
|
||||
|
||||
static DBG_LOGGER_DATA loggers[16];
|
||||
static int has_stdout_logger = 0;
|
||||
static int num_loggers = 0;
|
||||
|
||||
#ifndef CONF_FAMILY_WINDOWS
|
||||
static DBG_LOGGER_DATA stdout_nonewline_logger;
|
||||
#endif
|
||||
|
||||
static NETSTATS network_stats = {0};
|
||||
|
||||
#define VLEN 128
|
||||
|
@ -183,21 +171,20 @@ static NETSOCKET_INTERNAL invalid_socket = {NETTYPE_INVALID, -1, -1, -1};
|
|||
|
||||
#define AF_WEBSOCKET_INET (0xee)
|
||||
|
||||
static void dbg_assert_notify_loggers()
|
||||
std::atomic_bool dbg_assert_failing = false;
|
||||
|
||||
bool dbg_assert_has_failed()
|
||||
{
|
||||
for(int i = 0; i < num_loggers; i++)
|
||||
{
|
||||
if(loggers[i].on_assert)
|
||||
loggers[i].on_assert(loggers[i].user);
|
||||
}
|
||||
return dbg_assert_failing.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
void dbg_assert_imp(const char *filename, int line, int test, const char *msg)
|
||||
{
|
||||
if(!test)
|
||||
{
|
||||
dbg_assert_failing.store(true, std::memory_order_release);
|
||||
dbg_msg("assert", "%s(%d): %s", filename, line, msg);
|
||||
dbg_assert_notify_loggers();
|
||||
log_global_logger_finish();
|
||||
dbg_break();
|
||||
}
|
||||
}
|
||||
|
@ -214,177 +201,11 @@ void dbg_break()
|
|||
void dbg_msg(const char *sys, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *msg;
|
||||
int len;
|
||||
|
||||
char str[1024 * 4];
|
||||
int i;
|
||||
|
||||
char timestr[80];
|
||||
str_timestamp_format(timestr, sizeof(timestr), FORMAT_SPACE);
|
||||
|
||||
str_format(str, sizeof(str), "[%s][%s]: ", timestr, sys);
|
||||
|
||||
len = str_length(str);
|
||||
msg = (char *)str + len;
|
||||
|
||||
va_start(args, fmt);
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
_vsnprintf(msg, sizeof(str) - len, fmt, args);
|
||||
#elif defined(CONF_PLATFORM_ANDROID)
|
||||
__android_log_vprint(ANDROID_LOG_DEBUG, sys, fmt, args);
|
||||
#else
|
||||
vsnprintf(msg, sizeof(str) - len, fmt, args);
|
||||
#endif
|
||||
|
||||
log_log_v(LEVEL_INFO, sys, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
for(i = 0; i < num_loggers; i++)
|
||||
loggers[i].logger(str, loggers[i].user);
|
||||
}
|
||||
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
static void logger_win_debugger(const char *line, void *user)
|
||||
{
|
||||
(void)user;
|
||||
WCHAR wBuffer[512];
|
||||
MultiByteToWideChar(CP_UTF8, 0, line, -1, wBuffer, std::size(wBuffer));
|
||||
OutputDebugStringW(wBuffer);
|
||||
OutputDebugStringW(L"\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void logger_file(const char *line, void *user)
|
||||
{
|
||||
ASYNCIO *logfile = (ASYNCIO *)user;
|
||||
aio_lock(logfile);
|
||||
aio_write_unlocked(logfile, line, str_length(line));
|
||||
aio_write_newline_unlocked(logfile);
|
||||
aio_unlock(logfile);
|
||||
}
|
||||
|
||||
#if !defined(CONF_FAMILY_WINDOWS)
|
||||
static void logger_file_no_newline(const char *line, void *user)
|
||||
{
|
||||
ASYNCIO *logfile = (ASYNCIO *)user;
|
||||
aio_lock(logfile);
|
||||
aio_write_unlocked(logfile, line, str_length(line));
|
||||
aio_unlock(logfile);
|
||||
}
|
||||
#else
|
||||
static void logger_stdout_sync(const char *line, void *user)
|
||||
{
|
||||
size_t length = str_length(line);
|
||||
wchar_t *wide = (wchar_t *)malloc(length * sizeof(*wide));
|
||||
const char *p = line;
|
||||
int wlen = 0;
|
||||
HANDLE console;
|
||||
|
||||
(void)user;
|
||||
mem_zero(wide, length * sizeof *wide);
|
||||
|
||||
for(int codepoint = 0; (codepoint = str_utf8_decode(&p)); wlen++)
|
||||
{
|
||||
char u16[4] = {0};
|
||||
|
||||
if(codepoint < 0)
|
||||
{
|
||||
free(wide);
|
||||
return;
|
||||
}
|
||||
|
||||
if(str_utf16le_encode(u16, codepoint) != 2)
|
||||
{
|
||||
free(wide);
|
||||
return;
|
||||
}
|
||||
|
||||
mem_copy(&wide[wlen], u16, 2);
|
||||
}
|
||||
|
||||
console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
WriteConsoleW(console, wide, wlen, NULL, NULL);
|
||||
WriteConsoleA(console, "\n", 1, NULL, NULL);
|
||||
free(wide);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void logger_stdout_finish(void *user)
|
||||
{
|
||||
ASYNCIO *logfile = (ASYNCIO *)user;
|
||||
aio_wait(logfile);
|
||||
aio_free(logfile);
|
||||
}
|
||||
|
||||
static void logger_file_finish(void *user)
|
||||
{
|
||||
ASYNCIO *logfile = (ASYNCIO *)user;
|
||||
aio_close(logfile);
|
||||
logger_stdout_finish(user);
|
||||
}
|
||||
|
||||
static void dbg_logger_finish()
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < num_loggers; i++)
|
||||
{
|
||||
if(loggers[i].finish)
|
||||
{
|
||||
loggers[i].finish(loggers[i].user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dbg_logger(DBG_LOGGER logger, DBG_LOGGER_FINISH finish, void *user)
|
||||
{
|
||||
DBG_LOGGER_DATA data;
|
||||
if(num_loggers == 0)
|
||||
{
|
||||
atexit(dbg_logger_finish);
|
||||
}
|
||||
data.logger = logger;
|
||||
data.finish = finish;
|
||||
data.user = user;
|
||||
loggers[num_loggers] = data;
|
||||
num_loggers++;
|
||||
}
|
||||
|
||||
void dbg_logger_assertion(DBG_LOGGER logger, DBG_LOGGER_FINISH finish, DBG_LOGGER_ASSERTION on_assert, void *user)
|
||||
{
|
||||
dbg_logger(logger, finish, user);
|
||||
|
||||
loggers[num_loggers - 1].on_assert = on_assert;
|
||||
}
|
||||
|
||||
void dbg_logger_stdout()
|
||||
{
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
dbg_logger(logger_stdout_sync, 0, 0);
|
||||
#else
|
||||
ASYNCIO *logger_obj = aio_new(io_stdout());
|
||||
dbg_logger(logger_file, logger_stdout_finish, logger_obj);
|
||||
dbg_logger(logger_file_no_newline, 0, logger_obj);
|
||||
stdout_nonewline_logger = loggers[num_loggers - 1];
|
||||
--num_loggers;
|
||||
#endif
|
||||
has_stdout_logger = 1;
|
||||
}
|
||||
|
||||
void dbg_logger_debugger()
|
||||
{
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
dbg_logger(logger_win_debugger, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dbg_logger_file(const char *filename)
|
||||
{
|
||||
IOHANDLE logfile = io_open(filename, IOFLAG_WRITE);
|
||||
if(logfile)
|
||||
dbg_logger(logger_file, logger_file_finish, aio_new(logfile));
|
||||
else
|
||||
dbg_msg("dbg/logger", "failed to open '%s' for logging", filename);
|
||||
}
|
||||
/* */
|
||||
|
||||
void mem_copy(void *dest, const void *source, unsigned size)
|
||||
|
@ -4035,76 +3856,6 @@ int secure_rand_below(int below)
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
static int color_hsv_to_windows_console_color(const ColorHSVA *hsv)
|
||||
{
|
||||
int h = hsv->h * 255.0f;
|
||||
int s = hsv->s * 255.0f;
|
||||
int v = hsv->v * 255.0f;
|
||||
if(s >= 0 && s <= 10)
|
||||
{
|
||||
if(v <= 150)
|
||||
return 8;
|
||||
return 15;
|
||||
}
|
||||
else if(h >= 0 && h < 15)
|
||||
return 12;
|
||||
else if(h >= 15 && h < 30)
|
||||
return 6;
|
||||
else if(h >= 30 && h < 60)
|
||||
return 14;
|
||||
else if(h >= 60 && h < 110)
|
||||
return 10;
|
||||
else if(h >= 110 && h < 140)
|
||||
return 11;
|
||||
else if(h >= 140 && h < 170)
|
||||
return 9;
|
||||
else if(h >= 170 && h < 195)
|
||||
return 5;
|
||||
else if(h >= 195 && h < 240)
|
||||
return 13;
|
||||
else if(h >= 240)
|
||||
return 12;
|
||||
else
|
||||
return 15;
|
||||
}
|
||||
#endif
|
||||
|
||||
void set_console_msg_color(const void *rgbvoid)
|
||||
{
|
||||
static const char *pNoColor = getenv("NO_COLOR");
|
||||
if(pNoColor)
|
||||
return;
|
||||
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
const ColorRGBA *rgb = (const ColorRGBA *)rgbvoid;
|
||||
int color = 15;
|
||||
if(rgb)
|
||||
{
|
||||
ColorHSVA hsv = color_cast<ColorHSVA>(*rgb);
|
||||
color = color_hsv_to_windows_console_color(&hsv);
|
||||
}
|
||||
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
SetConsoleTextAttribute(console, color);
|
||||
#elif CONF_PLATFORM_LINUX
|
||||
const ColorRGBA *rgb = (const ColorRGBA *)rgbvoid;
|
||||
// set true color terminal escape codes refering
|
||||
// https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit
|
||||
int esc_seq = 0x1B;
|
||||
char buff[32];
|
||||
if(rgb == NULL)
|
||||
// reset foreground color
|
||||
str_format(buff, sizeof(buff), "%c[39m", esc_seq);
|
||||
else
|
||||
// set rgb foreground color
|
||||
// if not used by a true color terminal it is still converted refering
|
||||
// https://wiki.archlinux.org/title/Color_output_in_console#True_color_support
|
||||
str_format(buff, sizeof(buff), "%c[38;2;%d;%d;%dm", esc_seq, (int)uint8_t(rgb->r * 255.0f), (int)uint8_t(rgb->g * 255.0f), (int)uint8_t(rgb->b * 255.0f));
|
||||
if(has_stdout_logger)
|
||||
stdout_nonewline_logger.logger(buff, stdout_nonewline_logger.user);
|
||||
#endif
|
||||
}
|
||||
|
||||
int os_version_str(char *version, int length)
|
||||
{
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
|
|
|
@ -65,6 +65,17 @@ void dbg_assert_imp(const char *filename, int line, int test, const char *msg);
|
|||
#define GNUC_ATTRIBUTE(x)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Checks whether the program is currently shutting down due to a failed
|
||||
* assert.
|
||||
*
|
||||
* @ingroup Debug
|
||||
*
|
||||
* @return indication whether the program is currently shutting down due to a
|
||||
* failed assert.
|
||||
*/
|
||||
bool dbg_assert_has_failed();
|
||||
|
||||
/**
|
||||
* Breaks into the debugger.
|
||||
*
|
||||
|
@ -1899,17 +1910,6 @@ int open_file(const char *path);
|
|||
|
||||
void swap_endian(void *data, unsigned elem_size, unsigned num);
|
||||
|
||||
typedef void (*DBG_LOGGER)(const char *line, void *user);
|
||||
typedef void (*DBG_LOGGER_FINISH)(void *user);
|
||||
void dbg_logger(DBG_LOGGER logger, DBG_LOGGER_FINISH finish, void *user);
|
||||
|
||||
typedef void (*DBG_LOGGER_ASSERTION)(void *user);
|
||||
void dbg_logger_assertion(DBG_LOGGER logger, DBG_LOGGER_FINISH finish, DBG_LOGGER_ASSERTION on_assert, void *user);
|
||||
|
||||
void dbg_logger_stdout();
|
||||
void dbg_logger_debugger();
|
||||
void dbg_logger_file(const char *filename);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint64_t sent_packets;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <tuple>
|
||||
|
||||
#include <base/hash_ctxt.h>
|
||||
#include <base/logger.h>
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
#include <base/vmath.h>
|
||||
|
@ -35,6 +36,7 @@
|
|||
|
||||
#include <engine/client/http.h>
|
||||
#include <engine/client/notifications.h>
|
||||
#include <engine/shared/assertion_logger.h>
|
||||
#include <engine/shared/compression.h>
|
||||
#include <engine/shared/config.h>
|
||||
#include <engine/shared/datafile.h>
|
||||
|
@ -4339,6 +4341,23 @@ int main(int argc, const char **argv)
|
|||
init_exception_handler();
|
||||
#endif
|
||||
|
||||
std::vector<std::shared_ptr<ILogger>> apLoggers;
|
||||
#if defined(CONF_PLATFORM_ANDROID)
|
||||
apLoggers.push_back(std::shared_ptr<ILogger>(log_logger_android()));
|
||||
#else
|
||||
if(!Silent)
|
||||
{
|
||||
apLoggers.push_back(std::shared_ptr<ILogger>(log_logger_stdout()));
|
||||
}
|
||||
#endif
|
||||
std::shared_ptr<CFutureLogger> pFutureFileLogger = std::make_shared<CFutureLogger>();
|
||||
apLoggers.push_back(pFutureFileLogger);
|
||||
std::shared_ptr<CFutureLogger> pFutureConsoleLogger = std::make_shared<CFutureLogger>();
|
||||
apLoggers.push_back(pFutureConsoleLogger);
|
||||
std::shared_ptr<CFutureLogger> pFutureAssertionLogger = std::make_shared<CFutureLogger>();
|
||||
apLoggers.push_back(pFutureAssertionLogger);
|
||||
log_set_global_logger(log_logger_collection(std::move(apLoggers)).release());
|
||||
|
||||
if(secure_random_init() != 0)
|
||||
{
|
||||
RandInitFailed = true;
|
||||
|
@ -4352,7 +4371,7 @@ int main(int argc, const char **argv)
|
|||
pClient->RegisterInterfaces();
|
||||
|
||||
// create the components
|
||||
IEngine *pEngine = CreateEngine(GAME_NAME, Silent, 2);
|
||||
IEngine *pEngine = CreateEngine(GAME_NAME, pFutureConsoleLogger, 2);
|
||||
IConsole *pConsole = CreateConsole(CFGFLAG_CLIENT);
|
||||
IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_CLIENT, argc, (const char **)argv);
|
||||
IConfigManager *pConfigManager = CreateConfigManager();
|
||||
|
@ -4363,6 +4382,7 @@ int main(int argc, const char **argv)
|
|||
IDiscord *pDiscord = CreateDiscord();
|
||||
ISteam *pSteam = CreateSteam();
|
||||
|
||||
pFutureAssertionLogger->Set(CreateAssertionLogger(pStorage, GAME_NAME));
|
||||
#if defined(CONF_EXCEPTION_HANDLING)
|
||||
char aBufPath[IO_MAX_PATH_LENGTH];
|
||||
char aBufName[IO_MAX_PATH_LENGTH];
|
||||
|
@ -4484,7 +4504,18 @@ int main(int argc, const char **argv)
|
|||
pSteam->ClearConnectAddress();
|
||||
}
|
||||
|
||||
pClient->Engine()->InitLogfile();
|
||||
if(g_Config.m_Logfile[0])
|
||||
{
|
||||
IOHANDLE Logfile = io_open(g_Config.m_Logfile, IOFLAG_WRITE);
|
||||
if(Logfile)
|
||||
{
|
||||
pFutureFileLogger->Set(log_logger_file(Logfile));
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_msg("client", "failed to open '%s' for logging", g_Config.m_Logfile);
|
||||
}
|
||||
}
|
||||
|
||||
// run the client
|
||||
dbg_msg("client", "starting...");
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
static const ColorRGBA gs_ConsoleDefaultColor(1, 1, 1, 1);
|
||||
|
||||
enum LEVEL : char;
|
||||
struct CChecksumData;
|
||||
|
||||
class IConsole : public IInterface
|
||||
|
@ -104,8 +105,6 @@ public:
|
|||
virtual void ExecuteLineStroked(int Stroke, const char *pStr, int ClientID = -1, bool InterpretSemicolons = true) = 0;
|
||||
virtual void ExecuteFile(const char *pFilename, int ClientID = -1, bool LogFailure = false, int StorageType = IStorage::TYPE_ALL) = 0;
|
||||
|
||||
virtual int RegisterPrintCallback(int OutputLevel, FPrintCallback pfnPrintCallback, void *pUserData) = 0;
|
||||
virtual void SetPrintOutputLevel(int Index, int OutputLevel) = 0;
|
||||
virtual char *Format(char *pBuf, int Size, const char *pFrom, const char *pStr) = 0;
|
||||
virtual void Print(int Level, const char *pFrom, const char *pStr, ColorRGBA PrintColor = gs_ConsoleDefaultColor) = 0;
|
||||
virtual void SetTeeHistorianCommandCallback(FTeeHistorianCommandCallback pfnCallback, void *pUser) = 0;
|
||||
|
@ -115,6 +114,8 @@ public:
|
|||
|
||||
virtual void ResetServerGameSettings() = 0;
|
||||
|
||||
static LEVEL ToLogLevel(int ConsoleLevel);
|
||||
|
||||
// DDRace
|
||||
|
||||
bool m_Cheated;
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
#include "kernel.h"
|
||||
#include <engine/shared/jobs.h>
|
||||
|
||||
class CFutureLogger;
|
||||
class ILogger;
|
||||
|
||||
class CHostLookup : public IJob
|
||||
{
|
||||
private:
|
||||
|
@ -32,12 +35,12 @@ public:
|
|||
virtual ~IEngine() = default;
|
||||
|
||||
virtual void Init() = 0;
|
||||
virtual void InitLogfile() = 0;
|
||||
virtual void AddJob(std::shared_ptr<IJob> pJob) = 0;
|
||||
virtual void SetAdditionalLogger(std::unique_ptr<ILogger> &&pLogger) = 0;
|
||||
static void RunJobBlocking(IJob *pJob);
|
||||
};
|
||||
|
||||
extern IEngine *CreateEngine(const char *pAppname, bool Silent, int Jobs);
|
||||
extern IEngine *CreateEngine(const char *pAppname, std::shared_ptr<CFutureLogger> pFutureLogger, int Jobs);
|
||||
extern IEngine *CreateTestEngine(const char *pAppname, int Jobs);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -60,7 +60,6 @@ public:
|
|||
virtual int GetClientInfo(int ClientID, CClientInfo *pInfo) const = 0;
|
||||
virtual void SetClientDDNetVersion(int ClientID, int DDNetVersion) = 0;
|
||||
virtual void GetClientAddr(int ClientID, char *pAddrStr, int Size) const = 0;
|
||||
virtual void RestrictRconOutput(int ClientID) = 0;
|
||||
|
||||
virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientID) = 0;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "server.h"
|
||||
|
||||
#include <base/logger.h>
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
|
||||
|
@ -16,6 +17,7 @@
|
|||
#include <engine/server.h>
|
||||
#include <engine/storage.h>
|
||||
|
||||
#include <engine/shared/assertion_logger.h>
|
||||
#include <engine/shared/compression.h>
|
||||
#include <engine/shared/config.h>
|
||||
#include <engine/shared/datafile.h>
|
||||
|
@ -36,6 +38,7 @@
|
|||
|
||||
// DDRace
|
||||
#include <engine/shared/linereader.h>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <zlib.h>
|
||||
|
||||
|
@ -272,6 +275,77 @@ void CServerBan::ConBanRegionRange(IConsole::IResult *pResult, void *pUser)
|
|||
ConBanRange(pResult, static_cast<CNetBan *>(pServerBan));
|
||||
}
|
||||
|
||||
class CServerLogger : public ILogger
|
||||
{
|
||||
CServer *m_pServer;
|
||||
std::mutex m_PendingLock;
|
||||
std::vector<CLogMessage> m_aPending;
|
||||
std::thread::id m_MainThread;
|
||||
|
||||
public:
|
||||
CServerLogger(CServer *pServer) :
|
||||
m_pServer(pServer),
|
||||
m_MainThread(std::this_thread::get_id())
|
||||
{
|
||||
dbg_assert(pServer != nullptr, "server pointer must not be null");
|
||||
}
|
||||
void Log(const CLogMessage *pMessage) override;
|
||||
// Must be called from the main thread!
|
||||
void OnServerDeletion();
|
||||
};
|
||||
|
||||
void CServerLogger::Log(const CLogMessage *pMessage)
|
||||
{
|
||||
m_PendingLock.lock();
|
||||
if(m_MainThread == std::this_thread::get_id())
|
||||
{
|
||||
if(!m_aPending.empty())
|
||||
{
|
||||
if(m_pServer)
|
||||
{
|
||||
for(const auto &Message : m_aPending)
|
||||
{
|
||||
m_pServer->SendLogLine(&Message);
|
||||
}
|
||||
}
|
||||
m_aPending.clear();
|
||||
}
|
||||
m_PendingLock.unlock();
|
||||
m_pServer->SendLogLine(pMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_aPending.push_back(*pMessage);
|
||||
m_PendingLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CServerLogger::OnServerDeletion()
|
||||
{
|
||||
dbg_assert(m_MainThread == std::this_thread::get_id(), "CServerLogger::OnServerDeletion not called from the main thread");
|
||||
m_pServer = nullptr;
|
||||
}
|
||||
|
||||
// Not thread-safe!
|
||||
class CRconClientLogger : public ILogger
|
||||
{
|
||||
CServer *m_pServer;
|
||||
int m_ClientID;
|
||||
|
||||
public:
|
||||
CRconClientLogger(CServer *pServer, int ClientID) :
|
||||
m_pServer(pServer),
|
||||
m_ClientID(ClientID)
|
||||
{
|
||||
}
|
||||
void Log(const CLogMessage *pMessage) override;
|
||||
};
|
||||
|
||||
void CRconClientLogger::Log(const CLogMessage *pMessage)
|
||||
{
|
||||
m_pServer->SendRconLogLine(m_ClientID, pMessage);
|
||||
}
|
||||
|
||||
void CServer::CClient::Reset()
|
||||
{
|
||||
// reset input
|
||||
|
@ -319,8 +393,6 @@ CServer::CServer() :
|
|||
m_RconClientID = IServer::RCON_CID_SERV;
|
||||
m_RconAuthLevel = AUTHED_ADMIN;
|
||||
|
||||
m_RconRestrict = -1;
|
||||
|
||||
m_ServerInfoFirstRequest = 0;
|
||||
m_ServerInfoNumRequests = 0;
|
||||
m_ServerInfoNeedsUpdate = false;
|
||||
|
@ -539,6 +611,18 @@ int CServer::Init()
|
|||
return 0;
|
||||
}
|
||||
|
||||
void CServer::SendLogLine(const CLogMessage *pMessage)
|
||||
{
|
||||
if(pMessage->m_Level <= IConsole::ToLogLevel(g_Config.m_ConsoleOutputLevel))
|
||||
{
|
||||
SendRconLogLine(-1, pMessage);
|
||||
}
|
||||
if(pMessage->m_Level <= IConsole::ToLogLevel(g_Config.m_EcOutputLevel))
|
||||
{
|
||||
m_Econ.Send(-1, pMessage->m_aLine);
|
||||
}
|
||||
}
|
||||
|
||||
void CServer::SetRconCID(int ClientID)
|
||||
{
|
||||
m_RconClientID = ClientID;
|
||||
|
@ -1232,15 +1316,9 @@ void CServer::SendRconLine(int ClientID, const char *pLine)
|
|||
SendMsg(&Msg, MSGFLAG_VITAL, ClientID);
|
||||
}
|
||||
|
||||
void CServer::SendRconLineAuthed(const char *pLine, void *pUser, ColorRGBA PrintColor)
|
||||
void CServer::SendRconLogLine(int ClientID, const CLogMessage *pMessage)
|
||||
{
|
||||
CServer *pThis = (CServer *)pUser;
|
||||
static int s_ReentryGuard = 0;
|
||||
|
||||
if(s_ReentryGuard)
|
||||
return;
|
||||
s_ReentryGuard++;
|
||||
|
||||
const char *pLine = pMessage->m_aLine;
|
||||
const char *pStart = str_find(pLine, "<{");
|
||||
const char *pEnd = pStart == NULL ? NULL : str_find(pStart + 2, "}>");
|
||||
const char *pLineWithoutIps;
|
||||
|
@ -1267,13 +1345,19 @@ void CServer::SendRconLineAuthed(const char *pLine, void *pUser, ColorRGBA Print
|
|||
pLineWithoutIps = aLineWithoutIps;
|
||||
}
|
||||
|
||||
if(ClientID == -1)
|
||||
{
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed >= pThis->m_RconAuthLevel && (pThis->m_RconRestrict == -1 || pThis->m_RconRestrict == i))
|
||||
pThis->SendRconLine(i, pThis->m_aClients[i].m_ShowIps ? pLine : pLineWithoutIps);
|
||||
if(m_aClients[i].m_State != CClient::STATE_EMPTY && m_aClients[i].m_Authed >= AUTHED_ADMIN)
|
||||
SendRconLine(i, m_aClients[i].m_ShowIps ? pLine : pLineWithoutIps);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY)
|
||||
SendRconLine(ClientID, m_aClients[ClientID].m_ShowIps ? pLine : pLineWithoutIps);
|
||||
}
|
||||
|
||||
s_ReentryGuard--;
|
||||
}
|
||||
|
||||
void CServer::SendRconCmdAdd(const IConsole::CCommandInfo *pCommandInfo, int ClientID)
|
||||
|
@ -1587,7 +1671,11 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
|
|||
m_RconClientID = ClientID;
|
||||
m_RconAuthLevel = m_aClients[ClientID].m_Authed;
|
||||
Console()->SetAccessLevel(m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : m_aClients[ClientID].m_Authed == AUTHED_MOD ? IConsole::ACCESS_LEVEL_MOD : m_aClients[ClientID].m_Authed == AUTHED_HELPER ? IConsole::ACCESS_LEVEL_HELPER : IConsole::ACCESS_LEVEL_USER);
|
||||
{
|
||||
CRconClientLogger Logger(this, ClientID);
|
||||
CLogScope Scope(&Logger);
|
||||
Console()->ExecuteLineFlag(pCmd, CFGFLAG_SERVER, ClientID);
|
||||
}
|
||||
Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN);
|
||||
m_RconClientID = IServer::RCON_CID_SERV;
|
||||
m_RconAuthLevel = AUTHED_ADMIN;
|
||||
|
@ -2401,8 +2489,6 @@ int CServer::Run()
|
|||
g_UuidManager.DebugDump();
|
||||
}
|
||||
|
||||
m_PrintCBIndex = Console()->RegisterPrintCallback(Config()->m_ConsoleOutputLevel, SendRconLineAuthed, this);
|
||||
|
||||
{
|
||||
int Size = GameServer()->PersistentClientDataSize();
|
||||
for(auto &Client : m_aClients)
|
||||
|
@ -3357,16 +3443,6 @@ void CServer::ConchainCommandAccessUpdate(IConsole::IResult *pResult, void *pUse
|
|||
pfnCallback(pResult, pCallbackUserData);
|
||||
}
|
||||
|
||||
void CServer::ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
||||
{
|
||||
pfnCallback(pResult, pCallbackUserData);
|
||||
if(pResult->NumArguments() == 1)
|
||||
{
|
||||
CServer *pThis = static_cast<CServer *>(pUserData);
|
||||
pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0));
|
||||
}
|
||||
}
|
||||
|
||||
void CServer::LogoutClient(int ClientID, const char *pReason)
|
||||
{
|
||||
if(!IsSixup(ClientID))
|
||||
|
@ -3549,7 +3625,6 @@ void CServer::RegisterCommands()
|
|||
|
||||
Console()->Chain("sv_max_clients_per_ip", ConchainMaxclientsperipUpdate, this);
|
||||
Console()->Chain("access_level", ConchainCommandAccessUpdate, this);
|
||||
Console()->Chain("console_output_level", ConchainConsoleOutputLevelUpdate, this);
|
||||
|
||||
Console()->Chain("sv_rcon_password", ConchainRconPasswordChange, this);
|
||||
Console()->Chain("sv_rcon_mod_password", ConchainRconModPasswordChange, this);
|
||||
|
@ -3619,6 +3694,23 @@ int main(int argc, const char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ILogger>> apLoggers;
|
||||
#if defined(CONF_PLATFORM_ANDROID)
|
||||
apLoggers.push_back(std::shared_ptr<ILogger>(log_logger_android()));
|
||||
#else
|
||||
if(!Silent)
|
||||
{
|
||||
apLoggers.push_back(std::shared_ptr<ILogger>(log_logger_stdout()));
|
||||
}
|
||||
#endif
|
||||
std::shared_ptr<CFutureLogger> pFutureFileLogger = std::make_shared<CFutureLogger>();
|
||||
apLoggers.push_back(pFutureFileLogger);
|
||||
std::shared_ptr<CFutureLogger> pFutureConsoleLogger = std::make_shared<CFutureLogger>();
|
||||
apLoggers.push_back(pFutureConsoleLogger);
|
||||
std::shared_ptr<CFutureLogger> pFutureAssertionLogger = std::make_shared<CFutureLogger>();
|
||||
apLoggers.push_back(pFutureAssertionLogger);
|
||||
log_set_global_logger(log_logger_collection(std::move(apLoggers)).release());
|
||||
|
||||
if(secure_random_init() != 0)
|
||||
{
|
||||
dbg_msg("secure", "could not initialize secure RNG");
|
||||
|
@ -3641,7 +3733,7 @@ int main(int argc, const char **argv)
|
|||
IKernel *pKernel = IKernel::Create();
|
||||
|
||||
// create the components
|
||||
IEngine *pEngine = CreateEngine("DDNet", Silent, 2);
|
||||
IEngine *pEngine = CreateEngine("DDNet", pFutureConsoleLogger, 2);
|
||||
IEngineMap *pEngineMap = CreateEngineMap();
|
||||
IGameServer *pGameServer = CreateGameServer();
|
||||
IConsole *pConsole = CreateConsole(CFGFLAG_SERVER | CFGFLAG_ECON);
|
||||
|
@ -3650,6 +3742,7 @@ int main(int argc, const char **argv)
|
|||
IConfigManager *pConfigManager = CreateConfigManager();
|
||||
IEngineAntibot *pEngineAntibot = CreateEngineAntibot();
|
||||
|
||||
pFutureAssertionLogger->Set(CreateAssertionLogger(pStorage, GAME_NAME));
|
||||
#if defined(CONF_EXCEPTION_HANDLING)
|
||||
char aBuf[IO_MAX_PATH_LENGTH];
|
||||
char aBufName[IO_MAX_PATH_LENGTH];
|
||||
|
@ -3713,7 +3806,19 @@ int main(int argc, const char **argv)
|
|||
pConsole->Register("sv_test_cmds", "", CFGFLAG_SERVER, CServer::ConTestingCommands, pConsole, "Turns testing commands aka cheats on/off (setting only works in initial config)");
|
||||
pConsole->Register("sv_rescue", "", CFGFLAG_SERVER, CServer::ConRescue, pConsole, "Allow /rescue command so players can teleport themselves out of freeze (setting only works in initial config)");
|
||||
|
||||
pEngine->InitLogfile();
|
||||
if(g_Config.m_Logfile[0])
|
||||
{
|
||||
IOHANDLE Logfile = io_open(g_Config.m_Logfile, IOFLAG_WRITE);
|
||||
if(Logfile)
|
||||
{
|
||||
pFutureFileLogger->Set(log_logger_file(Logfile));
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_msg("client", "failed to open '%s' for logging", g_Config.m_Logfile);
|
||||
}
|
||||
}
|
||||
pEngine->SetAdditionalLogger(std::unique_ptr<ILogger>(new CServerLogger(pServer)));
|
||||
|
||||
// run the server
|
||||
dbg_msg("server", "starting...");
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include "upnp.h"
|
||||
#endif
|
||||
|
||||
class CLogMessage;
|
||||
|
||||
class CSnapIDPool
|
||||
{
|
||||
enum
|
||||
|
@ -88,6 +90,8 @@ public:
|
|||
|
||||
class CServer : public IServer
|
||||
{
|
||||
friend class CServerLogger;
|
||||
|
||||
class IGameServer *m_pGameServer;
|
||||
class CConfig *m_pConfig;
|
||||
class IConsole *m_pConsole;
|
||||
|
@ -251,8 +255,6 @@ public:
|
|||
CRegister m_RegSixup;
|
||||
CAuthManager m_AuthManager;
|
||||
|
||||
int m_RconRestrict;
|
||||
|
||||
int64_t m_ServerInfoFirstRequest;
|
||||
int m_ServerInfoNumRequests;
|
||||
|
||||
|
@ -285,6 +287,7 @@ public:
|
|||
|
||||
int Init();
|
||||
|
||||
void SendLogLine(const CLogMessage *pMessage);
|
||||
void SetRconCID(int ClientID);
|
||||
int GetAuthedState(int ClientID) const;
|
||||
const char *GetAuthName(int ClientID) const;
|
||||
|
@ -318,7 +321,8 @@ public:
|
|||
void SendMapData(int ClientID, int Chunk);
|
||||
void SendConnectionReady(int ClientID);
|
||||
void SendRconLine(int ClientID, const char *pLine);
|
||||
static void SendRconLineAuthed(const char *pLine, void *pUser, ColorRGBA PrintColor = {1, 1, 1, 1});
|
||||
// Accepts -1 as ClientID to mean "all clients with at least auth level admin"
|
||||
void SendRconLogLine(int ClientID, const CLogMessage *pMessage);
|
||||
|
||||
void SendRconCmdAdd(const IConsole::CCommandInfo *pCommandInfo, int ClientID);
|
||||
void SendRconCmdRem(const IConsole::CCommandInfo *pCommandInfo, int ClientID);
|
||||
|
@ -403,7 +407,6 @@ public:
|
|||
static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
static void ConchainMaxclientsperipUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
static void ConchainCommandAccessUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
static void ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
|
||||
void LogoutClient(int ClientID, const char *pReason);
|
||||
void LogoutKey(int Key, const char *pReason);
|
||||
|
@ -432,7 +435,6 @@ public:
|
|||
int m_aPrevStates[MAX_CLIENTS];
|
||||
const char *GetAnnouncementLine(char const *pFileName);
|
||||
unsigned m_AnnouncementLastLine;
|
||||
void RestrictRconOutput(int ClientID) { m_RconRestrict = ClientID; }
|
||||
|
||||
virtual int *GetIdMap(int ClientID);
|
||||
|
||||
|
|
|
@ -1,27 +1,49 @@
|
|||
#include "assertion_logger.h"
|
||||
|
||||
#include <base/logger.h>
|
||||
#include <base/system.h>
|
||||
#include <engine/shared/ringbuffer.h>
|
||||
#include <engine/storage.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
void CAssertionLogger::DbgLogger(const char *pLine, void *pUser)
|
||||
class CAssertionLogger : public ILogger
|
||||
{
|
||||
((CAssertionLogger *)pUser)->DbgLogger(pLine);
|
||||
}
|
||||
void Dump();
|
||||
|
||||
void CAssertionLogger::DbgLoggerAssertion(void *pUser)
|
||||
{
|
||||
((CAssertionLogger *)pUser)->DbgLoggerAssertion();
|
||||
}
|
||||
struct SDebugMessageItem
|
||||
{
|
||||
char m_aMessage[1024];
|
||||
};
|
||||
|
||||
void CAssertionLogger::DbgLogger(const char *pLine)
|
||||
std::mutex m_DbgMessageMutex;
|
||||
CStaticRingBuffer<SDebugMessageItem, sizeof(SDebugMessageItem) * 64, CRingBufferBase::FLAG_RECYCLE> m_DbgMessages;
|
||||
|
||||
char m_aAssertLogPath[IO_MAX_PATH_LENGTH];
|
||||
char m_aGameName[256];
|
||||
|
||||
public:
|
||||
CAssertionLogger(const char *pAssertLogPath, const char *pGameName);
|
||||
void Log(const CLogMessage *pMessage) override;
|
||||
void GlobalFinish() override;
|
||||
};
|
||||
|
||||
void CAssertionLogger::Log(const CLogMessage *pMessage)
|
||||
{
|
||||
std::unique_lock<std::mutex> Lock(m_DbgMessageMutex);
|
||||
|
||||
SDebugMessageItem *pMsgItem = (SDebugMessageItem *)m_DbgMessages.Allocate(sizeof(SDebugMessageItem));
|
||||
str_copy(pMsgItem->m_aMessage, pLine, std::size(pMsgItem->m_aMessage));
|
||||
str_copy(pMsgItem->m_aMessage, pMessage->m_aLine, std::size(pMsgItem->m_aMessage));
|
||||
}
|
||||
|
||||
void CAssertionLogger::DbgLoggerAssertion()
|
||||
void CAssertionLogger::GlobalFinish()
|
||||
{
|
||||
if(dbg_assert_has_failed())
|
||||
{
|
||||
Dump();
|
||||
}
|
||||
}
|
||||
|
||||
void CAssertionLogger::Dump()
|
||||
{
|
||||
char aAssertLogFile[IO_MAX_PATH_LENGTH];
|
||||
char aDate[64];
|
||||
|
@ -45,10 +67,15 @@ void CAssertionLogger::DbgLoggerAssertion()
|
|||
}
|
||||
}
|
||||
|
||||
void CAssertionLogger::Init(const char *pAssertLogPath, const char *pGameName)
|
||||
CAssertionLogger::CAssertionLogger(const char *pAssertLogPath, const char *pGameName)
|
||||
{
|
||||
str_copy(m_aAssertLogPath, pAssertLogPath, std::size(m_aAssertLogPath));
|
||||
str_copy(m_aGameName, pGameName, std::size(m_aGameName));
|
||||
|
||||
dbg_logger_assertion(DbgLogger, nullptr, DbgLoggerAssertion, this);
|
||||
}
|
||||
|
||||
std::unique_ptr<ILogger> CreateAssertionLogger(IStorage *pStorage, const char *pGameName)
|
||||
{
|
||||
char aAssertLogPath[IO_MAX_PATH_LENGTH];
|
||||
pStorage->GetCompletePath(IStorage::TYPE_SAVE, "dumps/", aAssertLogPath, sizeof(aAssertLogPath));
|
||||
return std::unique_ptr<ILogger>(new CAssertionLogger(aAssertLogPath, pGameName));
|
||||
}
|
||||
|
|
|
@ -1,36 +1,11 @@
|
|||
#ifndef ENGINE_SHARED_ASSERTION_LOGGER_H
|
||||
#define ENGINE_SHARED_ASSERTION_LOGGER_H
|
||||
|
||||
#include <base/system.h>
|
||||
#include <memory>
|
||||
|
||||
#include <cstddef>
|
||||
#include <mutex>
|
||||
class ILogger;
|
||||
class IStorage;
|
||||
|
||||
#include <engine/shared/ringbuffer.h>
|
||||
|
||||
class CAssertionLogger
|
||||
{
|
||||
static void DbgLogger(const char *pLine, void *pUser);
|
||||
static void DbgLoggerAssertion(void *pUser);
|
||||
|
||||
static constexpr size_t s_MaxDbgMessageCount = 64;
|
||||
|
||||
void DbgLogger(const char *pLine);
|
||||
void DbgLoggerAssertion();
|
||||
|
||||
struct SDebugMessageItem
|
||||
{
|
||||
char m_aMessage[1024];
|
||||
};
|
||||
|
||||
std::mutex m_DbgMessageMutex;
|
||||
CStaticRingBuffer<SDebugMessageItem, sizeof(SDebugMessageItem) * s_MaxDbgMessageCount, CRingBufferBase::FLAG_RECYCLE> m_DbgMessages;
|
||||
|
||||
char m_aAssertLogPath[IO_MAX_PATH_LENGTH];
|
||||
char m_aGameName[256];
|
||||
|
||||
public:
|
||||
void Init(const char *pAssertLogPath, const char *pGameName);
|
||||
};
|
||||
std::unique_ptr<ILogger> CreateAssertionLogger(IStorage *pStorage, const char *pGameName);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <new>
|
||||
|
||||
#include <base/color.h>
|
||||
#include <base/log.h>
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
#include <base/vmath.h>
|
||||
|
@ -290,23 +291,6 @@ char CConsole::NextParam(const char *&pFormat)
|
|||
return *pFormat;
|
||||
}
|
||||
|
||||
int CConsole::RegisterPrintCallback(int OutputLevel, FPrintCallback pfnPrintCallback, void *pUserData)
|
||||
{
|
||||
if(m_NumPrintCB == MAX_PRINT_CB)
|
||||
return -1;
|
||||
|
||||
m_aPrintCB[m_NumPrintCB].m_OutputLevel = clamp(OutputLevel, (int)(OUTPUT_LEVEL_STANDARD), (int)(OUTPUT_LEVEL_DEBUG));
|
||||
m_aPrintCB[m_NumPrintCB].m_pfnPrintCallback = pfnPrintCallback;
|
||||
m_aPrintCB[m_NumPrintCB].m_pPrintCallbackUserdata = pUserData;
|
||||
return m_NumPrintCB++;
|
||||
}
|
||||
|
||||
void CConsole::SetPrintOutputLevel(int Index, int OutputLevel)
|
||||
{
|
||||
if(Index >= 0 && Index < MAX_PRINT_CB)
|
||||
m_aPrintCB[Index].m_OutputLevel = clamp(OutputLevel, (int)(OUTPUT_LEVEL_STANDARD), (int)(OUTPUT_LEVEL_DEBUG));
|
||||
}
|
||||
|
||||
char *CConsole::Format(char *pBuf, int Size, const char *pFrom, const char *pStr)
|
||||
{
|
||||
char aTimeBuf[80];
|
||||
|
@ -316,26 +300,40 @@ char *CConsole::Format(char *pBuf, int Size, const char *pFrom, const char *pStr
|
|||
return pBuf;
|
||||
}
|
||||
|
||||
LEVEL IConsole::ToLogLevel(int Level)
|
||||
{
|
||||
switch(Level)
|
||||
{
|
||||
case IConsole::OUTPUT_LEVEL_STANDARD:
|
||||
return LEVEL_INFO;
|
||||
case IConsole::OUTPUT_LEVEL_ADDINFO:
|
||||
return LEVEL_DEBUG;
|
||||
case IConsole::OUTPUT_LEVEL_DEBUG:
|
||||
return LEVEL_TRACE;
|
||||
}
|
||||
dbg_assert(0, "invalid log level");
|
||||
return LEVEL_INFO;
|
||||
}
|
||||
|
||||
LOG_COLOR ColorToLogColor(ColorRGBA Color)
|
||||
{
|
||||
return LOG_COLOR{
|
||||
(uint8_t)(Color.r * 255.0),
|
||||
(uint8_t)(Color.g * 255.0),
|
||||
(uint8_t)(Color.b * 255.0)};
|
||||
}
|
||||
|
||||
void CConsole::Print(int Level, const char *pFrom, const char *pStr, ColorRGBA PrintColor)
|
||||
{
|
||||
if(g_Config.m_ConsoleEnableColors)
|
||||
{
|
||||
LEVEL LogLevel = IConsole::ToLogLevel(Level);
|
||||
// if the color is pure white, use default terminal color
|
||||
if(mem_comp(&PrintColor, &gs_ConsoleDefaultColor, sizeof(ColorRGBA)) == 0)
|
||||
set_console_msg_color(NULL);
|
||||
if(mem_comp(&PrintColor, &gs_ConsoleDefaultColor, sizeof(ColorRGBA)) != 0)
|
||||
{
|
||||
log_log_color(LogLevel, ColorToLogColor(PrintColor), pFrom, "%s", pStr);
|
||||
}
|
||||
else
|
||||
set_console_msg_color(&PrintColor);
|
||||
}
|
||||
dbg_msg(pFrom, "%s", pStr);
|
||||
set_console_msg_color(NULL);
|
||||
char aBuf[1024];
|
||||
Format(aBuf, sizeof(aBuf), pFrom, pStr);
|
||||
for(int i = 0; i < m_NumPrintCB; ++i)
|
||||
{
|
||||
if(Level <= m_aPrintCB[i].m_OutputLevel && m_aPrintCB[i].m_pfnPrintCallback)
|
||||
{
|
||||
m_aPrintCB[i].m_pfnPrintCallback(aBuf, m_aPrintCB[i].m_pPrintCallbackUserdata, PrintColor);
|
||||
}
|
||||
log_log(LogLevel, pFrom, "%s", pStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -966,8 +964,6 @@ CConsole::CConsole(int FlagMask)
|
|||
m_ExecutionQueue.Reset();
|
||||
m_pFirstCommand = 0;
|
||||
m_pFirstExec = 0;
|
||||
mem_zero(m_aPrintCB, sizeof(m_aPrintCB));
|
||||
m_NumPrintCB = 0;
|
||||
m_pfnTeeHistorianCommandCallback = 0;
|
||||
m_pTeeHistorianCommandUserdata = 0;
|
||||
|
||||
|
|
|
@ -63,14 +63,6 @@ class CConsole : public IConsole
|
|||
|
||||
void ExecuteLineStroked(int Stroke, const char *pStr, int ClientID = -1, bool InterpretSemicolons = true);
|
||||
|
||||
struct
|
||||
{
|
||||
int m_OutputLevel;
|
||||
FPrintCallback m_pfnPrintCallback;
|
||||
void *m_pPrintCallbackUserdata;
|
||||
} m_aPrintCB[MAX_PRINT_CB];
|
||||
int m_NumPrintCB;
|
||||
|
||||
FTeeHistorianCommandCallback m_pfnTeeHistorianCommandCallback;
|
||||
void *m_pTeeHistorianCommandUserdata;
|
||||
|
||||
|
@ -217,8 +209,6 @@ public:
|
|||
virtual void ExecuteLineFlag(const char *pStr, int FlagMask, int ClientID = -1, bool InterpretSemicolons = true);
|
||||
virtual void ExecuteFile(const char *pFilename, int ClientID = -1, bool LogFailure = false, int StorageType = IStorage::TYPE_ALL);
|
||||
|
||||
virtual int RegisterPrintCallback(int OutputLevel, FPrintCallback pfnPrintCallback, void *pUserData);
|
||||
virtual void SetPrintOutputLevel(int Index, int OutputLevel);
|
||||
virtual char *Format(char *pBuf, int Size, const char *pFrom, const char *pStr);
|
||||
virtual void Print(int Level, const char *pFrom, const char *pStr, ColorRGBA PrintColor = gs_ConsoleDefaultColor);
|
||||
virtual void SetTeeHistorianCommandCallback(FTeeHistorianCommandCallback pfnCallback, void *pUser);
|
||||
|
|
|
@ -36,21 +36,6 @@ int CEcon::DelClientCallback(int ClientID, const char *pReason, void *pUser)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void CEcon::SendLineCB(const char *pLine, void *pUserData, ColorRGBA PrintColor)
|
||||
{
|
||||
static_cast<CEcon *>(pUserData)->Send(-1, pLine);
|
||||
}
|
||||
|
||||
void CEcon::ConchainEconOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
||||
{
|
||||
pfnCallback(pResult, pCallbackUserData);
|
||||
if(pResult->NumArguments() == 1)
|
||||
{
|
||||
CEcon *pThis = static_cast<CEcon *>(pUserData);
|
||||
pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0));
|
||||
}
|
||||
}
|
||||
|
||||
void CEcon::ConLogout(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CEcon *pThis = static_cast<CEcon *>(pUserData);
|
||||
|
@ -94,10 +79,6 @@ void CEcon::Init(CConfig *pConfig, IConsole *pConsole, CNetBan *pNetBan)
|
|||
char aBuf[128];
|
||||
str_format(aBuf, sizeof(aBuf), "bound to %s:%d", g_Config.m_EcBindaddr, g_Config.m_EcPort);
|
||||
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "econ", aBuf);
|
||||
|
||||
Console()->Chain("ec_output_level", ConchainEconOutputLevelUpdate, this);
|
||||
m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_EcOutputLevel, SendLineCB, this);
|
||||
|
||||
Console()->Register("logout", "", CFGFLAG_ECON, ConLogout, this, "Logout of econ");
|
||||
}
|
||||
else
|
||||
|
|
|
@ -39,7 +39,6 @@ class CEcon
|
|||
int m_UserClientID;
|
||||
|
||||
static void SendLineCB(const char *pLine, void *pUserData, ColorRGBA PrintColor = {1, 1, 1, 1});
|
||||
static void ConchainEconOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
static void ConLogout(IConsole::IResult *pResult, void *pUserData);
|
||||
|
||||
static int NewClientCallback(int ClientID, void *pUser);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
|
||||
#include <base/logger.h>
|
||||
#include <base/system.h>
|
||||
|
||||
#include <engine/console.h>
|
||||
|
@ -31,7 +32,7 @@ public:
|
|||
IStorage *m_pStorage;
|
||||
bool m_Logging;
|
||||
|
||||
CAssertionLogger m_AssertionLogger;
|
||||
std::shared_ptr<CFutureLogger> m_pFutureLogger;
|
||||
|
||||
char m_aAppName[256];
|
||||
|
||||
|
@ -57,15 +58,12 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
CEngine(bool Test, const char *pAppname, bool Silent, int Jobs)
|
||||
CEngine(bool Test, const char *pAppname, std::shared_ptr<CFutureLogger> pFutureLogger, int Jobs) :
|
||||
m_pFutureLogger(std::move(pFutureLogger))
|
||||
{
|
||||
str_copy(m_aAppName, pAppname, std::size(m_aAppName));
|
||||
if(!Test)
|
||||
{
|
||||
if(!Silent)
|
||||
dbg_logger_stdout();
|
||||
dbg_logger_debugger();
|
||||
|
||||
//
|
||||
dbg_msg("engine", "running on %s-%s-%s", CONF_FAMILY_STRING, CONF_PLATFORM_STRING, CONF_ARCH_STRING);
|
||||
#ifdef CONF_ARCH_ENDIAN_LITTLE
|
||||
|
@ -101,24 +99,20 @@ public:
|
|||
|
||||
char aFullPath[IO_MAX_PATH_LENGTH];
|
||||
m_pStorage->GetCompletePath(IStorage::TYPE_SAVE, "dumps/", aFullPath, sizeof(aFullPath));
|
||||
m_AssertionLogger.Init(aFullPath, m_aAppName);
|
||||
|
||||
m_pConsole->Register("dbg_lognetwork", "", CFGFLAG_SERVER | CFGFLAG_CLIENT, Con_DbgLognetwork, this, "Log the network");
|
||||
}
|
||||
|
||||
void InitLogfile() override
|
||||
{
|
||||
// open logfile if needed
|
||||
if(g_Config.m_Logfile[0])
|
||||
dbg_logger_file(g_Config.m_Logfile);
|
||||
}
|
||||
|
||||
void AddJob(std::shared_ptr<IJob> pJob) override
|
||||
{
|
||||
if(g_Config.m_Debug)
|
||||
dbg_msg("engine", "job added");
|
||||
m_JobPool.Add(std::move(pJob));
|
||||
}
|
||||
|
||||
void SetAdditionalLogger(std::unique_ptr<ILogger> &&pLogger) override
|
||||
{
|
||||
m_pFutureLogger->Set(std::move(pLogger));
|
||||
}
|
||||
};
|
||||
|
||||
void IEngine::RunJobBlocking(IJob *pJob)
|
||||
|
@ -126,5 +120,5 @@ void IEngine::RunJobBlocking(IJob *pJob)
|
|||
CJobPool::RunBlocking(pJob);
|
||||
}
|
||||
|
||||
IEngine *CreateEngine(const char *pAppname, bool Silent, int Jobs) { return new CEngine(false, pAppname, Silent, Jobs); }
|
||||
IEngine *CreateTestEngine(const char *pAppname, int Jobs) { return new CEngine(true, pAppname, true, Jobs); }
|
||||
IEngine *CreateEngine(const char *pAppname, std::shared_ptr<CFutureLogger> pFutureLogger, int Jobs) { return new CEngine(false, pAppname, std::move(pFutureLogger), Jobs); }
|
||||
IEngine *CreateTestEngine(const char *pAppname, int Jobs) { return new CEngine(true, pAppname, nullptr, Jobs); }
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "gameclient.h"
|
||||
|
||||
class IKernel *CComponent::Kernel() const { return m_pClient->Kernel(); }
|
||||
class IEngine *CComponent::Engine() const { return m_pClient->Engine(); }
|
||||
class IGraphics *CComponent::Graphics() const { return m_pClient->Graphics(); }
|
||||
class ITextRender *CComponent::TextRender() const { return m_pClient->TextRender(); }
|
||||
class IInput *CComponent::Input() const { return m_pClient->Input(); }
|
||||
|
|
|
@ -36,6 +36,7 @@ protected:
|
|||
* Get the kernel interface.
|
||||
*/
|
||||
class IKernel *Kernel() const;
|
||||
class IEngine *Engine() const;
|
||||
/**
|
||||
* Get the graphics interface.
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
|
||||
#include <base/logger.h>
|
||||
#include <base/tl/sorted_array.h>
|
||||
|
||||
#include <climits>
|
||||
|
@ -38,6 +39,49 @@
|
|||
|
||||
#include "console.h"
|
||||
|
||||
class CConsoleLogger : public ILogger
|
||||
{
|
||||
CGameConsole *m_pConsole;
|
||||
std::mutex m_ConsoleMutex;
|
||||
|
||||
public:
|
||||
CConsoleLogger(CGameConsole *pConsole) :
|
||||
m_pConsole(pConsole)
|
||||
{
|
||||
dbg_assert(pConsole != nullptr, "console pointer must not be null");
|
||||
}
|
||||
|
||||
void Log(const CLogMessage *pMessage) override;
|
||||
void OnConsoleDeletion();
|
||||
};
|
||||
|
||||
void CConsoleLogger::Log(const CLogMessage *pMessage)
|
||||
{
|
||||
// TODO: Fix thread-unsafety of accessing `g_Config.m_ConsoleOutputLevel`
|
||||
if(pMessage->m_Level > IConsole::ToLogLevel(g_Config.m_ConsoleOutputLevel))
|
||||
{
|
||||
return;
|
||||
}
|
||||
ColorRGBA Color = gs_ConsoleDefaultColor;
|
||||
if(pMessage->m_HaveColor)
|
||||
{
|
||||
Color.r = pMessage->m_Color.r / 255.0;
|
||||
Color.g = pMessage->m_Color.g / 255.0;
|
||||
Color.b = pMessage->m_Color.b / 255.0;
|
||||
}
|
||||
std::unique_lock<std::mutex> Guard(m_ConsoleMutex);
|
||||
if(m_pConsole)
|
||||
{
|
||||
m_pConsole->m_LocalConsole.PrintLine(pMessage->m_aLine, pMessage->m_LineLength, Color);
|
||||
}
|
||||
}
|
||||
|
||||
void CConsoleLogger::OnConsoleDeletion()
|
||||
{
|
||||
std::unique_lock<std::mutex> Guard(m_ConsoleMutex);
|
||||
m_pConsole = nullptr;
|
||||
}
|
||||
|
||||
CGameConsole::CInstance::CInstance(int Type)
|
||||
{
|
||||
m_pHistoryEntry = 0x0;
|
||||
|
@ -69,18 +113,22 @@ void CGameConsole::CInstance::Init(CGameConsole *pGameConsole)
|
|||
|
||||
void CGameConsole::CInstance::ClearBacklog()
|
||||
{
|
||||
m_BacklogLock.lock();
|
||||
m_Backlog.Init();
|
||||
m_BacklogCurPage = 0;
|
||||
m_BacklogLock.unlock();
|
||||
}
|
||||
|
||||
void CGameConsole::CInstance::ClearBacklogYOffsets()
|
||||
{
|
||||
m_BacklogLock.lock();
|
||||
auto *pEntry = m_Backlog.First();
|
||||
while(pEntry)
|
||||
{
|
||||
pEntry->m_YOffset = -1.0f;
|
||||
pEntry = m_Backlog.Next(pEntry);
|
||||
}
|
||||
m_BacklogLock.unlock();
|
||||
}
|
||||
|
||||
void CGameConsole::CInstance::ClearHistory()
|
||||
|
@ -374,13 +422,12 @@ void CGameConsole::CInstance::OnInput(IInput::CEvent Event)
|
|||
}
|
||||
}
|
||||
|
||||
void CGameConsole::CInstance::PrintLine(const char *pLine, ColorRGBA PrintColor)
|
||||
void CGameConsole::CInstance::PrintLine(const char *pLine, int Len, ColorRGBA PrintColor)
|
||||
{
|
||||
int Len = str_length(pLine);
|
||||
|
||||
if(Len > 255)
|
||||
Len = 255;
|
||||
|
||||
m_BacklogLock.lock();
|
||||
CBacklogEntry *pEntry = m_Backlog.Allocate(sizeof(CBacklogEntry) + Len);
|
||||
pEntry->m_YOffset = -1.0f;
|
||||
pEntry->m_PrintColor = PrintColor;
|
||||
|
@ -388,6 +435,7 @@ void CGameConsole::CInstance::PrintLine(const char *pLine, ColorRGBA PrintColor)
|
|||
pEntry->m_aText[Len] = 0;
|
||||
if(m_pGameConsole->m_ConsoleType == m_Type)
|
||||
m_pGameConsole->m_NewLineCounter++;
|
||||
m_BacklogLock.unlock();
|
||||
}
|
||||
|
||||
CGameConsole::CGameConsole() :
|
||||
|
@ -399,6 +447,11 @@ CGameConsole::CGameConsole() :
|
|||
m_StateChangeDuration = 0.1f;
|
||||
}
|
||||
|
||||
CGameConsole::~CGameConsole()
|
||||
{
|
||||
m_pConsoleLogger->OnConsoleDeletion();
|
||||
}
|
||||
|
||||
float CGameConsole::TimeNow()
|
||||
{
|
||||
static long long s_TimeStart = time_get();
|
||||
|
@ -685,6 +738,8 @@ void CGameConsole::OnRender()
|
|||
}
|
||||
}
|
||||
|
||||
pConsole->m_BacklogLock.lock();
|
||||
|
||||
// render log (current page, wrap lines)
|
||||
CInstance::CBacklogEntry *pEntry = pConsole->m_Backlog.Last();
|
||||
float OffsetY = 0.0f;
|
||||
|
@ -805,6 +860,8 @@ void CGameConsole::OnRender()
|
|||
}
|
||||
}
|
||||
|
||||
pConsole->m_BacklogLock.unlock();
|
||||
|
||||
// render page
|
||||
char aBuf[128];
|
||||
TextRender()->TextColor(1, 1, 1, 1);
|
||||
|
@ -894,11 +951,13 @@ void CGameConsole::Dump(int Type)
|
|||
IOHANDLE io = Storage()->OpenFile(aFilename, IOFLAG_WRITE, IStorage::TYPE_SAVE);
|
||||
if(io)
|
||||
{
|
||||
pConsole->m_BacklogLock.lock();
|
||||
for(CInstance::CBacklogEntry *pEntry = pConsole->m_Backlog.First(); pEntry; pEntry = pConsole->m_Backlog.Next(pEntry))
|
||||
{
|
||||
io_write(io, pEntry->m_aText, str_length(pEntry->m_aText));
|
||||
io_write_newline(io);
|
||||
}
|
||||
pConsole->m_BacklogLock.unlock();
|
||||
io_close(io);
|
||||
}
|
||||
}
|
||||
|
@ -933,11 +992,6 @@ void CGameConsole::ConDumpRemoteConsole(IConsole::IResult *pResult, void *pUserD
|
|||
((CGameConsole *)pUserData)->Dump(CONSOLETYPE_REMOTE);
|
||||
}
|
||||
|
||||
void CGameConsole::ClientConsolePrintCallback(const char *pStr, void *pUserData, ColorRGBA PrintColor)
|
||||
{
|
||||
((CGameConsole *)pUserData)->m_LocalConsole.PrintLine(pStr, PrintColor);
|
||||
}
|
||||
|
||||
void CGameConsole::ConConsolePageUp(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CInstance *pConsole = ((CGameConsole *)pUserData)->CurrentConsole();
|
||||
|
@ -952,16 +1006,6 @@ void CGameConsole::ConConsolePageDown(IConsole::IResult *pResult, void *pUserDat
|
|||
pConsole->m_BacklogCurPage = 0;
|
||||
}
|
||||
|
||||
void CGameConsole::ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
||||
{
|
||||
pfnCallback(pResult, pCallbackUserData);
|
||||
if(pResult->NumArguments() == 1)
|
||||
{
|
||||
CGameConsole *pThis = static_cast<CGameConsole *>(pUserData);
|
||||
pThis->Console()->SetPrintOutputLevel(pThis->m_PrintCBIndex, pResult->GetInteger(0));
|
||||
}
|
||||
}
|
||||
|
||||
void CGameConsole::RequireUsername(bool UsernameReq)
|
||||
{
|
||||
if((m_RemoteConsole.m_UsernameReq = UsernameReq))
|
||||
|
@ -974,9 +1018,9 @@ void CGameConsole::RequireUsername(bool UsernameReq)
|
|||
void CGameConsole::PrintLine(int Type, const char *pLine)
|
||||
{
|
||||
if(Type == CONSOLETYPE_LOCAL)
|
||||
m_LocalConsole.PrintLine(pLine);
|
||||
m_LocalConsole.PrintLine(pLine, str_length(pLine), ColorRGBA{1, 1, 1, 1});
|
||||
else if(Type == CONSOLETYPE_REMOTE)
|
||||
m_RemoteConsole.PrintLine(pLine);
|
||||
m_RemoteConsole.PrintLine(pLine, str_length(pLine), ColorRGBA{1, 1, 1, 1});
|
||||
}
|
||||
|
||||
void CGameConsole::OnConsoleInit()
|
||||
|
@ -987,9 +1031,6 @@ void CGameConsole::OnConsoleInit()
|
|||
|
||||
m_pConsole = Kernel()->RequestInterface<IConsole>();
|
||||
|
||||
//
|
||||
m_PrintCBIndex = Console()->RegisterPrintCallback(g_Config.m_ConsoleOutputLevel, ClientConsolePrintCallback, this);
|
||||
|
||||
Console()->Register("toggle_local_console", "", CFGFLAG_CLIENT, ConToggleLocalConsole, this, "Toggle local console");
|
||||
Console()->Register("toggle_remote_console", "", CFGFLAG_CLIENT, ConToggleRemoteConsole, this, "Toggle remote console");
|
||||
Console()->Register("clear_local_console", "", CFGFLAG_CLIENT, ConClearLocalConsole, this, "Clear local console");
|
||||
|
@ -999,12 +1040,12 @@ void CGameConsole::OnConsoleInit()
|
|||
|
||||
Console()->Register("console_page_up", "", CFGFLAG_CLIENT, ConConsolePageUp, this, "Previous page in console");
|
||||
Console()->Register("console_page_down", "", CFGFLAG_CLIENT, ConConsolePageDown, this, "Next page in console");
|
||||
|
||||
Console()->Chain("console_output_level", ConchainConsoleOutputLevelUpdate, this);
|
||||
}
|
||||
|
||||
void CGameConsole::OnInit()
|
||||
{
|
||||
m_pConsoleLogger = new CConsoleLogger(this);
|
||||
Engine()->SetAdditionalLogger(std::unique_ptr<ILogger>(m_pConsoleLogger));
|
||||
// add resize event
|
||||
Graphics()->AddWindowResizeListener([this](void *) {
|
||||
m_LocalConsole.ClearBacklogYOffsets();
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include <engine/console.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
enum
|
||||
{
|
||||
CONSOLE_CLOSED,
|
||||
|
@ -16,8 +18,11 @@ enum
|
|||
CONSOLE_CLOSING,
|
||||
};
|
||||
|
||||
class CConsoleLogger;
|
||||
|
||||
class CGameConsole : public CComponent
|
||||
{
|
||||
friend class CConsoleLogger;
|
||||
class CInstance
|
||||
{
|
||||
public:
|
||||
|
@ -27,6 +32,7 @@ class CGameConsole : public CComponent
|
|||
ColorRGBA m_PrintColor;
|
||||
char m_aText[1];
|
||||
};
|
||||
std::mutex m_BacklogLock;
|
||||
CStaticRingBuffer<CBacklogEntry, 1024 * 1024, CRingBufferBase::FLAG_RECYCLE> m_Backlog;
|
||||
CStaticRingBuffer<char, 64 * 1024, CRingBufferBase::FLAG_RECYCLE> m_History;
|
||||
char *m_pHistoryEntry;
|
||||
|
@ -64,20 +70,20 @@ class CGameConsole : public CComponent
|
|||
void ExecuteLine(const char *pLine);
|
||||
|
||||
void OnInput(IInput::CEvent Event);
|
||||
void PrintLine(const char *pLine, ColorRGBA PrintColor = {1, 1, 1, 1});
|
||||
void PrintLine(const char *pLine, int Len, ColorRGBA PrintColor);
|
||||
|
||||
const char *GetString() const { return m_Input.GetString(); }
|
||||
static void PossibleCommandsCompleteCallback(const char *pStr, void *pUser);
|
||||
};
|
||||
|
||||
class IConsole *m_pConsole;
|
||||
CConsoleLogger *m_pConsoleLogger = nullptr;
|
||||
|
||||
CInstance m_LocalConsole;
|
||||
CInstance m_RemoteConsole;
|
||||
|
||||
CInstance *CurrentConsole();
|
||||
float TimeNow();
|
||||
int m_PrintCBIndex;
|
||||
|
||||
int m_ConsoleType;
|
||||
int m_ConsoleState;
|
||||
|
@ -100,7 +106,6 @@ class CGameConsole : public CComponent
|
|||
void Dump(int Type);
|
||||
|
||||
static void PossibleCommandsRenderCallback(const char *pStr, void *pUser);
|
||||
static void ClientConsolePrintCallback(const char *pStr, void *pUserData, ColorRGBA PrintColor = {1, 1, 1, 1});
|
||||
static void ConToggleLocalConsole(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConToggleRemoteConsole(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConClearLocalConsole(IConsole::IResult *pResult, void *pUserData);
|
||||
|
@ -109,7 +114,6 @@ class CGameConsole : public CComponent
|
|||
static void ConDumpRemoteConsole(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConConsolePageUp(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConConsolePageDown(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConchainConsoleOutputLevelUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
|
||||
public:
|
||||
enum
|
||||
|
@ -119,6 +123,7 @@ public:
|
|||
};
|
||||
|
||||
CGameConsole();
|
||||
~CGameConsole();
|
||||
virtual int Sizeof() const override { return sizeof(*this); }
|
||||
|
||||
void PrintLine(int Type, const char *pLine);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "gamecontext.h"
|
||||
#include "teeinfo.h"
|
||||
#include <antibot/antibot_data.h>
|
||||
#include <base/logger.h>
|
||||
#include <base/math.h>
|
||||
#include <cstring>
|
||||
#include <engine/console.h>
|
||||
|
@ -29,6 +30,26 @@
|
|||
#include "player.h"
|
||||
#include "score.h"
|
||||
|
||||
// Not thread-safe!
|
||||
class CClientChatLogger : public ILogger
|
||||
{
|
||||
CGameContext *m_pGameServer;
|
||||
int m_ClientID;
|
||||
|
||||
public:
|
||||
CClientChatLogger(CGameContext *pGameServer, int ClientID) :
|
||||
m_pGameServer(pGameServer),
|
||||
m_ClientID(ClientID)
|
||||
{
|
||||
}
|
||||
void Log(const CLogMessage *pMessage) override;
|
||||
};
|
||||
|
||||
void CClientChatLogger::Log(const CLogMessage *pMessage)
|
||||
{
|
||||
m_pGameServer->SendChatTarget(m_ClientID, pMessage->Message());
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
RESET,
|
||||
|
@ -1824,7 +1845,6 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
|
|||
pPlayer->m_LastCommandPos = (pPlayer->m_LastCommandPos + 1) % 4;
|
||||
|
||||
m_ChatResponseTargetID = ClientID;
|
||||
Server()->RestrictRconOutput(ClientID);
|
||||
Console()->SetFlagMask(CFGFLAG_CHAT);
|
||||
|
||||
int Authed = Server()->GetAuthedState(ClientID);
|
||||
|
@ -1832,9 +1852,12 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
|
|||
Console()->SetAccessLevel(Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : Authed == AUTHED_MOD ? IConsole::ACCESS_LEVEL_MOD : IConsole::ACCESS_LEVEL_HELPER);
|
||||
else
|
||||
Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_USER);
|
||||
Console()->SetPrintOutputLevel(m_ChatPrintCBIndex, 0);
|
||||
|
||||
{
|
||||
CClientChatLogger Logger(this, ClientID);
|
||||
CLogScope Scope(&Logger);
|
||||
Console()->ExecuteLine(pMsg->m_pMessage + 1, ClientID, false);
|
||||
}
|
||||
// m_apPlayers[ClientID] can be NULL, if the player used a
|
||||
// timeout code and replaced another client.
|
||||
char aBuf[256];
|
||||
|
@ -1844,7 +1867,6 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
|
|||
Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN);
|
||||
Console()->SetFlagMask(CFGFLAG_SERVER);
|
||||
m_ChatResponseTargetID = -1;
|
||||
Server()->RestrictRconOutput(-1);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -3072,8 +3094,6 @@ void CGameContext::OnConsoleInit()
|
|||
m_pEngine = Kernel()->RequestInterface<IEngine>();
|
||||
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
||||
|
||||
m_ChatPrintCBIndex = Console()->RegisterPrintCallback(0, SendChatResponse, this);
|
||||
|
||||
Console()->Register("tune", "s[tuning] i[value]", CFGFLAG_SERVER | CFGFLAG_GAME, ConTuneParam, this, "Tune variable to value");
|
||||
Console()->Register("toggle_tune", "s[tuning] i[value 1] i[value 2]", CFGFLAG_SERVER | CFGFLAG_GAME, ConToggleTuneParam, this, "Toggle tune variable");
|
||||
Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, "Reset tuning");
|
||||
|
@ -3673,58 +3693,6 @@ const char *CGameContext::NetVersion() const { return GAME_NETVERSION; }
|
|||
|
||||
IGameServer *CreateGameServer() { return new CGameContext; }
|
||||
|
||||
void CGameContext::SendChatResponseAll(const char *pLine, void *pUser)
|
||||
{
|
||||
CGameContext *pSelf = (CGameContext *)pUser;
|
||||
|
||||
static int ReentryGuard = 0;
|
||||
const char *pLineOrig = pLine;
|
||||
|
||||
if(ReentryGuard)
|
||||
return;
|
||||
ReentryGuard++;
|
||||
|
||||
if(*pLine == '[')
|
||||
do
|
||||
pLine++;
|
||||
while((pLine - 2 < pLineOrig || *(pLine - 2) != ':') && *pLine != 0); // remove the category (e.g. [Console]: No Such Command)
|
||||
|
||||
pSelf->SendChat(-1, CHAT_ALL, pLine);
|
||||
|
||||
ReentryGuard--;
|
||||
}
|
||||
|
||||
void CGameContext::SendChatResponse(const char *pLine, void *pUser, ColorRGBA PrintColor)
|
||||
{
|
||||
CGameContext *pSelf = (CGameContext *)pUser;
|
||||
int ClientID = pSelf->m_ChatResponseTargetID;
|
||||
|
||||
if(ClientID < 0 || ClientID >= MAX_CLIENTS)
|
||||
return;
|
||||
|
||||
const char *pLineOrig = pLine;
|
||||
|
||||
static int ReentryGuard = 0;
|
||||
|
||||
if(ReentryGuard)
|
||||
return;
|
||||
ReentryGuard++;
|
||||
|
||||
if(pLine[0] == '[')
|
||||
{
|
||||
// Remove time and category: [20:39:00][Console]
|
||||
pLine = str_find(pLine, "]: ");
|
||||
if(pLine)
|
||||
pLine += 3;
|
||||
else
|
||||
pLine = pLineOrig;
|
||||
}
|
||||
|
||||
pSelf->SendChatTarget(ClientID, pLine);
|
||||
|
||||
ReentryGuard--;
|
||||
}
|
||||
|
||||
bool CGameContext::PlayerCollision()
|
||||
{
|
||||
float Temp;
|
||||
|
|
|
@ -456,8 +456,6 @@ public:
|
|||
inline bool IsSpecVote() const { return m_VoteType == VOTE_TYPE_SPECTATE; }
|
||||
|
||||
void SendRecord(int ClientID);
|
||||
static void SendChatResponse(const char *pLine, void *pUser, ColorRGBA PrintColor = {1, 1, 1, 1});
|
||||
static void SendChatResponseAll(const char *pLine, void *pUser);
|
||||
virtual void OnSetAuthed(int ClientID, int Level);
|
||||
virtual bool PlayerCollision();
|
||||
virtual bool PlayerHooking();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
#include <base/logger.h>
|
||||
#include <base/system.h>
|
||||
|
||||
#include <engine/config.h>
|
||||
|
@ -326,7 +327,7 @@ int main(int argc, const char **argv)
|
|||
|
||||
cmdline_fix(&argc, &argv);
|
||||
|
||||
dbg_logger_stdout();
|
||||
log_set_global_logger_default();
|
||||
net_init();
|
||||
|
||||
mem_copy(m_CountData.m_Header, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT));
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "test.h"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <base/logger.h>
|
||||
#include <base/system.h>
|
||||
#include <engine/storage.h>
|
||||
|
||||
|
@ -114,6 +115,7 @@ CTestInfo::~CTestInfo()
|
|||
int main(int argc, const char **argv)
|
||||
{
|
||||
cmdline_fix(&argc, &argv);
|
||||
log_set_global_logger_default();
|
||||
::testing::InitGoogleTest(&argc, const_cast<char **>(argv));
|
||||
net_init();
|
||||
if(secure_random_init())
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <base/logger.h>
|
||||
#include <base/system.h>
|
||||
#include <engine/storage.h>
|
||||
|
||||
|
@ -47,7 +48,7 @@ static int ListdirCallback(const char *pItemName, int IsDir, int StorageType, vo
|
|||
int main(int argc, const char **argv) // NOLINT(misc-definitions-in-headers)
|
||||
{
|
||||
cmdline_fix(&argc, &argv);
|
||||
dbg_logger_stdout();
|
||||
log_set_global_logger_default();
|
||||
IStorage *pStorage = CreateLocalStorage();
|
||||
if(argc == 1)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
#include <base/logger.h>
|
||||
#include <base/system.h>
|
||||
|
||||
#include <array> // std::size
|
||||
|
@ -209,8 +210,8 @@ void Run(unsigned short Port, NETADDR Dest)
|
|||
int main(int argc, const char **argv)
|
||||
{
|
||||
cmdline_fix(&argc, &argv);
|
||||
log_set_global_logger_default();
|
||||
NETADDR Addr = {NETTYPE_IPV4, {127, 0, 0, 1}, 8303};
|
||||
dbg_logger_stdout();
|
||||
Run(8302, Addr);
|
||||
cmdline_free(argc, argv);
|
||||
return 0;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
#include <base/logger.h>
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
#include <engine/shared/image_manipulation.h>
|
||||
|
@ -74,7 +75,7 @@ int DilateFile(const char *pFilename)
|
|||
int main(int argc, const char **argv)
|
||||
{
|
||||
cmdline_fix(&argc, &argv);
|
||||
dbg_logger_stdout();
|
||||
log_set_global_logger_default();
|
||||
if(argc == 1)
|
||||
{
|
||||
dbg_msg("usage", "%s FILE1 [ FILE2... ]", argv[0]);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <base/logger.h>
|
||||
#include <base/system.h>
|
||||
#include <base/tl/array.h>
|
||||
#include <engine/shared/datafile.h>
|
||||
|
@ -74,7 +75,7 @@ void CreateEmptyMap(IStorage *pStorage)
|
|||
int main(int argc, const char **argv)
|
||||
{
|
||||
cmdline_fix(&argc, &argv);
|
||||
dbg_logger_stdout();
|
||||
log_set_global_logger_default();
|
||||
IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_SERVER, argc, argv);
|
||||
CreateEmptyMap(pStorage);
|
||||
cmdline_free(argc, argv);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* (c) DDNet developers. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
|
||||
#include <base/logger.h>
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
#include <engine/graphics.h>
|
||||
|
@ -136,7 +137,7 @@ void *ReplaceImageItem(void *pItem, int Type, CMapItemImage *pNewImgItem)
|
|||
int main(int argc, const char **argv)
|
||||
{
|
||||
cmdline_fix(&argc, &argv);
|
||||
dbg_logger_stdout();
|
||||
log_set_global_logger_default();
|
||||
|
||||
IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_BASIC, argc, argv);
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <base/logger.h>
|
||||
#include <base/system.h>
|
||||
#include <engine/shared/datafile.h>
|
||||
#include <engine/storage.h>
|
||||
|
@ -101,8 +102,14 @@ bool Process(IStorage *pStorage, const char **pMapNames)
|
|||
int main(int argc, const char *argv[])
|
||||
{
|
||||
cmdline_fix(&argc, &argv);
|
||||
dbg_logger_stdout();
|
||||
dbg_logger_file("map_diff.txt");
|
||||
std::vector<std::shared_ptr<ILogger>> apLoggers;
|
||||
apLoggers.push_back(std::shared_ptr<ILogger>(log_logger_stdout()));
|
||||
IOHANDLE LogFile = io_open("map_diff.txt", IOFLAG_WRITE);
|
||||
if(LogFile)
|
||||
{
|
||||
apLoggers.push_back(std::shared_ptr<ILogger>(log_logger_file(LogFile)));
|
||||
}
|
||||
log_set_global_logger(log_logger_collection(std::move(apLoggers)).release());
|
||||
|
||||
IStorage *pStorage = CreateLocalStorage();
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Adapted from TWMapImagesRecovery by Tardo: https://github.com/Tardo/TWMapImagesRecovery
|
||||
#include <base/logger.h>
|
||||
#include <base/system.h>
|
||||
#include <engine/shared/datafile.h>
|
||||
#include <engine/storage.h>
|
||||
|
@ -96,7 +97,7 @@ bool Process(IStorage *pStorage, const char *pMapName, const char *pPathSave)
|
|||
int main(int argc, const char *argv[])
|
||||
{
|
||||
cmdline_fix(&argc, &argv);
|
||||
dbg_logger_stdout();
|
||||
log_set_global_logger_default();
|
||||
|
||||
char aMap[512];
|
||||
char aDir[512];
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <algorithm>
|
||||
#include <base/logger.h>
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
#include <cstdint>
|
||||
|
@ -79,7 +80,7 @@ void GetImageSHA256(uint8_t *pImgBuff, int ImgSize, int Width, int Height, char
|
|||
int main(int argc, const char **argv)
|
||||
{
|
||||
cmdline_fix(&argc, &argv);
|
||||
dbg_logger_stdout();
|
||||
log_set_global_logger_default();
|
||||
|
||||
IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_BASIC, argc, argv);
|
||||
int ID = 0, Type = 0, Size;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* (c) DDNet developers. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
|
||||
#include <base/logger.h>
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
#include <engine/graphics.h>
|
||||
|
@ -111,7 +112,7 @@ void *ReplaceImageItem(void *pItem, int Type, const char *pImgName, const char *
|
|||
int main(int argc, const char **argv)
|
||||
{
|
||||
cmdline_fix(&argc, &argv);
|
||||
dbg_logger_stdout();
|
||||
log_set_global_logger_default();
|
||||
|
||||
IStorage *pStorage = CreateStorage(IStorage::STORAGETYPE_BASIC, argc, argv);
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#include <base/logger.h>
|
||||
#include <base/system.h>
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
cmdline_fix(&argc, &argv);
|
||||
dbg_logger_stdout();
|
||||
log_set_global_logger_default();
|
||||
if(argc < 1 + 2)
|
||||
{
|
||||
dbg_msg("usage", "%s STR1 STR2", argv[0] ? argv[0] : "unicode_confusables");
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#include <base/logger.h>
|
||||
#include <engine/shared/uuid_manager.h>
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
cmdline_fix(&argc, &argv);
|
||||
dbg_logger_stdout();
|
||||
log_set_global_logger_default();
|
||||
if(argc != 2)
|
||||
{
|
||||
dbg_msg("usage", "uuid <NAME>");
|
||||
|
|
Loading…
Reference in a new issue