#pragma once #include #include #include #include #include #include #include #include #include #include #include namespace rl { template class Console : public godot::Object { GDCLASS(Console, godot::Object); public: Console() { m_static_inst = this; init_loggers(); } ~Console() { m_static_inst = nullptr; } static inline rl::Console* get() { return m_static_inst; } void set_context(TContext* context) { m_gui_console = context; } void clear_context() { m_gui_console = nullptr; } void stop_logging() { m_logger->flush(); m_stop = true; } void init_loggers() requires std::same_as { auto stdout_sink{ std::make_shared() }; auto stderr_sink{ std::make_shared() }; auto callbk_sink{ std::make_shared( [this](const spdlog::details::log_msg& msg) { if (!m_stop.load(std::memory_order_relaxed)) { if (m_gui_console == nullptr) return; using duration_t = std::chrono::duration; const duration_t elapsed{ clock_t::now() - m_start_time }; m_gui_console->append_text( fmt::format("[color=gray]{:5} [{:>7.2}] [b]=>[/b] {}[/color]\n", m_line_num.fetch_add(1, std::memory_order_relaxed), elapsed, msg.payload) .c_str()); } }) }; stderr_sink->set_level(spdlog::level::err); stdout_sink->set_level(spdlog::level::info); callbk_sink->set_level(spdlog::level::debug); m_logger = std::unique_ptr( new spdlog::logger{ "custom_callback_logger", { stdout_sink, stderr_sink, callbk_sink } }); using namespace std::chrono_literals; spdlog::flush_every(0.25s); } template void print(fmt::format_string format_str, TArgs&&... args) { m_logger->info(format_str, std::forward(args)...); } protected: static void _bind_methods() { } private: std::unique_ptr m_logger{ nullptr }; std::atomic m_stop{ false }; std::atomic m_line_num{ 0 }; TContext* m_gui_console{ nullptr }; using clock_t = std::chrono::high_resolution_clock; const clock_t::time_point m_start_time{ clock_t::now() }; private: static inline Console* m_static_inst{ nullptr }; }; using console = Console; }