diff --git a/CMakeLists.txt b/CMakeLists.txt
index e5a7ebe7c..eddf5bb56 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1579,6 +1579,8 @@ set_src(ENGINE_INTERFACE GLOB src/engine
warning.h
)
set_src(ENGINE_SHARED GLOB src/engine/shared
+ assertion_logger.cpp
+ assertion_logger.h
compression.cpp
compression.h
config.cpp
diff --git a/src/base/system.cpp b/src/base/system.cpp
index e0b7651fb..56e7fe41b 100644
--- a/src/base/system.cpp
+++ b/src/base/system.cpp
@@ -132,12 +132,13 @@ IOHANDLE io_current_exe()
#endif
}
-typedef struct
+struct DBG_LOGGER_DATA
{
DBG_LOGGER logger;
DBG_LOGGER_FINISH finish;
+ DBG_LOGGER_ASSERTION on_assert = nullptr;
void *user;
-} DBG_LOGGER_DATA;
+};
static DBG_LOGGER_DATA loggers[16];
static int has_stdout_logger = 0;
@@ -181,11 +182,21 @@ static NETSOCKET_INTERNAL invalid_socket = {NETTYPE_INVALID, -1, -1, -1};
#define AF_WEBSOCKET_INET (0xee)
+static void dbg_assert_notify_loggers()
+{
+ for(int i = 0; i < num_loggers; i++)
+ {
+ if(loggers[i].on_assert)
+ loggers[i].on_assert(loggers[i].user);
+ }
+}
+
void dbg_assert_imp(const char *filename, int line, int test, const char *msg)
{
if(!test)
{
dbg_msg("assert", "%s(%d): %s", filename, line, msg);
+ dbg_assert_notify_loggers();
dbg_break();
}
}
@@ -337,6 +348,13 @@ void dbg_logger(DBG_LOGGER logger, DBG_LOGGER_FINISH finish, void *user)
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)
diff --git a/src/base/system.h b/src/base/system.h
index af2ed2f66..9ff16fb52 100644
--- a/src/base/system.h
+++ b/src/base/system.h
@@ -1849,6 +1849,9 @@ 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);
diff --git a/src/engine/shared/assertion_logger.cpp b/src/engine/shared/assertion_logger.cpp
new file mode 100644
index 000000000..7077bbcc3
--- /dev/null
+++ b/src/engine/shared/assertion_logger.cpp
@@ -0,0 +1,54 @@
+#include "assertion_logger.h"
+
+#include
+#include
+
+void CAssertionLogger::DbgLogger(const char *pLine, void *pUser)
+{
+ ((CAssertionLogger *)pUser)->DbgLogger(pLine);
+}
+
+void CAssertionLogger::DbgLoggerAssertion(void *pUser)
+{
+ ((CAssertionLogger *)pUser)->DbgLoggerAssertion();
+}
+
+void CAssertionLogger::DbgLogger(const char *pLine)
+{
+ std::unique_lock Lock(m_DbgMessageMutex);
+
+ SDebugMessageItem *pMsgItem = (SDebugMessageItem *)m_DbgMessages.Allocate(sizeof(SDebugMessageItem));
+ str_copy(pMsgItem->m_aMessage, pLine, std::size(pMsgItem->m_aMessage));
+}
+
+void CAssertionLogger::DbgLoggerAssertion()
+{
+ char aAssertLogFile[IO_MAX_PATH_LENGTH];
+ char aDate[64];
+ str_timestamp(aDate, sizeof(aDate));
+ str_format(aAssertLogFile, std::size(aAssertLogFile), "%s%s_assert_log_%d_%s.txt", m_aAssertLogPath, m_aGameName, pid(), aDate);
+ std::unique_lock Lock(m_DbgMessageMutex);
+ IOHANDLE FileHandle = io_open(aAssertLogFile, IOFLAG_WRITE);
+ if(FileHandle)
+ {
+ auto *pIt = m_DbgMessages.First();
+ while(pIt)
+ {
+ io_write(FileHandle, pIt->m_aMessage, str_length(pIt->m_aMessage));
+ io_write(FileHandle, "\n", 1);
+
+ pIt = m_DbgMessages.Next(pIt);
+ }
+
+ io_sync(FileHandle);
+ io_close(FileHandle);
+ }
+}
+
+void CAssertionLogger::Init(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);
+}
diff --git a/src/engine/shared/assertion_logger.h b/src/engine/shared/assertion_logger.h
new file mode 100644
index 000000000..72c1f68dd
--- /dev/null
+++ b/src/engine/shared/assertion_logger.h
@@ -0,0 +1,36 @@
+#ifndef ENGINE_SHARED_ASSERTION_LOGGER_H
+#define ENGINE_SHARED_ASSERTION_LOGGER_H
+
+#include
+
+#include
+#include
+
+#include
+
+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 m_DbgMessages;
+
+ char m_aAssertLogPath[IO_MAX_PATH_LENGTH];
+ char m_aGameName[256];
+
+public:
+ void Init(const char *pAssertLogPath, const char *pGameName);
+};
+
+#endif
diff --git a/src/engine/shared/engine.cpp b/src/engine/shared/engine.cpp
index 0bfeed0d3..17f768d59 100644
--- a/src/engine/shared/engine.cpp
+++ b/src/engine/shared/engine.cpp
@@ -9,6 +9,8 @@
#include
#include
+#include
+
CHostLookup::CHostLookup() = default;
CHostLookup::CHostLookup(const char *pHostname, int Nettype)
@@ -29,6 +31,10 @@ public:
IStorage *m_pStorage;
bool m_Logging;
+ CAssertionLogger m_AssertionLogger;
+
+ char m_aAppName[256];
+
static void Con_DbgLognetwork(IConsole::IResult *pResult, void *pUserData)
{
CEngine *pEngine = static_cast(pUserData);
@@ -53,6 +59,7 @@ public:
CEngine(bool Test, const char *pAppname, bool Silent, int Jobs)
{
+ str_copy(m_aAppName, pAppname, std::size(m_aAppName));
if(!Test)
{
if(!Silent)
@@ -87,6 +94,10 @@ public:
if(!m_pConsole || !m_pStorage)
return;
+ 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");
}