diff --git a/include/Cubed/app.hpp b/include/Cubed/app.hpp index abd550f..4d9ee45 100644 --- a/include/Cubed/app.hpp +++ b/include/Cubed/app.hpp @@ -64,6 +64,7 @@ private: inline static int fps = 0; Argument m_argument; bool m_running = true; + bool m_opengl_init = false; void init(int argc, char** argv); void handle_argument(int argc, char** argv); auto init_camera(); diff --git a/include/Cubed/argument.hpp b/include/Cubed/argument.hpp index 0df551b..4f4ac31 100644 --- a/include/Cubed/argument.hpp +++ b/include/Cubed/argument.hpp @@ -6,9 +6,12 @@ struct Argument { std::optional port; std::optional ip; std::optional player; - std::optional no_debug; std::optional language; std::optional video_driver; std::optional enable_exclusive; + std::optional logs_path; + std::optional log_level; + std::optional enable_filelog; + std::optional enable_consolelog; }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/audio/audio_error.hpp b/include/Cubed/audio/audio_error.hpp index 5b8617d..4beb1c1 100644 --- a/include/Cubed/audio/audio_error.hpp +++ b/include/Cubed/audio/audio_error.hpp @@ -2,6 +2,7 @@ #include "Cubed/tools/log.hpp" #include +#include namespace Cubed { inline void check_al_error( std::source_location loaction = std::source_location::current()) { diff --git a/include/Cubed/camera.hpp b/include/Cubed/camera.hpp index 2e2ea98..ff40f72 100644 --- a/include/Cubed/camera.hpp +++ b/include/Cubed/camera.hpp @@ -18,7 +18,6 @@ private: THIRD_PERSON_FRONT, }; - bool m_firse_mouse = true; ClientPlayer* m_player; float m_last_mouse_x, m_last_mouse_y; glm::vec3 m_camera_pos; @@ -38,7 +37,6 @@ public: void camera_init(ClientPlayer* player); void hot_reload(); - void reset_camera(); void update_cursor_position_camera(float offset_x, float offset_y); const glm::mat4 get_camera_lookat() const; diff --git a/include/Cubed/config.hpp b/include/Cubed/config.hpp index 0abf0dc..5503819 100644 --- a/include/Cubed/config.hpp +++ b/include/Cubed/config.hpp @@ -1,4 +1,5 @@ #pragma once +#include "Cubed/tools/cubed_assert.hpp" #include "Cubed/tools/toml.utils.hpp" namespace Cubed { @@ -19,6 +20,7 @@ public: template T get(std::string_view key, T default_value) { + ASSERT(m_init); if (auto* node = find_node(m_tbl, key)) { if (auto value = node->value()) return *value; @@ -30,6 +32,7 @@ public: } template void set(std::string_view key, T&& value) { + ASSERT(m_init); auto pos = key.rfind('.'); toml::table* table; @@ -53,6 +56,7 @@ public: private: toml::table m_tbl; + bool m_init = false; const std::string CONGIF_PATH; const toml::node* find_node(const toml::table& root, std::string_view path) const; diff --git a/include/Cubed/debug_collector.hpp b/include/Cubed/debug_collector.hpp index 5de74fd..40690b8 100644 --- a/include/Cubed/debug_collector.hpp +++ b/include/Cubed/debug_collector.hpp @@ -13,7 +13,7 @@ namespace Cubed { class DebugCollector { public: static DebugCollector& get(); - static void distory(); + static void destory(); DebugCollector(); void report(const std::string& name, std::string_view content); diff --git a/include/Cubed/render/renderer.hpp b/include/Cubed/render/renderer.hpp index 9043070..00edc28 100644 --- a/include/Cubed/render/renderer.hpp +++ b/include/Cubed/render/renderer.hpp @@ -27,7 +27,7 @@ public: Renderer(TextureManager& texture_manager, Config& config); ~Renderer(); void reload_config(); - void init(bool debug_on); + void init(); const Shader& get_shader(const std::string& name) const; void begin_frame(); void end_frame(); diff --git a/include/Cubed/tools/log.hpp b/include/Cubed/tools/log.hpp index 7a3d0a1..f499830 100644 --- a/include/Cubed/tools/log.hpp +++ b/include/Cubed/tools/log.hpp @@ -1,85 +1,184 @@ #pragma once +#include #include +#include #include +#include #include -#include +#include +#include +#include +#include #include #include +#include +#ifdef DEBUG +#undef DEBUG +#endif + +#ifdef INFO +#undef INFO +#endif + +#ifdef ERROR +#undef ERROR +#endif + +#ifdef WARN +#undef WARN +#endif namespace Cubed { -namespace Logger { -enum class Level { L_TRACE, L_DEBUG, L_INFO, L_ERROR, L_WARN }; +class Logger { +public: + enum class Level { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3 }; + static constexpr Level get_level(int i) { + switch (i) { + case std::to_underlying(Level::DEBUG): + return Level::DEBUG; -template -inline void info(std::format_string fmt, Args&&... args) { - auto now_time = std::chrono::time_point_cast( - std::chrono::system_clock::now()); - - std::string msg = std::vformat(fmt.get(), std::make_format_args(args...)); - std::osyncstream(std::cout) - << "\033[1;32m" << std::format("[INFO][{:%Y-%m-%d %H:%M:%S}]", now_time) - << msg << "\033[0m" - << "\n"; -} - -template -inline void error(std::format_string fmt, Args&&... args) { - auto now_time = std::chrono::time_point_cast( - std::chrono::system_clock::now()); - std::string msg = std::vformat(fmt.get(), std::make_format_args(args...)); - std::osyncstream(std::cerr) - << "\033[1;31m" - << std::format("[ERROR][{:%Y-%m-%d %H:%M:%S}]", now_time) << msg - << "\033[0m" - << "\n"; -} - -template -inline void warn(std::format_string fmt, Args&&... args) { - auto now_time = std::chrono::time_point_cast( - std::chrono::system_clock::now()); - std::string msg = std::vformat(fmt.get(), std::make_format_args(args...)); - std::osyncstream(std::cout) - << "\033[1;33m" << std::format("[WARN][{:%Y-%m-%d %H:%M:%S}]", now_time) - << msg << "\033[0m" - << "\n"; -} - -template -inline void log(Level level, std::source_location loc, - std::format_string fmt, Args&&... args) { - auto now_time = std::chrono::time_point_cast( - std::chrono::system_clock::now()); - std::string msg = std::vformat(fmt.get(), std::make_format_args(args...)); - switch (level) { - case Logger::Level::L_TRACE: - std::osyncstream(std::cout) - << "\033[1;34m" - << std::format("[TRACE][{:%Y-%m-%d %H:%M:%S}]", now_time) << "[" - << loc.file_name() << ":" << loc.line() << "]" - << "[" << loc.function_name() << "]" << msg << "\033[0m" - << "\n"; - break; - case Logger::Level::L_DEBUG: - std::osyncstream(std::cout) - << "\033[1;34m" - << std::format("[DEBUG][{:%Y-%m-%d %H:%M:%S}]", now_time) << msg - << "\033[0m" - << "\n"; - break; - case Logger::Level::L_INFO: - info(fmt, std::forward(args)...); - break; - case Logger::Level::L_WARN: - warn(fmt, std::forward(args)...); - break; - case Logger::Level::L_ERROR: - error(fmt, std::forward(args)...); - break; + case std::to_underlying(Level::INFO): + return Level::INFO; + case std::to_underlying(Level::WARN): + return Level::WARN; + case std::to_underlying(Level::ERROR): + return Level::ERROR; + default: + return Level::INFO; + } + } + Logger() { m_file_stream = std::make_unique(); } + ~Logger() { m_file_stream.reset(); } + static Logger& instance() { + static Logger inst; + return inst; + } + static void set_level(Level level) { instance().m_level = level; } + static void set_console_write(bool write) { + instance().m_console_write = write; } -} -} // namespace Logger + static void set_file_write(bool write) { instance().m_file_write = write; } + + static void set_logs_path(const std::string& path) { + std::unique_lock lock(instance().m_mutex); + instance().m_logs_path = path; + } + + template + static void info(std::format_string fmt, Args&&... args) { + instance().log(Level::INFO, fmt, std::forward(args)...); + } + template + static void error(std::format_string fmt, Args&&... args) { + instance().log(Level::ERROR, fmt, std::forward(args)...); + } + template + static void warn(std::format_string fmt, Args&&... args) { + instance().log(Level::WARN, fmt, std::forward(args)...); + } + template + static void debug(std::format_string fmt, Args&&... args) { + instance().log(Level::DEBUG, fmt, std::forward(args)...); + } + template + void log(Level level, std::format_string fmt, Args&&... args) { + auto now_time = std::chrono::time_point_cast( + std::chrono::system_clock::now()); + if (level < m_level) { + return; + } + + auto msg = std::format(fmt, std::forward(args)...); + std::string_view level_str; + std::string_view color; + switch (level) { + case Level::INFO: + color = INFO_ANSI; + level_str = "[INFO]"; + break; + case Level::ERROR: + color = ERROR_ANSI; + level_str = "[ERROR]"; + break; + case Level::WARN: + color = WARN_ANSI; + level_str = "[WARN]"; + break; + case Level::DEBUG: + color = DEBUG_ANSI; + level_str = "[DEBUG]"; + break; + } + std::string message = + std::format("{}[{:%Y-%m-%d %H:%M:%S}]{}", level_str, now_time, msg); + + if (m_file_write) { + + std::shared_lock lock{m_mutex}; + auto today = std::chrono::floor( + std::chrono::system_clock::now()); + auto date = std::chrono::year_month_day{today}; + + if (!m_today.ok() || date > m_today) { + lock.unlock(); + std::unique_lock l(m_mutex); + + auto today = std::chrono::floor( + std::chrono::system_clock::now()); + auto date = std::chrono::year_month_day{today}; + + if (!m_today.ok() || date > m_today) { + m_today = date; + try { + std::filesystem::create_directories(m_logs_path); + m_file_stream->close(); + std::filesystem::path log = + m_logs_path / std::format("{:%Y-%m-%d}.log", date); + m_file_stream->open(log, std::ios::app); + if (!m_file_stream->is_open()) { + std::osyncstream(std::cerr) + << "File Stream Open Error\n"; + } + // direct write + if (m_file_stream && m_file_stream->is_open()) { + std::osyncstream sync_out{*m_file_stream}; + std::println(sync_out, "{}", message); + } + } catch (const std::exception& e) { + std::osyncstream(std::cerr) + << "Catch Logger Error " << e.what() << "\n"; + } + } + } else { + if (m_file_stream && m_file_stream->is_open()) { + std::osyncstream sync_out{*m_file_stream}; + std::println(sync_out, "{}", message); + } + } + } + + if (m_console_write) { + std::osyncstream sync_out(level == Level::ERROR ? std::cerr + : std::cout); + std::println(sync_out, "{}{}{}", color, message, CLEAR_ANSI); + } + } + +private: + static constexpr std::string_view CLEAR_ANSI = "\033[0m"; + static constexpr std::string_view INFO_ANSI = "\033[1;32m"; + static constexpr std::string_view ERROR_ANSI = "\033[1;31m"; + static constexpr std::string_view DEBUG_ANSI = "\033[1;34m"; + static constexpr std::string_view WARN_ANSI = "\033[1;33m"; + std::filesystem::path m_logs_path{"logs"}; + std::shared_mutex m_mutex; + std::atomic m_file_write{false}; + std::atomic m_console_write{true}; + std::atomic m_level{Level::INFO}; + std::chrono::year_month_day m_today{}; + std::unique_ptr m_file_stream; +}; } // namespace Cubed diff --git a/src/app.cpp b/src/app.cpp index c70746a..068f286 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -1,6 +1,5 @@ #include "Cubed/app.hpp" -#include "Cubed/camera.hpp" #include "Cubed/config.hpp" #include "Cubed/debug_collector.hpp" #include "Cubed/localization.hpp" @@ -25,13 +24,43 @@ App::App() App::~App() { stop_text_input(); - Font::destroy(); - DebugCollector::distory(); + if (m_opengl_init) { + Font::destroy(); + DebugCollector::destory(); + } } void App::init(int argc, char** argv) { - handle_argument(argc, argv); + bool debug = false; + Logger::set_console_write(true); +#ifdef DEBUG_MODE + Logger::set_level(Logger::Level::DEBUG); + Logger::set_file_write(false); + debug = true; +#else + Logger::set_level(Logger::Level::INFO); + debug = false; +#endif + handle_argument(argc, argv); + if (m_argument.log_level) { + Logger::set_level(Logger::get_level(*m_argument.log_level)); + } + if (m_argument.logs_path) { + Logger::set_logs_path(*m_argument.logs_path); + Logger::set_file_write(true); + } + if (!debug) { + Logger::set_file_write(true); + Logger::set_console_write(false); + } + if (m_argument.enable_consolelog) { + Logger::set_console_write(true); + } + if (m_argument.enable_filelog) { + Logger::set_file_write(true); + } + m_game_config.load_config(); if (m_argument.language) { Localization::instance().load_language(*m_argument.language); m_game_config.set("language", *m_argument.language); @@ -47,16 +76,12 @@ void App::init(int argc, char** argv) { m_window.init(m_argument); m_window.imgui_init(); - + m_opengl_init = true; Logger::info("Window Init Success"); m_audio.init(); BlockManager::init(); - if (m_argument.no_debug) { - m_renderer.init(*m_argument.no_debug); - } else { - m_renderer.init(true); - } + m_renderer.init(); Logger::info("Renderer Init Success"); // MapTable::init_map(); m_texture_manager.init_texture(); @@ -90,16 +115,16 @@ void App::handle_argument(int argc, char** argv) { int port; auto r = std::from_chars(arg.data(), arg.data() + arg.size(), port); - m_argument.port = port; + if (r.ec != std::errc{} || r.ptr != arg.data() + arg.size()) { throw std::runtime_error( std::format("Invalid port: {}", arg)); } - - if (m_argument.port > 65535) { + if (port > 65535) { throw std::runtime_error( - std::format("Port {} out of range", *m_argument.port)); + std::format("Port {} out of range", port)); } + m_argument.port = port; }}, {"--ip", @@ -117,12 +142,6 @@ void App::handle_argument(int argc, char** argv) { std::cout << CUBED_VERSION << "\n"; exit(EXIT_SUCCESS); }}, - - {"--no-debug", - [&](ArgParser) { - m_argument.no_debug = false; - Logger::info("Switch off opengl debug out put"); - }}, {"--language", [&](ArgParser& p) { auto arg = p.require_next("--language"); @@ -134,7 +153,32 @@ void App::handle_argument(int argc, char** argv) { m_argument.video_driver = arg; }}, {"--enable-exclusive", - [&](ArgParser&) { m_argument.enable_exclusive = true; }} + [&](ArgParser&) { m_argument.enable_exclusive = true; }}, + {"--logs-path", + [&](ArgParser& p) { + m_argument.logs_path = p.require_next("--logs-path"); + }}, + {"--log-level", + [&](ArgParser& p) { + auto arg = p.require_next("--log-level"); + int level; + auto r = std::from_chars(arg.data(), arg.data() + arg.size(), + level); + + if (r.ec != std::errc{} || r.ptr != arg.data() + arg.size()) { + throw std::runtime_error( + std::format("Invalid log Level: {}", arg)); + } + if (level > 3) { + throw std::runtime_error( + std::format("Level {} out of range", level)); + } + m_argument.log_level = level; + }}, + {"--enable-filelog", + [&](ArgParser&) { m_argument.enable_filelog = true; }}, + {"--enale-consolelog", + [&](ArgParser&) { m_argument.enable_consolelog = true; }} }; ArgParser parser(argc, argv); @@ -563,10 +607,6 @@ void App::handle_sdl_mouse_button(SDL_Event& e) { void App::handle_window_focus(bool focused) { if (focused) { - auto camera = m_window.camera(); - if (camera) { - camera->reset_camera(); - } } } @@ -675,6 +715,10 @@ void App::handle_sdl_event(SDL_Event& e) { break; case SDL_EVENT_MOUSE_MOTION: if (!imgui_enable) { + if (std::abs(e.motion.xrel) > 200 || + std::abs(e.motion.yrel) > 200) { + return; + } handle_mouse_move(e.motion.x, e.motion.y, e.motion.xrel, e.motion.yrel); } diff --git a/src/camera.cpp b/src/camera.cpp index af84816..5272dc1 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -61,19 +61,12 @@ void Camera::update_move_camera() { void Camera::camera_init(ClientPlayer* player) { m_player = player; update_move_camera(); - reset_camera(); hot_reload(); } void Camera::hot_reload() {} -void Camera::reset_camera() { m_firse_mouse = true; } - void Camera::update_cursor_position_camera(float offset_x, float offset_y) { - if (m_firse_mouse) { - m_firse_mouse = false; - return; - } ASSERT_MSG(m_player, "nullptr"); m_player->update_front_vec(offset_x, offset_y); @@ -109,34 +102,29 @@ bool Camera::is_first_person() const { } ClientPlayer* Camera::player() { return m_player; } bool Camera::handle_event(const Event& e) { - return std::visit(Overloaded{[this](const MouseMoveEvent& e) { - if (handle_mouse_move_event(e)) { - return true; - } + return std::visit( + Overloaded{[this](const MouseMoveEvent& e) { + if (handle_mouse_move_event(e)) { + return true; + } - return false; - }, - [](const MouseButtonEvent&) { return false; }, - [](const MouseWheelEvent&) { return false; }, - [this](const KeyEvent& e) { - if (handle_key_event(e)) { - return true; - } - return false; - }, - [](const TextInputEvent&) { return false; }, - [this](const WindowResizeEvent&) { - reset_camera(); - return false; - }, - [this](const FrameBufferResizeEvent&) { - reset_camera(); - return false; - } + return false; + }, + [](const MouseButtonEvent&) { return false; }, + [](const MouseWheelEvent&) { return false; }, + [this](const KeyEvent& e) { + if (handle_key_event(e)) { + return true; + } + return false; + }, + [](const TextInputEvent&) { return false; }, + [](const WindowResizeEvent&) { return false; }, + [](const FrameBufferResizeEvent&) { return false; } - } // namespace Cubed - , - e); + } // namespace Cubed + , + e); } glm::vec3 Camera::camera_collision(glm::vec3 start, glm::vec3 end, @@ -161,10 +149,6 @@ glm::vec3 Camera::camera_collision(glm::vec3 start, glm::vec3 end, } bool Camera::handle_key_event(const KeyEvent& e) { - if (e.key == Key::LEFT_ALT && e.action == KeyAction::PRESS) { - reset_camera(); - return true; - } if (e.key == Key::F5 && e.action == KeyAction::PRESS) { change_perspective(); return true; diff --git a/src/config.cpp b/src/config.cpp index 762233b..d5a8b54 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -10,7 +10,7 @@ using namespace std::string_view_literals; namespace Cubed { -Config::Config(std::string_view path) : CONGIF_PATH(path) { load_config(); } +Config::Config(std::string_view path) : CONGIF_PATH(path) {} Config::~Config() { save_to_file(); } @@ -23,6 +23,7 @@ void Config::load_config() { try { m_tbl = toml::parse_file(config_path.string()); Logger::info("Load Config File Success"); + m_init = true; } catch (const toml::parse_error& err) { Logger::error("Load Config Error: \"{}\"", err.what()); } diff --git a/src/debug_collector.cpp b/src/debug_collector.cpp index 9a90dae..493a1e0 100644 --- a/src/debug_collector.cpp +++ b/src/debug_collector.cpp @@ -17,7 +17,7 @@ std::unique_ptr& DebugCollector::get_ptr() { std::make_unique(); return instance; } -void DebugCollector::distory() { get_ptr().reset(); } +void DebugCollector::destory() { get_ptr().reset(); } void DebugCollector::init(int width, int height) { constexpr float SCALE = 0.6f; diff --git a/src/render/renderer.cpp b/src/render/renderer.cpp index 0c523f2..b1db816 100644 --- a/src/render/renderer.cpp +++ b/src/render/renderer.cpp @@ -36,7 +36,7 @@ void Renderer::reload_config() { update_fov(m_config.get("player.fov", 70.0f)); } -void Renderer::init(bool debug_on) { +void Renderer::init() { Logger::info("OpenGL Version: {}.{}", GLVersion.major, GLVersion.minor); Logger::info("Renderer: {}", @@ -51,17 +51,15 @@ void Renderer::init(bool debug_on) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); #ifdef DEBUG_MODE - if (debug_on) { - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback( - [](GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar* message, - const void*) { - Logger::log(Logger::Level::L_DEBUG, - std::source_location::current(), "GL Debug: {}", - reinterpret_cast(message)); - }, - nullptr); - } + + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback( + [](GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar* message, + const void*) { + Logger::debug("GL Debug: {}", + reinterpret_cast(message)); + }, + nullptr); #endif diff --git a/src/scene/world_scene.cpp b/src/scene/world_scene.cpp index 4fda842..e0f529b 100644 --- a/src/scene/world_scene.cpp +++ b/src/scene/world_scene.cpp @@ -282,6 +282,9 @@ ClientWorld& WorldScene::client_world() { return m_client_world; } ServerWorld& WorldScene::server_world() { return m_server.server_world(); } bool WorldScene::pause() const { return m_paused; } void WorldScene::set_pause(bool pause) { + if (m_paused == pause) { + return; + } m_paused = pause; auto& window = m_scene_manager.app().window(); window.set_game_running(!m_paused); diff --git a/src/window.cpp b/src/window.cpp index 7a5e2fb..e234b31 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -238,6 +238,10 @@ void Window::init(const Argument& argument) { SDL_SetWindowPosition(m_window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); +#ifdef DEBUG_MODE + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); + Logger::debug("Debug Flag On"); +#endif } void Window::reload_config() { @@ -298,9 +302,6 @@ void Window::enable_mouse() { } void Window::disable_mouse() { SDL_SetWindowRelativeMouseMode(m_window, true); - if (m_camera) { - m_camera->reset_camera(); - } m_mouse_enable = false; }