489 lines
12 KiB
C++
Raw Normal View History

2022-02-27 12:53:44 +00:00
#include <STDInclude.hpp>
#include "Console.hpp"
#include "Events.hpp"
2017-01-19 22:23:59 +01:00
namespace Components
{
using namespace Utils::String;
2017-01-19 22:23:59 +01:00
std::mutex Logger::MessageMutex;
std::vector<std::string> Logger::MessageQueue;
2023-04-03 13:49:37 +02:00
std::recursive_mutex Logger::LoggingMutex;
2017-01-19 22:23:59 +01:00
std::vector<Network::Address> Logger::LoggingAddresses[2];
2024-01-12 09:53:58 +01:00
Dvar::Var Logger::IW4x_one_log;
Dvar::Var Logger::IW4x_fail2ban_location;
2022-12-28 13:37:03 +01:00
void(*Logger::PipeCallback)(const std::string&) = nullptr;;
2017-01-19 22:23:59 +01:00
bool Logger::IsConsoleReady()
{
return (IsWindow(Console::GetWindow()) != FALSE || (Dedicated::IsEnabled() && !Flags::HasFlag("console")));
2017-01-19 22:23:59 +01:00
}
2022-07-22 11:27:38 +02:00
void Logger::Print_Stub(const int channel, const char* message, ...)
2017-01-19 22:23:59 +01:00
{
char buf[4096]{};
2017-01-19 22:23:59 +01:00
2022-06-12 23:07:53 +02:00
va_list va;
va_start(va, message);
vsnprintf_s(buf, _TRUNCATE, message, va);
2022-06-12 23:07:53 +02:00
va_end(va);
2017-01-19 22:23:59 +01:00
MessagePrint(channel, std::string{ buf });
2017-01-19 22:23:59 +01:00
}
2022-06-12 23:07:53 +02:00
void Logger::MessagePrint(const int channel, const std::string& msg)
2017-01-19 22:23:59 +01:00
{
static const auto shouldPrint = []() -> bool
{
return Flags::HasFlag("stdout") || Loader::IsPerformingUnitTests();
}();
if (shouldPrint)
{
(void)std::fputs(msg.data(), stdout);
(void)std::fflush(stdout);
return;
}
2017-01-19 22:23:59 +01:00
#ifdef _DEBUG
if (!IsConsoleReady())
{
OutputDebugStringA(msg.data());
}
#endif
2017-01-19 22:23:59 +01:00
if (!Game::Sys_IsMainThread())
{
EnqueueMessage(msg);
}
else
{
Game::Com_PrintMessage(channel, msg.data(), 0);
}
2017-01-19 22:23:59 +01:00
}
2022-12-28 11:03:14 +01:00
void Logger::DebugInternal(const std::string_view& fmt, std::format_args&& args, [[maybe_unused]] const std::source_location& loc)
2017-01-19 22:23:59 +01:00
{
#ifdef LOGGER_TRACE
2022-07-02 11:41:59 +02:00
const auto msg = std::vformat(fmt, args);
const auto out = std::format("Debug:\n {}\nFile: {}\nLine: {}\n", msg, loc.file_name(), loc.line());
#else
const auto msg = std::vformat(fmt, args);
const auto out = std::format("^2{}\n", msg);
#endif
2017-01-19 22:23:59 +01:00
2022-11-29 14:18:10 +00:00
MessagePrint(Game::CON_CHANNEL_DONT_FILTER, out);
2017-01-19 22:23:59 +01:00
}
2022-12-28 11:03:14 +01:00
void Logger::PrintInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args)
2017-01-19 22:23:59 +01:00
{
2022-06-12 23:07:53 +02:00
const auto msg = std::vformat(fmt, args);
2022-11-29 14:18:10 +00:00
MessagePrint(channel, msg);
2017-01-19 22:23:59 +01:00
}
2022-12-28 11:03:14 +01:00
void Logger::ErrorInternal(const Game::errorParm_t error, const std::string_view& fmt, std::format_args&& args)
2017-01-19 22:23:59 +01:00
{
2023-02-05 11:29:34 +00:00
const auto msg = std::vformat(fmt, args);
2022-06-12 23:07:53 +02:00
#ifdef _DEBUG
if (IsDebuggerPresent()) __debugbreak();
#endif
Game::Com_Error(error, "%s", msg.data());
2017-01-19 22:23:59 +01:00
}
2022-12-28 11:03:14 +01:00
void Logger::PrintErrorInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args)
2017-01-19 22:23:59 +01:00
{
2022-06-12 23:07:53 +02:00
const auto msg = "^1Error: " + std::vformat(fmt, args);
++(*Game::com_errorPrintsCount);
2022-11-29 14:18:10 +00:00
MessagePrint(channel, msg);
2017-01-19 22:23:59 +01:00
if (Game::cls->uiStarted && (*Game::com_fixedConsolePosition == 0))
2022-06-12 23:07:53 +02:00
{
Game::CL_ConsoleFixPosition();
}
}
2022-04-16 10:01:25 +02:00
2022-12-28 11:03:14 +01:00
void Logger::WarningInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args)
2022-06-12 23:07:53 +02:00
{
const auto msg = "^3" + std::vformat(fmt, args);
2017-01-19 22:23:59 +01:00
2022-11-29 14:18:10 +00:00
MessagePrint(channel, msg);
2017-01-19 22:23:59 +01:00
}
2024-01-12 09:53:58 +01:00
void Logger::PrintFail2BanInternal(const std::string_view& fmt, std::format_args&& args)
{
static const auto shouldPrint = []() -> bool
{
return Flags::HasFlag("fail2ban");
}();
2024-01-12 09:53:58 +01:00
if (!shouldPrint)
{
return;
}
2024-01-12 09:53:58 +01:00
auto msg = std::vformat(fmt, args);
2024-01-12 09:53:58 +01:00
static auto log_next_time_stamp = true;
if (log_next_time_stamp)
{
auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
// Convert to local time
std::tm timeInfo = *std::localtime(&now);
2024-01-12 09:53:58 +01:00
std::ostringstream ss;
ss << std::put_time(&timeInfo, "%Y-%m-%d %H:%M:%S ");
2024-01-12 09:53:58 +01:00
msg.insert(0, ss.str());
}
2024-01-12 09:53:58 +01:00
log_next_time_stamp = (msg.find('\n') != std::string::npos);
2024-01-12 09:53:58 +01:00
Utils::IO::WriteFile(IW4x_fail2ban_location.get<std::string>(), msg, true);
2024-01-12 09:53:58 +01:00
}
2017-01-19 22:23:59 +01:00
void Logger::Frame()
{
2022-11-29 14:18:10 +00:00
std::unique_lock _(MessageMutex);
2017-01-19 22:23:59 +01:00
2022-11-29 14:18:10 +00:00
for (auto i = MessageQueue.begin(); i != MessageQueue.end();)
2017-01-19 22:23:59 +01:00
{
Game::Com_PrintMessage(Game::CON_CHANNEL_DONT_FILTER, i->data(), 0);
2017-01-19 22:23:59 +01:00
2023-05-01 22:50:42 +01:00
#ifdef _DEBUG
2022-11-29 14:18:10 +00:00
if (!IsConsoleReady())
2017-01-19 22:23:59 +01:00
{
OutputDebugStringA(i->data());
2017-01-19 22:23:59 +01:00
}
2023-05-01 22:50:42 +01:00
#endif
2017-01-19 22:23:59 +01:00
2022-11-29 14:18:10 +00:00
i = MessageQueue.erase(i);
}
2017-01-19 22:23:59 +01:00
}
2022-12-28 13:37:03 +01:00
void Logger::PipeOutput(void(*callback)(const std::string&))
2017-01-19 22:23:59 +01:00
{
2022-11-29 14:18:10 +00:00
PipeCallback = callback;
2017-01-19 22:23:59 +01:00
}
void Logger::PrintMessagePipe(const char* data)
{
2022-11-29 14:18:10 +00:00
if (PipeCallback)
2017-01-19 22:23:59 +01:00
{
2022-11-29 14:18:10 +00:00
PipeCallback(data);
2017-01-19 22:23:59 +01:00
}
}
void Logger::NetworkLog(const char* data, bool gLog)
{
if (!data)
{
return;
}
2017-01-19 22:23:59 +01:00
std::unique_lock lock(LoggingMutex);
2022-11-29 14:18:10 +00:00
for (const auto& addr : LoggingAddresses[gLog & 1])
2017-01-19 22:23:59 +01:00
{
Network::SendCommand(addr, "print", data);
2017-01-19 22:23:59 +01:00
}
}
2022-07-22 11:27:38 +02:00
void Logger::G_LogPrintf_Hk(const char* fmt, ...)
2017-01-19 22:23:59 +01:00
{
2022-07-22 11:27:38 +02:00
char string[1024]{};
char string2[1024]{};
2017-02-01 13:44:25 +01:00
va_list ap;
va_start(ap, fmt);
vsnprintf_s(string2, _TRUNCATE, fmt, ap);
va_end(ap);
2017-01-19 22:23:59 +01:00
2022-07-22 11:27:38 +02:00
const auto time = Game::level->time / 1000;
const auto len = sprintf_s(string, "%3i:%i%i %s", time / 60, time % 60 / 10, time % 60 % 10, string2);
if (Game::level->logFile)
{
Game::FS_Write(string, len, Game::level->logFile);
2017-01-19 22:23:59 +01:00
}
// Allow the network log to run even if logFile was not opened
2022-11-29 14:18:10 +00:00
NetworkLog(string, true);
2017-01-19 22:23:59 +01:00
}
2022-07-22 11:27:38 +02:00
__declspec(naked) void Logger::PrintMessage_Stub()
2017-01-19 22:23:59 +01:00
{
__asm
{
2022-11-29 14:18:10 +00:00
mov eax, PipeCallback
2017-01-19 22:23:59 +01:00
test eax, eax
jz returnPrint
2017-02-01 13:44:25 +01:00
pushad
push[esp + 28h]
2022-11-29 14:18:10 +00:00
call PrintMessagePipe
2017-01-19 22:23:59 +01:00
add esp, 4h
2017-02-01 13:44:25 +01:00
popad
ret
2017-01-19 22:23:59 +01:00
returnPrint :
2017-02-01 13:44:25 +01:00
pushad
push 0 // gLog
push[esp + 2Ch] // data
call NetworkLog
add esp, 8h
popad
2017-01-19 22:23:59 +01:00
push esi
mov esi, [esp + 0Ch]
2017-01-19 22:23:59 +01:00
push 4AA835h
ret
2017-01-19 22:23:59 +01:00
}
}
2018-12-17 14:29:18 +01:00
void Logger::EnqueueMessage(const std::string& message)
2017-01-19 22:23:59 +01:00
{
2022-11-29 14:18:10 +00:00
std::unique_lock _(MessageMutex);
MessageQueue.push_back(message);
2017-01-19 22:23:59 +01:00
}
2017-02-25 01:17:11 +01:00
void Logger::RedirectOSPath(const char* file, char* folder)
{
2023-04-03 13:49:37 +02:00
const auto* g_log = (*Game::g_log) ? (*Game::g_log)->current.string : "";
if (g_log) // This can be null - has happened before
2017-02-25 01:17:11 +01:00
{
if (std::strcmp(g_log, file) == 0)
2017-02-25 01:17:11 +01:00
{
if (std::strcmp(folder, "userraw") != 0)
2017-02-25 01:17:11 +01:00
{
if (IW4x_one_log.get<bool>())
{
strncpy_s(folder, 256, "userraw", _TRUNCATE);
}
2017-02-25 01:17:11 +01:00
}
}
}
}
2022-07-22 11:27:38 +02:00
__declspec(naked) void Logger::BuildOSPath_Stub()
2017-02-25 01:17:11 +01:00
{
__asm
{
pushad
push[esp + 20h + 8h]
push[esp + 20h + 10h]
2022-11-29 14:18:10 +00:00
call RedirectOSPath
2017-02-25 01:17:11 +01:00
add esp, 8h
popad
mov eax, [esp + 8h]
push ebp
push esi
mov esi, [esp + 0Ch]
push 64213Fh
retn
}
}
void Logger::LSP_LogString_Stub([[maybe_unused]] int localControllerIndex, const char* string)
{
NetworkLog(string, false);
}
void Logger::LSP_LogStringAboutUser_Stub([[maybe_unused]] int localControllerIndex, std::uint64_t xuid, const char* string)
{
NetworkLog(VA("%" PRIx64 ";%s", xuid, string), false);
}
2022-06-16 16:15:26 +02:00
void Logger::AddServerCommands()
2017-01-19 22:23:59 +01:00
{
2023-05-06 14:20:56 +01:00
Command::AddSV("log_add", [](const Command::Params* params)
2022-06-16 16:15:26 +02:00
{
if (params->size() < 2) return;
2017-01-19 22:23:59 +01:00
std::unique_lock lock(LoggingMutex);
2017-01-19 22:23:59 +01:00
Network::Address addr(params->get(1));
if (std::ranges::find(LoggingAddresses[0], addr) == LoggingAddresses[0].end())
{
LoggingAddresses[0].push_back(addr);
}
});
Command::AddSV("log_del", [](const Command::Params* params)
2017-01-19 22:23:59 +01:00
{
if (params->size() < 2) return;
std::unique_lock lock(LoggingMutex);
2017-01-19 22:23:59 +01:00
const auto num = std::atoi(params->get(1));
if (!std::strcmp(VA("%i", num), params->get(1)) && static_cast<unsigned int>(num) < LoggingAddresses[0].size())
2017-01-19 22:23:59 +01:00
{
auto addr = Logger::LoggingAddresses[0].begin() + num;
Print("Address {} removed\n", addr->getString());
LoggingAddresses[0].erase(addr);
2017-01-19 22:23:59 +01:00
}
else
{
Network::Address addr(params->get(1));
if (const auto i = std::ranges::find(LoggingAddresses[0], addr); i != LoggingAddresses[0].end())
{
LoggingAddresses[0].erase(i);
Print("Address {} removed\n", addr.getString());
}
else
{
Print("Address {} not found!\n", addr.getString());
}
2017-01-19 22:23:59 +01:00
}
});
2017-01-19 22:23:59 +01:00
2023-05-06 14:20:56 +01:00
Command::AddSV("log_list", []([[maybe_unused]] const Command::Params* params)
{
Print("# ID: Address\n");
Print("-------------\n");
2022-06-16 16:15:26 +02:00
std::unique_lock lock(LoggingMutex);
for (unsigned int i = 0; i < LoggingAddresses[0].size(); ++i)
{
Print("#{:03d}: {}\n", i, LoggingAddresses[0][i].getString());
}
});
2017-01-19 22:23:59 +01:00
2023-05-06 14:20:56 +01:00
Command::AddSV("g_log_add", [](const Command::Params* params)
{
if (params->size() < 2) return;
2017-01-19 22:23:59 +01:00
std::unique_lock lock(LoggingMutex);
2017-01-19 22:23:59 +01:00
const Network::Address addr(params->get(1));
if (std::ranges::find(LoggingAddresses[1], addr) == LoggingAddresses[1].end())
{
LoggingAddresses[1].push_back(addr);
}
});
2017-01-19 22:23:59 +01:00
2023-05-06 14:20:56 +01:00
Command::AddSV("g_log_del", [](const Command::Params* params)
{
if (params->size() < 2) return;
2017-01-19 22:23:59 +01:00
std::unique_lock lock(LoggingMutex);
const auto num = std::atoi(params->get(1));
if (!std::strcmp(VA("%i", num), params->get(1)) && static_cast<unsigned int>(num) < LoggingAddresses[1].size())
2017-01-19 22:23:59 +01:00
{
const auto addr = LoggingAddresses[1].begin() + num;
Print("Address {} removed\n", addr->getString());
LoggingAddresses[1].erase(addr);
2017-01-19 22:23:59 +01:00
}
else
{
const Network::Address addr(params->get(1));
const auto i = std::ranges::find(LoggingAddresses[1].begin(), LoggingAddresses[1].end(), addr);
if (i != LoggingAddresses[1].end())
{
LoggingAddresses[1].erase(i);
Print("Address {} removed\n", addr.getString());
}
else
{
Print("Address {} not found!\n", addr.getString());
}
2017-01-19 22:23:59 +01:00
}
});
2017-01-19 22:23:59 +01:00
2023-05-06 14:20:56 +01:00
Command::AddSV("g_log_list", []([[maybe_unused]] const Command::Params* params)
2017-01-19 22:23:59 +01:00
{
Print("# ID: Address\n");
Print("-------------\n");
std::unique_lock lock(LoggingMutex);
for (std::size_t i = 0; i < LoggingAddresses[1].size(); ++i)
{
Print("#{:03d}: {}\n", i, LoggingAddresses[1][i].getString());
}
});
2022-06-16 16:15:26 +02:00
}
2017-01-19 22:23:59 +01:00
void PrintAliasError(Game::conChannel_t channel, const char* originalMsg, const char* soundName, const char* lastErrorStr)
{
// We add a bit more info and we clear the sound stream when it happens
// to avoid spamming the error
const auto newMsg = std::format("{}Make sure you have the 'miles' folder in your game directory! Otherwise MP3 and other codecs will be unavailable.\n", originalMsg);
Game::Com_PrintError(channel, newMsg.c_str(), soundName, lastErrorStr);
for (size_t i = 0; i < ARRAYSIZE(Game::milesGlobal->streamReadInfo); i++)
{
if (0 == std::strncmp(Game::milesGlobal->streamReadInfo[i].path, soundName, ARRAYSIZE(Game::milesGlobal->streamReadInfo[i].path)))
{
Game::milesGlobal->streamReadInfo[i].path[0] = '\x00'; // This kills it and make sure it doesn't get played again for now
break;
}
}
}
2022-06-16 16:15:26 +02:00
Logger::Logger()
{
// Print sound aliases errors
if (!Dedicated::IsEnabled())
{
Utils::Hook(0x64BA67, PrintAliasError, HOOK_CALL).install()->quick();
}
2022-11-29 14:18:10 +00:00
Utils::Hook(0x642139, BuildOSPath_Stub, HOOK_JUMP).install()->quick();
2022-06-16 16:15:26 +02:00
2022-11-29 14:18:10 +00:00
Scheduler::Loop(Frame, Scheduler::Pipeline::SERVER);
2022-06-16 16:15:26 +02:00
2022-11-29 14:18:10 +00:00
Utils::Hook(Game::G_LogPrintf, G_LogPrintf_Hk, HOOK_JUMP).install()->quick();
Utils::Hook(Game::Com_PrintMessage, PrintMessage_Stub, HOOK_JUMP).install()->quick();
2022-06-16 16:15:26 +02:00
Utils::Hook(0x5F67AE, LSP_LogString_Stub, HOOK_CALL).install()->quick(); // Scr_LogString
Utils::Hook(0x5F67EE, LSP_LogStringAboutUser_Stub, HOOK_CALL).install()->quick(); // ScrCmd_LogString_Stub
2022-06-16 16:15:26 +02:00
if (Loader::IsPerformingUnitTests())
{
2022-11-29 14:18:10 +00:00
Utils::Hook(Game::Com_Printf, Print_Stub, HOOK_JUMP).install()->quick();
2022-06-16 16:15:26 +02:00
}
2022-11-29 14:18:10 +00:00
Events::OnSVInit(AddServerCommands);
2024-01-12 09:53:58 +01:00
Events::OnDvarInit([]
{
IW4x_one_log = Dvar::Register<bool>("iw4x_onelog", false, Game::DVAR_LATCH, "Only write the game log to the 'userraw' OS folder");
IW4x_fail2ban_location = Dvar::Register<const char*>("iw4x_fail2ban_location", "/var/log/iw4x.log", Game::DVAR_NONE, "Fail2Ban logfile location");
});
2017-01-19 22:23:59 +01:00
}
Logger::~Logger()
{
std::unique_lock lock_logging(LoggingMutex);
2022-11-29 14:18:10 +00:00
LoggingAddresses[0].clear();
LoggingAddresses[1].clear();
2017-01-19 22:23:59 +01:00
std::unique_lock lock_message(MessageMutex);
2022-11-29 14:18:10 +00:00
MessageQueue.clear();
2017-01-19 22:23:59 +01:00
// Flush the console log
if (*Game::logfile)
2017-01-19 22:23:59 +01:00
{
Game::FS_FCloseFile(*Game::logfile);
2017-01-19 22:23:59 +01:00
}
}
}