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:
bors[bot] 2022-04-29 14:17:07 +00:00 committed by GitHub
commit ca4b6222da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 1180 additions and 540 deletions

View file

@ -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
View 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
View 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
View 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

View file

@ -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)

View file

@ -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;

View file

@ -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...");

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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;
}
for(int i = 0; i < MAX_CLIENTS; i++)
if(ClientID == -1)
{
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);
for(int i = 0; i < MAX_CLIENTS; i++)
{
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);
Console()->ExecuteLineFlag(pCmd, CFGFLAG_SERVER, ClientID);
{
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...");

View file

@ -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);

View file

@ -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));
}

View file

@ -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

View file

@ -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)
{
// if the color is pure white, use default terminal color
if(mem_comp(&PrintColor, &gs_ConsoleDefaultColor, sizeof(ColorRGBA)) == 0)
set_console_msg_color(NULL);
else
set_console_msg_color(&PrintColor);
log_log_color(LogLevel, ColorToLogColor(PrintColor), pFrom, "%s", pStr);
}
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)
else
{
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;

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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); }

View file

@ -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(); }

View file

@ -36,6 +36,7 @@ protected:
* Get the kernel interface.
*/
class IKernel *Kernel() const;
class IEngine *Engine() const;
/**
* Get the graphics interface.
*/

View file

@ -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();

View file

@ -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);

View file

@ -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);
Console()->ExecuteLine(pMsg->m_pMessage + 1, ClientID, false);
{
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;

View file

@ -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();

View file

@ -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));

View file

@ -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())

View file

@ -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)
{

View file

@ -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;

View file

@ -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]);

View file

@ -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);

View file

@ -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);

View file

@ -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();

View file

@ -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];

View file

@ -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;

View file

@ -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);

View file

@ -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");

View file

@ -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>");