diff --git a/native/projects/lcpu/CMakeLists.txt b/native/projects/lcpu/CMakeLists.txt index 8cef5cb..a0bd888 100644 --- a/native/projects/lcpu/CMakeLists.txt +++ b/native/projects/lcpu/CMakeLists.txt @@ -30,6 +30,7 @@ endfunction() add_library(lcpu_native SHARED src/main.cpp + src/SourceSink.cpp ) if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") diff --git a/native/projects/lcpu/src/SourceSink.cpp b/native/projects/lcpu/src/SourceSink.cpp new file mode 100644 index 0000000..dbee2c4 --- /dev/null +++ b/native/projects/lcpu/src/SourceSink.cpp @@ -0,0 +1,47 @@ +#include "SourceSink.hpp" + +#include + +namespace tier0 { + // TODO: For now this'll leak.. + lucore::Library* library = nullptr; + + // TODO: Switch this to using the LoggingSystem. It is available on the x86-64 branch of + // gmod. + using Msg_t = void (*)(const char* format, ...); + Msg_t Msg = nullptr; + Msg_t Warning = nullptr; +} // namespace tier0 + +namespace lcpu { + + SourceSink& SourceSink::The() { + static SourceSink sink; + return sink; + } + + void SourceSink::Init() { + // Modern 64-bit GMod has one tier0 library. + // I am not supporting non-x86-64 branch servers, so this is OK and I don't need to + // do any library scanning nonsense to pick the correct one. + // + // (note that the x86-64 branch also includes 32-bit binaries, rather + // weirdly. You can build the module for this too, but not the non-x86-64 branch + // 32-bit GMod, and it also shares having only one tier0 library.) + tier0::library = lucore::Library::Open("tier0"); + tier0::Msg = tier0::library->Symbol("Msg"); + tier0::Warning = tier0::library->Symbol("Warning"); + } + + void SourceSink::OutputMessage(const lucore::Logger::MessageData& data) { + auto formatted = + std::format("[LCPU Native/{}] [{}] {}\n", lucore::Logger::SeverityToString(data.severity), + data.time, std::vformat(data.format, data.args)); + if(data.severity < lucore::Logger::MessageSeverity::Warning) { + tier0::Msg("%s", formatted.c_str()); + } else { + tier0::Warning("%s", formatted.c_str()); + } + } + +} // namespace lcpu diff --git a/native/projects/lcpu/src/SourceSink.hpp b/native/projects/lcpu/src/SourceSink.hpp new file mode 100644 index 0000000..ab1772b --- /dev/null +++ b/native/projects/lcpu/src/SourceSink.hpp @@ -0,0 +1,15 @@ +#include + +namespace lcpu { + + /// A lucore logger sink which funnels to the Source engine's + /// Tier0 library's logging functionality. + struct SourceSink : public lucore::Logger::Sink { + static SourceSink& The(); + + static void Init(); + + void OutputMessage(const lucore::Logger::MessageData& data) override; + }; + +} diff --git a/native/projects/lcpu/src/main.cpp b/native/projects/lcpu/src/main.cpp index 28a660c..9d10ac9 100644 --- a/native/projects/lcpu/src/main.cpp +++ b/native/projects/lcpu/src/main.cpp @@ -1,6 +1,17 @@ #include +#include "SourceSink.hpp" + +#include + GMOD_MODULE_OPEN() { + // Initalize the source logging sink and attach it to the lucore logger. + lcpu::SourceSink::Init(); + lucore::Logger::The().AttachSink(lcpu::SourceSink::The()); + + lucore::LogInfo("Hello Source World :) {} {}", 123.456, "This should work"); + lucore::LogWarning("test"); + LUCORE_CHECK(false, "this should bring the process down"); return 0; } diff --git a/native/projects/lucore/CMakeLists.txt b/native/projects/lucore/CMakeLists.txt index 7a824f9..3881c11 100644 --- a/native/projects/lucore/CMakeLists.txt +++ b/native/projects/lucore/CMakeLists.txt @@ -8,6 +8,9 @@ project(lucore add_library(lucore src/Assert.cpp src/Logger.cpp + + src/OsLibrary.cpp + src/Library.cpp ) target_compile_features(lucore PUBLIC cxx_std_20) diff --git a/native/projects/lucore/include/lucore/Assert.hpp b/native/projects/lucore/include/lucore/Assert.hpp index dd39748..ef0841f 100644 --- a/native/projects/lucore/include/lucore/Assert.hpp +++ b/native/projects/lucore/include/lucore/Assert.hpp @@ -5,27 +5,24 @@ #include namespace lucore { - - // TODO: wrapper which uses source_location (we don't need no macros anymore!) - [[noreturn]] void ExitMsg(const char* fileName, int fileLine, const char* message); - + [[noreturn]] void ExitMsg(const char* message); } // namespace lucore #ifndef NDEBUG - #define LUCORE_ASSERT(expr, fmt, ...) \ - if(!(expr)) [[unlikely]] { \ - auto msg = std::format("Assertion \"{}\" failed with message: {}", #expr, \ - std::format(fmt, ##__VA_ARGS__)); \ - ::lucore::ExitMsg(__FILE__, __LINE__, msg.c_str()); \ + #define LUCORE_ASSERT(expr, fmt, ...) \ + if(!(expr)) [[unlikely]] { \ + auto msg = std::format("Assertion \"{}\" @ {}:{} failed with message: {}", #expr, \ + __FILE__, __LINE__, std::format(fmt, ##__VA_ARGS__)); \ + ::lucore::ExitMsg(msg.c_str()); \ } #else #define LUCORE_ASSERT(expr, format, ...) #endif // CHECK() is always active, even in release builds -#define LUCORE_CHECK(expr, fmt, ...) \ - if(!(expr)) [[unlikely]] { \ - auto msg = std::format("Check \"{}\" failed with message: {}", #expr, \ - std::format(fmt, ##__VA_ARGS__)); \ - ::lucore::ExitMsg(__FILE__, __LINE__, msg.c_str()); \ +#define LUCORE_CHECK(expr, fmt, ...) \ + if(!(expr)) [[unlikely]] { \ + auto msg = std::format("Check \"{}\" @ {}:{} failed with message: {}", #expr, __FILE__, \ + __LINE__, std::format(fmt, ##__VA_ARGS__)); \ + ::lucore::ExitMsg(msg.c_str()); \ } diff --git a/native/projects/lucore/include/lucore/Library.hpp b/native/projects/lucore/include/lucore/Library.hpp new file mode 100644 index 0000000..808c045 --- /dev/null +++ b/native/projects/lucore/include/lucore/Library.hpp @@ -0,0 +1,27 @@ +#include + +namespace lucore { + + struct Library { + using Handle = void*; + + /// Create a new library instance. + static Library* Open(std::string_view dllname); + + /// Query if [dllname] is loaded in the process. + static bool Loaded(std::string_view dllname); + + ~Library(); + + template + T Symbol(std::string_view symbolName) { + return reinterpret_cast(SymbolImpl(symbolName.data())); + } + + private: + void* SymbolImpl(const char* symbol); + constexpr explicit Library(Handle handle) : handle(handle) {} + Handle handle {}; + }; + +} // namespace lucore diff --git a/native/projects/lucore/include/lucore/Logger.hpp b/native/projects/lucore/include/lucore/Logger.hpp index 6c22168..29c2209 100644 --- a/native/projects/lucore/include/lucore/Logger.hpp +++ b/native/projects/lucore/include/lucore/Logger.hpp @@ -82,14 +82,36 @@ namespace lucore { std::vector sinks; }; +#if 0 /// A logger sink implementation that prints to standard output. struct StdoutSink : public Logger::Sink { static StdoutSink& The(); virtual void OutputMessage(const Logger::MessageData& data) override; }; +#endif /// Attach the stdout logger sink to the logger. void LoggerAttachStdout(); + template + void LogInfo(std::string_view format, Args... args) { + Logger::The().Info(format, std::forward(args)...); + } + + template + void LogWarning(std::string_view format, Args... args) { + Logger::The().Warning(format, std::forward(args)...); + } + + template + void LogError(std::string_view format, Args... args) { + Logger::The().Error(format, std::forward(args)...); + } + + template + void LogFatal(std::string_view format, Args... args) { + Logger::The().Fatal(format, std::forward(args)...); + } + } // namespace lucore diff --git a/native/projects/lucore/src/Assert.cpp b/native/projects/lucore/src/Assert.cpp index da8b2fb..16ce376 100644 --- a/native/projects/lucore/src/Assert.cpp +++ b/native/projects/lucore/src/Assert.cpp @@ -6,8 +6,8 @@ namespace lucore { - [[noreturn]] void ExitMsg(const char* fileName, int fileLine, const char* message) { - Logger::The().Fatal(message); + [[noreturn]] void ExitMsg(const char* message) { + LogFatal("{}", message); std::quick_exit(0xAF); } diff --git a/native/projects/lucore/src/Library.cpp b/native/projects/lucore/src/Library.cpp new file mode 100644 index 0000000..58552fd --- /dev/null +++ b/native/projects/lucore/src/Library.cpp @@ -0,0 +1,37 @@ +#include +#include +#include + +#include "OsLibrary.hpp" + +namespace lucore { + + namespace { + std::string FormatLibraryName(std::string_view dllName) { +#ifdef _WIN32 + return std::format("{}.dll", dllName); +#else + return std::format("lib{}.so", dllName); +#endif + } + } // namespace + + Library* Library::Open(std::string_view dllname) { + return new Library( + static_cast(detail::OsOpenLibrary(FormatLibraryName(dllname).c_str()))); + } + + bool Library::Loaded(std::string_view dllname) { + return detail::OsLibraryLoaded(FormatLibraryName(dllname).c_str()); + } + + Library::~Library() { + if(handle) { + detail::OsFreeLibrary(static_cast(handle)); + } + } + + void* Library::SymbolImpl(const char* symbolName) { + return detail::OsLibrarySymbol(static_cast(handle), symbolName); + } +} // namespace lucore diff --git a/native/projects/lucore/src/Logger.cpp b/native/projects/lucore/src/Logger.cpp index fee6d29..bef86a6 100644 --- a/native/projects/lucore/src/Logger.cpp +++ b/native/projects/lucore/src/Logger.cpp @@ -31,7 +31,7 @@ namespace lucore { for(auto sink : sinks) sink->OutputMessage(data); } - +#if 0 StdoutSink& StdoutSink::The() { static StdoutSink sink; return sink; @@ -69,5 +69,6 @@ namespace lucore { void LoggerAttachStdout() { Logger::The().AttachSink(StdoutSink::The()); } +#endif } // namespace lucore diff --git a/native/projects/lucore/src/OsLibrary.cpp b/native/projects/lucore/src/OsLibrary.cpp new file mode 100644 index 0000000..0b4e27a --- /dev/null +++ b/native/projects/lucore/src/OsLibrary.cpp @@ -0,0 +1,9 @@ +#include "OsLibrary.hpp" + +#if defined(_WIN32) +#include "OsLibrary.win32.cpp" +#elif defined(__linux__) +#include "OsLibrary.linux.cpp" +#else +#error No OsLibrary implementation for this platform +#endif diff --git a/native/projects/lucore/src/OsLibrary.hpp b/native/projects/lucore/src/OsLibrary.hpp new file mode 100644 index 0000000..60c9a77 --- /dev/null +++ b/native/projects/lucore/src/OsLibrary.hpp @@ -0,0 +1,21 @@ +//! Operating-system independent utilities for opening +//! shared libraries. This is currently a detail-only +//! Lucore API, and its stability is NOT guaranteed. + +namespace lucore::detail { + /// Opaque handle type for libraries. + using OsLibraryHandle = void*; + + /// Open a library. + OsLibraryHandle OsOpenLibrary(const char* filename); + + /// Query if the library with the given [filename] is loaded. + bool OsLibraryLoaded(const char* filename); + + /// Get a pointer to the symbol of a library. + void* OsLibrarySymbol(OsLibraryHandle dll, const char* symbolName); + + /// Free the library. + void OsFreeLibrary(OsLibraryHandle handle); + +} // namespace lucore::detail diff --git a/native/projects/lucore/src/OsLibrary.linux.cpp b/native/projects/lucore/src/OsLibrary.linux.cpp new file mode 100644 index 0000000..bf3184e --- /dev/null +++ b/native/projects/lucore/src/OsLibrary.linux.cpp @@ -0,0 +1,22 @@ +#include + +namespace lucore::detail { + OsLibraryHandle OsOpenLibrary(const char* filename) { + return dlopen(filename, RTLD_LAZY); + } + + bool OsLibraryLoaded(const char* filename) { + return dlopen(filename, RTLD_NOLOAD | RTLD_LAZY) != nullptr; + } + + void* OsLibrarySymbol(OsLibraryHandle dll, const char* symbolName) { + return dlsym(dll, symbolName); + } + + void OsFreeLibrary(OsLibraryHandle handle) { + // The reference count on *Nix will be incremented by the launcher + // process itself, therefore we do not risk accidentally pulling the + // library out of the rug of the engine in either case. + dlclose(handle); + } +} // namespace lucore::detail diff --git a/native/projects/lucore/src/OsLibrary.win32.cpp b/native/projects/lucore/src/OsLibrary.win32.cpp new file mode 100644 index 0000000..1b5e3a0 --- /dev/null +++ b/native/projects/lucore/src/OsLibrary.win32.cpp @@ -0,0 +1,23 @@ +#define _WIN32_LEAN_AND_MEAN +#include + +namespace lucore::detail { + + OsLibraryHandle OsOpenLibrary(const char* filename) { + return reinterpret_cast(GetModuleHandleA(filename); + } + + bool OsLibraryLoaded(const char* filename) { + return GetModuleHandleA(filename) != nullptr; + } + + void* OsLibrarySymbol(OsLibraryHandle dll, const char* symbolName) { + return GetProcAddressA(reinterpret_cast(dll), symbolName); + } + + void OsFreeLibrary(OsLibraryHandle handle) { + // GetModuleHandle*() does not increment the reference count; + // therefore, we have nothing to do here on Windows. + } + +}