diff --git a/CMakeLists.txt b/CMakeLists.txt index c2d14c2..2561911 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build." FORCE) endif() -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_XCODE_GENERATE_SCHEME ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -114,6 +114,11 @@ set(ENGINE_SOURCES src/scene_manager.cpp src/filepacker.cpp ) +if (WIN32) + list(APPEND ENGINE_SOURCES src/platform/windows/FatalHandlerWindows.cpp) +elseif(UNIX) + list(APPEND ENGINE_SOURCES src/platform/posix/FatalHandlerPosix.cpp) +endif() add_library(IsoEngine ${ENGINE_SOURCES}) @@ -147,7 +152,7 @@ target_link_libraries(rlImGui PRIVATE raylib ImGui) target_link_libraries(IsoEngine PRIVATE ImGui rlImGui) # Example executables (client/server) -if(BUILD_CLIENT OR BUILD_SANDBOX) +if(BUILD_CLIENT) # Client executable set(CLIENT_SOURCE "${CMAKE_CURRENT_LIST_DIR}/sandbox/client.cpp") add_executable(client ${CLIENT_SOURCE}) @@ -186,6 +191,7 @@ endif() # Build Configuration Optimizations if(CMAKE_BUILD_TYPE STREQUAL "Debug") message(STATUS "Configuring Debug build: No optimizations applied.") + target_compile_definitions(IsoEngine PRIVATE ENABLE_LOGGING) elseif(CMAKE_BUILD_TYPE STREQUAL "Release") message(STATUS "Configuring Release build: Applying moderate optimizations.") target_compile_options(IsoEngine PRIVATE -O2) diff --git a/sandbox/client.cpp b/sandbox/client.cpp index 280c3e2..cd7279e 100644 --- a/sandbox/client.cpp +++ b/sandbox/client.cpp @@ -1,45 +1,99 @@ #include "IsoEngine.h" -#include "components/transform_component.h" -#include "components/input_component.h" -#include +#include "components.h" + +#include + +#define ENABLE_LOGGING + +enum class MenuState { + MAIN_MENU, + OPTIONS, + QUIT +}; int main() { - IsoEngine engine(800, 600, nullptr); + int screenWidth = 800; + int screenHeight = 600; + IsoEngine engine(screenWidth, screenHeight, nullptr); engine.Initialize(); SceneManager& sceneManager = engine.GetSceneManager(); - entt::entity gameScene = sceneManager.CreateScene(); - sceneManager.SetActiveScene(gameScene); + entt::entity mainMenuScene = sceneManager.CreateScene(); - entt::entity player = engine.GetRegistry().create(); - engine.GetRegistry().emplace(player, 400.0f, 300.0f, 1.0f, 1.0f, 0.0f); + auto& registry = engine.GetRegistry(); - auto& inputComponent = engine.GetRegistry().emplace(player); + // Button dimensions + float buttonWidth = 200.0f; + float buttonHeight = 50.0f; + float spacing = 20.0f; // Vertical spacing between buttons - inputComponent.BindKey(InputAction::MOVE_UP, KEY_W); - inputComponent.BindKey(InputAction::MOVE_DOWN, KEY_S); - inputComponent.BindKey(InputAction::MOVE_LEFT, KEY_A); - inputComponent.BindKey(InputAction::MOVE_RIGHT, KEY_D); + // Center X position + float centerX = screenWidth / 2 - buttonWidth / 2; - inputComponent.SetActionCallback(InputAction::MOVE_UP, [&]() { - auto& transform = engine.GetRegistry().get(player); - transform.y -= 5.0f; - }); - inputComponent.SetActionCallback(InputAction::MOVE_DOWN, [&]() { - auto& transform = engine.GetRegistry().get(player); - transform.y += 5.0f; - }); - inputComponent.SetActionCallback(InputAction::MOVE_LEFT, [&]() { - auto& transform = engine.GetRegistry().get(player); - transform.x -= 5.0f; - }); - inputComponent.SetActionCallback(InputAction::MOVE_RIGHT, [&]() { - auto& transform = engine.GetRegistry().get(player); - transform.x += 5.0f; + // Y positions for each button + float singlePlayerY = screenHeight / 2 - (buttonHeight + spacing); + float quitButtonY = screenHeight / 2 + (buttonHeight + spacing); + + // Create a UI button for "Single Player" + entt::entity singlePlayerButton = registry.create(); + registry.emplace(singlePlayerButton, centerX, singlePlayerY, 1.0f, 1.0f, 0.0f); // Centered horizontally + registry.emplace(singlePlayerButton, "Single Player", buttonWidth, buttonHeight, [&]() { + std::cout << "Single Player button clicked!" << std::endl; }); + registry.emplace(singlePlayerButton, 1); // Main menu on layer 1 + // Create a "Quit" button + entt::entity quitButton = registry.create(); + registry.emplace(quitButton, centerX, quitButtonY, 1.0f, 1.0f, 0.0f); // Centered horizontally + registry.emplace(quitButton, "Quit", buttonWidth, buttonHeight, [&]() { + engine.Shutdown(); + }); + registry.emplace(quitButton, 1); // Also on layer 1 + + // Create an "F3" debug overlay on a higher layer + entt::entity debugOverlay = registry.create(); + registry.emplace(debugOverlay, 10.0f, 10.0f, 1.0f, 1.0f, 0.0f); // Placed at top-left corner + registry.emplace(debugOverlay, "FPS: 60\nPosition: (100, 200)", 300.0f, 100.0f, nullptr); + registry.emplace(debugOverlay, 2); // Debug overlay on layer 2 + + // Set active scene to the main menu + sceneManager.SetActiveScene(mainMenuScene); + + // Run the engine loop engine.Run(); engine.Shutdown(); return 0; } + + + + // entt::entity gameScene = sceneManager.CreateScene(); + // sceneManager.SetActiveScene(gameScene); + // + // entt::entity player = engine.GetRegistry().create(); + // engine.GetRegistry().emplace(player, 400.0f, 300.0f, 1.0f, 1.0f, 0.0f); + // + // auto& inputComponent = engine.GetRegistry().emplace(player); + // + // inputComponent.BindKey(InputAction::MOVE_UP, KEY_W); + // inputComponent.BindKey(InputAction::MOVE_DOWN, KEY_S); + // inputComponent.BindKey(InputAction::MOVE_LEFT, KEY_A); + // inputComponent.BindKey(InputAction::MOVE_RIGHT, KEY_D); + // + // inputComponent.SetActionCallback(InputAction::MOVE_UP, [&]() { + // auto& transform = engine.GetRegistry().get(player); + // transform.y -= 5.0f; + // }); + // inputComponent.SetActionCallback(InputAction::MOVE_DOWN, [&]() { + // auto& transform = engine.GetRegistry().get(player); + // transform.y += 5.0f; + // }); + // inputComponent.SetActionCallback(InputAction::MOVE_LEFT, [&]() { + // auto& transform = engine.GetRegistry().get(player); + // transform.x -= 5.0f; + // }); + // inputComponent.SetActionCallback(InputAction::MOVE_RIGHT, [&]() { + // auto& transform = engine.GetRegistry().get(player); + // transform.x += 5.0f; + // }); diff --git a/src/FatalHandler.h b/src/FatalHandler.h new file mode 100644 index 0000000..7549b4c --- /dev/null +++ b/src/FatalHandler.h @@ -0,0 +1,19 @@ +// +// Created by illyum on 9/13/2024. +// + +#ifndef FATALHANDLER_H +#define FATALHANDLER_H + +#include + + +// TODO: Implement warning popup/crash handler (platform specific) +// TODO: Separate fatal handlers for core (the engine) and app (the user's app) +// maybe let the user handle their own fatal errors if they so choose, or they can use the default? similar +// to how the user has to specify an engine application +namespace IsoEngine { + void FatalHandler(const std::string& message); +}; + +#endif //FATALHANDLER_H diff --git a/src/IsoEngine.cpp b/src/IsoEngine.cpp index 749b20d..d614c61 100644 --- a/src/IsoEngine.cpp +++ b/src/IsoEngine.cpp @@ -1,4 +1,7 @@ #include "IsoEngine.h" + +#include + #include "components.h" #include @@ -16,8 +19,9 @@ void IsoEngine::Initialize() { if (network) { network->Initialize(); } - - std::cout << "Engine initialized." << std::endl; + Logger::GetCoreLogger().AddOutputStream(&std::cout); + Logger::GetAppLogger().AddOutputStream(&std::cout); + CORE_LOG_INFO("Engine initialized!"); } void IsoEngine::Run() { @@ -39,7 +43,7 @@ void IsoEngine::Shutdown() { network->Shutdown(); } - std::cout << "Engine shut down." << std::endl; + CORE_LOG_INFO("Engine shut down"); } void IsoEngine::Update() { diff --git a/src/IsoEnginePCH.h b/src/IsoEnginePCH.h index 87ca935..72b2481 100644 --- a/src/IsoEnginePCH.h +++ b/src/IsoEnginePCH.h @@ -22,6 +22,7 @@ #include #include #include +#include #endif //ISOENGINEPCH_H diff --git a/src/Logger.cpp b/src/Logger.cpp index d584421..4b47fa1 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -1,109 +1,63 @@ #include "Logger.h" +#include "FatalHandler.h" -void Logger::Initialize(const std::string &logFile, LogLevel consoleLevel, LogLevel fileLevel) { - logFileName = logFile; - consoleLogLevel = consoleLevel; - fileLogLevel = fileLevel; - if (!logFile.empty()) { - logFileStream.open(logFile, std::ios::out | std::ios::app); +Logger& Logger::GetCoreLogger() { + static Logger coreLogger; + return coreLogger; +} + +Logger& Logger::GetAppLogger() { + static Logger appLogger; + return appLogger; +} + +Logger::Logger() {} + +void Logger::AddOutputStream(std::ostream* stream) { + outputStreams.push_back(stream); +} + +void Logger::SetLogLevel(LogLevel level) { + logLevel = level; +} + +void Logger::Log(LogLevel level, const std::string& message) { + if (level >= logLevel) { + LogInternal(level, message); } } -Logger::~Logger() { - if (logFileStream.is_open()) { - logFileStream.close(); +void Logger::LogInternal(LogLevel level, const std::string& message) { + std::string time = GetCurrentTime(); + std::string levelStr = LevelToString(level); + + for (auto* stream : outputStreams) { + *stream << time << " [" << levelStr << "]: " << message << std::endl; } } -void Logger::Log(LogLevel level, const std::string& message, const std::string& scope) { - std::lock_guard lock(logMutex); +std::string Logger::GetCurrentTime() { + auto now = std::chrono::system_clock::now(); + auto in_time_t = std::chrono::system_clock::to_time_t(now); - std::string formattedMessage = FormatMessage(level, message, scope); - - memoryLogs.push_back(formattedMessage); - if (memoryLogs.size() > memoryLogSize) { - memoryLogs.pop_front(); - } - - if (ShouldLogToConsole(level)) { - std::cout << formattedMessage << std::endl; - } - - if (ShouldLogToFile(level)) { - if (logFileStream.is_open()) { - logFileStream << formattedMessage << std::endl; - } - } + std::stringstream ss; + ss << std::put_time(std::localtime(&in_time_t), "%Y/%m/%d %H:%M:%S"); + return ss.str(); } -void Logger::LogInfo(const std::string& message, const std::string& scope) { - Log(LogLevel::INFOL, message, scope); -} - -void Logger::LogDebug(const std::string& message, const std::string& scope) { - Log(LogLevel::DEBUGL, message, scope); -} - -void Logger::LogWarning(const std::string& message, const std::string& scope) { - Log(LogLevel::WARNINGL, message, scope); -} - -void Logger::LogError(const std::string& message, const std::string& scope) { - Log(LogLevel::ERRORL, message, scope); -} - -void Logger::LogCritical(const std::string& message, const std::string& scope) { - Log(LogLevel::CRITICALL, message, scope); -} - -void Logger::DumpLogs(const std::string& dumpFileName) { - std::lock_guard lock(logMutex); - - std::ofstream dumpFile(dumpFileName, std::ios::out | std::ios::app); - if (!dumpFile.is_open()) { - std::cerr << "Failed to open dump file: " << dumpFileName << std::endl; - return; - } - - dumpFile << "---- Log Dump ----" << std::endl; - dumpFile << "Timestamp: " << GetTimestamp() << std::endl; - dumpFile << "------------------" << std::endl; - - for (const std::string& log : memoryLogs) { - dumpFile << log << std::endl; - } - - dumpFile.close(); -} - -std::string Logger::FormatMessage(LogLevel level, const std::string& message, const std::string& scope) { - std::string levelStr = LogLevelToString(level); - std::string timestamp = GetTimestamp(); - return timestamp + " [" + levelStr + "] [" + scope + "] " + message; -} - -std::string Logger::GetTimestamp() { - std::time_t now = std::time(nullptr); - char buf[20]; - std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", std::localtime(&now)); - return std::string(buf); -} - -std::string Logger::LogLevelToString(LogLevel level) { +std::string Logger::LevelToString(LogLevel level) { switch (level) { - case LogLevel::DEBUGL: return "DEBUG"; - case LogLevel::INFOL: return "INFO"; - case LogLevel::WARNINGL: return "WARNING"; - case LogLevel::ERRORL: return "ERROR"; - case LogLevel::CRITICALL: return "CRITICAL"; - default: return "UNKNOWN"; + case LogLevel::TRACE_l: return "TRACE"; + case LogLevel::DEBUG_l: return "DEBUG"; + case LogLevel::INFO_l: return "INFO"; + case LogLevel::WARN_l: return "WARN"; + case LogLevel::ERROR_l: return "ERROR"; + case LogLevel::FATAL_l: return "FATAL"; + default: return "UNKNOWN"; } } -bool Logger::ShouldLogToConsole(LogLevel level) { - return level >= consoleLogLevel; -} - -bool Logger::ShouldLogToFile(LogLevel level) { - return level >= fileLogLevel && !logFileName.empty(); -} +template +void Logger::FatalHandler(const std::string& format, Args... args) { + IsoEngine::FatalHandler(format, args...); +} \ No newline at end of file diff --git a/src/Logger.h b/src/Logger.h index 6372dfe..07a4eb1 100644 --- a/src/Logger.h +++ b/src/Logger.h @@ -1,49 +1,77 @@ #pragma once +#include + enum class LogLevel { - DEBUGL = 0, - INFOL = 10, - WARNINGL = 20, - ERRORL = 30, - CRITICALL = 40 + TRACE_l, + DEBUG_l, + INFO_l, + WARN_l, + ERROR_l, + FATAL_l }; class Logger { public: - static Logger& GetInstance() { - static Logger instance; - return instance; + static Logger& GetCoreLogger(); + static Logger& GetAppLogger(); + + void Log(LogLevel level, const std::string& message); + + template + void LogFormat(LogLevel level, const std::string& format, Args&&... args) { + if (level >= logLevel) { + std::string formattedMessage = std::vformat(format, std::make_format_args(std::forward(args)...)); + LogInternal(level, formattedMessage); + } } - Logger(const Logger&) = delete; - Logger& operator=(const Logger&) = delete; + void AddOutputStream(std::ostream* stream); + void SetLogLevel(LogLevel level); - void Initialize(const std::string &logFile = "", LogLevel consoleLevel = LogLevel::DEBUGL, LogLevel fileLevel = LogLevel::DEBUGL); - void Log(LogLevel level, const std::string &message, const std::string &scope = ""); - void LogDebug(const std::string& message, const std::string& scope = ""); - void LogInfo(const std::string& message, const std::string& scope = ""); - void LogWarning(const std::string& message, const std::string& scope = ""); - void LogError(const std::string& message, const std::string& scope = ""); - void LogCritical(const std::string& message, const std::string& scope = ""); - - void DumpLogs(const std::string& dumpFileName = "crash_dump.log"); + template + static void FatalHandler(const std::string& format, Args... args); private: - Logger() = default; - ~Logger(); + Logger(); + void LogInternal(LogLevel level, const std::string& message); + std::string GetCurrentTime(); + std::string LevelToString(LogLevel level); - std::ofstream logFileStream; - std::string logFileName; - LogLevel consoleLogLevel; - LogLevel fileLogLevel; - std::mutex logMutex; - - std::deque memoryLogs; - size_t memoryLogSize = 100; - - std::string FormatMessage(LogLevel level, const std::string& message, const std::string& scope); - std::string GetTimestamp(); - std::string LogLevelToString(LogLevel level); - bool ShouldLogToConsole(LogLevel level); - bool ShouldLogToFile(LogLevel level); + std::vector outputStreams; + LogLevel logLevel = LogLevel::TRACE_l; }; + +#ifdef ENABLE_LOGGING + +#define LOG_TRACE(...) Logger::GetAppLogger().LogFormat(LogLevel::TRACE_l, __VA_ARGS__) +#define LOG_DEBUG(...) Logger::GetAppLogger().LogFormat(LogLevel::DEBUG_l, __VA_ARGS__) +#define LOG_INFO(...) Logger::GetAppLogger().LogFormat(LogLevel::INFO_l, __VA_ARGS__) +#define LOG_WARN(...) Logger::GetAppLogger().LogFormat(LogLevel::WARN_l, __VA_ARGS__) +#define LOG_ERROR(...) Logger::GetAppLogger().LogFormat(LogLevel::ERROR_l, __VA_ARGS__) +#define LOG_FATAL(...) Logger::FatalHandler(__VA_ARGS__) + +#define CORE_LOG_TRACE(...) Logger::GetCoreLogger().LogFormat(LogLevel::TRACE_l, __VA_ARGS__) +#define CORE_LOG_DEBUG(...) Logger::GetCoreLogger().LogFormat(LogLevel::DEBUG_l, __VA_ARGS__) +#define CORE_LOG_INFO(...) Logger::GetCoreLogger().LogFormat(LogLevel::INFO_l, __VA_ARGS__) +#define CORE_LOG_WARN(...) Logger::GetCoreLogger().LogFormat(LogLevel::WARN_l, __VA_ARGS__) +#define CORE_LOG_ERROR(...) Logger::GetCoreLogger().LogFormat(LogLevel::ERROR_l, __VA_ARGS__) +#define CORE_LOG_FATAL(...) Logger::FatalHandler(__VA_ARGS__) + +#else + +#define LOG_TRACE(...) +#define LOG_DEBUG(...) +#define LOG_INFO(...) +#define LOG_WARN(...) +#define LOG_ERROR(...) +#define LOG_FATAL(...) Logger::FatalHandler(__VA_ARGS__) + +#define CORE_LOG_TRACE(...) +#define CORE_LOG_DEBUG(...) +#define CORE_LOG_INFO(...) +#define CORE_LOG_WARN(...) +#define CORE_LOG_ERROR(...) +#define CORE_LOG_FATAL(...) Logger::FatalHandler(__VA_ARGS__) + +#endif diff --git a/src/enet_client.cpp b/src/enet_client.cpp index 5e67b9e..c3173ce 100644 --- a/src/enet_client.cpp +++ b/src/enet_client.cpp @@ -7,17 +7,15 @@ public: ENetClient() : client(nullptr), peer(nullptr) {} void Initialize() override { - Logger& logger = Logger::GetInstance(); - if (enet_initialize() != 0) { - logger.LogError("An error occurred while initializing ENet.", "ENetClient"); - exit(EXIT_FAILURE); + CORE_LOG_FATAL("An error occurred while initializing ENet"); + throw std::runtime_error("An error occurred while initializing ENet"); } client = enet_host_create(nullptr, 1, 2, 0, 0); if (!client) { - logger.LogError("An error occurred while creating the ENet client.", "ENetClient"); - exit(EXIT_FAILURE); + CORE_LOG_FATAL("An error occurred while creating the ENet client"); + throw std::runtime_error("An error occurred while initializing ENet Client"); } ENetAddress address; @@ -26,42 +24,35 @@ public: peer = enet_host_connect(client, &address, 2, 0); if (!peer) { - logger.LogError("No available peers for initiating connection.", "ENetClient"); - exit(EXIT_FAILURE); + CORE_LOG_ERROR("No available peers for initiating connection"); } - logger.LogInfo("ENet client initialized and connected to server.", "ENetClient"); + CORE_LOG_TRACE("ENet client initialized and connected to server"); } void Shutdown() override { - Logger& logger = Logger::GetInstance(); - if (peer) { enet_peer_disconnect(peer, 0); - logger.LogInfo("Disconnected from peer.", "ENetClient"); + CORE_LOG_TRACE("Disconnected from peer"); } enet_host_destroy(client); enet_deinitialize(); - logger.LogInfo("ENet client shut down.", "ENetClient"); + CORE_LOG_TRACE("ENet client shut down"); } void SendData(const void* data, size_t size) override { - Logger& logger = Logger::GetInstance(); - if (peer) { ENetPacket* packet = enet_packet_create(data, size, ENET_PACKET_FLAG_RELIABLE); enet_peer_send(peer, 0, packet); - logger.LogDebug("Data sent to server.", "ENetClient"); + CORE_LOG_TRACE("Data sent to server"); } } void ReceiveData() override { - Logger& logger = Logger::GetInstance(); - ENetEvent event; while (enet_host_service(client, &event, 0) > 0) { if (event.type == ENET_EVENT_TYPE_RECEIVE) { - logger.LogInfo("Received packet: " + std::string(reinterpret_cast(event.packet->data)), "ENetClient"); + CORE_LOG_TRACE("Received packet: {}", std::string(reinterpret_cast(event.packet->data))); enet_packet_destroy(event.packet); } } diff --git a/src/filepacker.cpp b/src/filepacker.cpp index 45ef59f..22c4aab 100644 --- a/src/filepacker.cpp +++ b/src/filepacker.cpp @@ -11,7 +11,7 @@ bool FilePacker::Pack(const std::string &directoryPath, const std::string &outpu std::ofstream outputFile(outputBapFile, std::ios::binary); if (!outputFile) { - Logger::GetInstance().LogError("FilePacker::Pack(): Failed to open output file."); + CORE_LOG_ERROR("Failed to open output file"); return false; } @@ -33,7 +33,7 @@ bool FilePacker::Pack(const std::string &directoryPath, const std::string &outpu std::ifstream inputFile(entry.path(), std::ios::binary | std::ios::ate); if (!inputFile) { - Logger::GetInstance().LogError("FilePacker::Pack(): Failed to open file for packing: " + entry.path().string()); + CORE_LOG_ERROR("FilePacker::Pack(): Failed to open file for packing: {}", entry.path().string()); return false; } fileEntry.dataSize = inputFile.tellg(); @@ -75,7 +75,7 @@ bool FilePacker::Pack(const std::string &directoryPath, const std::string &outpu std::vector FilePacker::listFiles(const std::string& bapFile) { std::ifstream inputFile(bapFile, std::ios::binary); if (!inputFile) { - Logger::GetInstance().LogCritical("Failed to open .bap file for listing: " + bapFile); + CORE_LOG_FATAL("Failed to open .bap file for listing: {}", bapFile); throw std::runtime_error("File could not be opened"); } @@ -87,7 +87,7 @@ std::vector FilePacker::listFiles(const std::string& bapFile) { bool FilePacker::ExtractFile(const std::string& bapFile, const std::string& fileName, const std::string& outputPath) { std::ifstream inputFile(bapFile, std::ios::binary); if (!inputFile) { - Logger::GetInstance().LogError("Failed to open .bap file for extraction: " + bapFile); + CORE_LOG_ERROR("Failed to open .bap file for extraction: {}", bapFile); return false; } @@ -96,7 +96,7 @@ bool FilePacker::ExtractFile(const std::string& bapFile, const std::string& file if (fileEntry.filePath == fileName) { std::ofstream outFile(outputPath, std::ios::binary); if (!outFile) { - Logger::GetInstance().LogError("Failed to create output file: " + outputPath); + CORE_LOG_ERROR("Failed to create output file: {}", outputPath); return false; } @@ -110,7 +110,7 @@ bool FilePacker::ExtractFile(const std::string& bapFile, const std::string& file } } - Logger::GetInstance().LogError("File not found in .bap archive: " + fileName); + CORE_LOG_ERROR("File not found in .bap archive: {}", fileName); return false; } @@ -143,7 +143,7 @@ BapHeader FilePacker::ReadHeader(std::ifstream& inFile) { inFile.read(reinterpret_cast(&header.fileCount), sizeof(header.fileCount)); if (header.version != PACKER_VERSION) { - Logger::GetInstance().LogCritical("FilePacker::ReadHeader(): Wrong .bap file version."); + CORE_LOG_FATAL("FilePacker::ReadHeader(): Wrong .bap file version."); throw std::runtime_error("Invalid bap file version"); } diff --git a/src/platform/posix/FatalHandlerPosix.cpp b/src/platform/posix/FatalHandlerPosix.cpp new file mode 100644 index 0000000..c50c262 --- /dev/null +++ b/src/platform/posix/FatalHandlerPosix.cpp @@ -0,0 +1,38 @@ +// +// Created by illyum on 9/13/2024. +// + +#include "FatalHandler.h" +#include +#include +#include +#include +#include +#include + +namespace IsoEngine { + + template + void FatalHandler(const std::string& format, Args... args) { + std::ostringstream stream; + stream << std::format(format, args...); + + // TODO: change the name of the file to reflect the time the crash happened + std::ofstream crashFile("crash.log", std::ios::app); + crashFile << "FATAL ERROR: " << stream.str() << std::endl; + + void* callstack[128]; + int frames = backtrace(callstack, 128); + char** symbols = backtrace_symbols(callstack, frames); + + crashFile << "Stack trace:\n"; + for (int i = 0; i < frames; ++i) { + crashFile << symbols[i] << std::endl; + } + + free(symbols); + crashFile.close(); + + std::abort(); + } +} diff --git a/src/platform/windows/FatalHandlerWindows.cpp b/src/platform/windows/FatalHandlerWindows.cpp new file mode 100644 index 0000000..1c4fa28 --- /dev/null +++ b/src/platform/windows/FatalHandlerWindows.cpp @@ -0,0 +1,64 @@ +// +// Created by illyum on 9/13/2024. +// + +#include "FatalHandler.h" +#include +#include +#include +#include +#include + +#pragma comment(lib, "dbghelp.lib") + +namespace IsoEngine { + template + void FatalHandler(const std::string &format, Args... args) { + std::ostringstream stream; + stream << std::format(format, args...); + + // TODO: change the name of the file to reflect the time the crash happened + std::ofstream crashFile("crash.log", std::ios::app); + crashFile << "FATAL ERROR: " << stream.str() << std::endl; + + HANDLE process = GetCurrentProcess(); + SymInitialize(process, NULL, TRUE); + + CONTEXT context; + RtlCaptureContext(&context); + + STACKFRAME64 stackFrame = {}; + stackFrame.AddrPC.Offset = context.Rip; + stackFrame.AddrPC.Mode = AddrModeFlat; + stackFrame.AddrFrame.Offset = context.Rbp; + stackFrame.AddrFrame.Mode = AddrModeFlat; + stackFrame.AddrStack.Offset = context.Rsp; + stackFrame.AddrStack.Mode = AddrModeFlat; + + crashFile << "Stack trace:\n"; + while (StackWalk64(IMAGE_FILE_MACHINE_AMD64, process, GetCurrentThread(), &stackFrame, &context, NULL, + SymFunctionTableAccess64, SymGetModuleBase64, NULL)) { + DWORD64 address = stackFrame.AddrPC.Offset; + if (address == 0) { + break; + } + + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO) buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + + DWORD64 displacement = 0; + if (SymFromAddr(process, address, &displacement, symbol)) { + crashFile << symbol->Name << " - 0x" << std::hex << symbol->Address << std::dec << "\n"; + } else { + crashFile << "Unresolved symbol at address: 0x" << std::hex << address << std::dec << "\n"; + } + } + + SymCleanup(process); + crashFile.close(); + + std::abort(); + } +}