diff --git a/demo/Demo.hpp b/demo/Demo.hpp index 39126bb..8170687 100644 --- a/demo/Demo.hpp +++ b/demo/Demo.hpp @@ -46,7 +46,7 @@ struct Mushroom : Sprite Mushroom(Node*); void update(); - std::string get_class_name() { return "Mushroom"; } + virtual std::string get_class_name() const { return "Mushroom"; } }; @@ -71,6 +71,6 @@ struct Demo : Game void load_gl_context(); void respond(SDL_Event&); void update(); - std::string get_class_name() { return "Demo"; } + virtual std::string get_class_name() const { return "Demo"; } }; diff --git a/src/Animation.hpp b/src/Animation.hpp index 7f43742..a6e4914 100644 --- a/src/Animation.hpp +++ b/src/Animation.hpp @@ -9,7 +9,7 @@ typedef std::function callback; -struct Node; +class Node; struct Animation { diff --git a/src/Box.hpp b/src/Box.hpp index 357e20a..2085adf 100644 --- a/src/Box.hpp +++ b/src/Box.hpp @@ -77,7 +77,7 @@ struct Box : SDL_FRect bool collide(const Segment&, glm::vec2&) const; bool collide(const Box&, Box* = nullptr) const; bool collide(const Box&, Box&) const; - std::string get_class_name() { return "Box"; } + virtual std::string get_class_name() const { return "Box"; } std::ostream& to_string (std::ostream&) const; }; diff --git a/src/Configuration.cpp b/src/Configuration.cpp index fe755b7..eb75cb9 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -75,9 +75,10 @@ void Configuration::set_defaults() sys_config["animation"] = { {"all-frames-frameset-name", "all"} }; - sys_config["logging"] = { + sys_config["log"] = { {"enabled", false}, - {"debug", false}, + {"debug-to-stdout", false}, + {"debug-to-file", false}, {"ouput-directory", "."}, {"info-file-name", "log.txt"}, {"debug-file-name", "debug_log.txt"} diff --git a/src/Configuration.hpp b/src/Configuration.hpp index 5f95b8e..14756d1 100644 --- a/src/Configuration.hpp +++ b/src/Configuration.hpp @@ -20,7 +20,7 @@ struct Configuration : Node void merge(); void write(); void write(fs::path path); - std::string get_class_name() { return "Configuration"; } + virtual std::string get_class_name() const { return "Configuration"; } }; diff --git a/src/Game.cpp b/src/Game.cpp index 79be51d..ee1295e 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -39,49 +39,26 @@ void FramerateIndicator::refresh() } } -void log(void* userdata, int category, SDL_LogPriority priority, const char* message) -{ - Game* game = static_cast(userdata); - std::ostream& out = (priority > SDL_LOG_PRIORITY_DEBUG) ? std::cerr : std::cout; - out << message << std::endl; - if (game->get_configuration()["logging"]["enabled"]) - { - fs::path path = game->get_configuration()["logging"]["output-directory"]; - if (!fs::exists(path)) - { - fs::create_directories(path); - } - std::time_t now = std::time(nullptr); - std::stringstream stamped_message; - stamped_message << std::put_time(std::localtime(&now), "%F %T ") << message; - if (game->get_configuration()["logging"]["debug"]) - { - fs::path debug_path = path / game->get_configuration()["logging"]["debug-file-name"]; - std::ofstream debug_stream(debug_path, std::ios_base::app); - debug_stream << stamped_message.str() << std::endl; - } - if (priority > SDL_LOG_PRIORITY_DEBUG) - { - fs::path info_path = path / game->get_configuration()["logging"]["info-file-name"]; - std::ofstream info_stream(info_path, std::ios_base::app); - info_stream << stamped_message.str() << std::endl; - } - } -} - Game::Game() { - // use config to determine if debug statements should be printed - if (get_configuration()["logging"]["debug"]) + // set the appropriate priority level for the default log category so either info level messages + // and higher are enabled or debug level messages are enabled, depending on the global configuration + SDL_LogPriority default_log_category_priority; + if (get_configuration()["log"]["debug-to-file"] || get_configuration()["log"]["debug-to-stdout"]) { - SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG); + default_log_category_priority = SDL_LOG_PRIORITY_DEBUG; } + else + { + default_log_category_priority = SDL_LOG_PRIORITY_INFO; + } + SDL_LogSetPriority(DEFAULT_SDL_LOG_CATEGORY, default_log_category_priority); // set custom log function (prints to stdout/stderr and to file if enabled) - SDL_LogSetOutputFunction(&log, this); + SDL_LogSetOutputFunction(&Game::sdl_log_override, this); // pretty print config to debug log std::stringstream config_formatted; config_formatted << std::setw(4) << get_configuration() << std::endl; - SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s", config_formatted.str().c_str()); + SDL_LogDebug(DEFAULT_SDL_LOG_CATEGORY, "%s", config_formatted.str().c_str()); SDL_SetHint(SDL_HINT_RENDER_DRIVER, get_configuration()["display"]["render driver"].get().c_str()); frame_length_history.reserve(5000); set_framerate(get_configuration()["display"]["framerate"]); @@ -192,6 +169,48 @@ Game::~Game() get_delegate().unsubscribe(this); } +/* Overrides SDL's default log function to log a message to stdout/stderr and, if log is enabled in the + * global configuration, to a file. Debug level statements may be suppressed, printed to stdout, or printed to + * both stdout and file, depending on the global configuration. + */ +void Game::sdl_log_override(void* userdata, int category, SDL_LogPriority priority, const char* message) +{ + Game* game = static_cast(userdata); + std::ostream& out = (priority > SDL_LOG_PRIORITY_WARN) ? std::cerr : std::cout; + // print to stdout/stderr if priority is higher than debug or debug statements are enabled + if (priority > SDL_LOG_PRIORITY_DEBUG || game->get_configuration()["log"]["debug-to-stdout"]) + { + out << message << std::endl; + } + // handle writing to log file + if (game->get_configuration()["log"]["enabled"]) + { + fs::path path = game->get_configuration()["log"]["output-directory"]; + if (!fs::exists(path)) + { + fs::create_directories(path); + } + // prepend a timestamp to the message + std::time_t now = std::time(nullptr); + std::stringstream stamped_message; + stamped_message << std::put_time(std::localtime(&now), "%F %T ") << message; + // if debug is enabled, append message to debug log file + if (game->get_configuration()["log"]["debug-to-file"]) + { + fs::path debug_path = path / game->get_configuration()["log"]["debug-file-name"]; + std::ofstream debug_stream(debug_path, std::ios_base::app); + debug_stream << stamped_message.str() << std::endl; + } + // only append messages to the info log that are higher than debug priority + if (priority > SDL_LOG_PRIORITY_DEBUG) + { + fs::path info_path = path / game->get_configuration()["log"]["info-file-name"]; + std::ofstream info_stream(info_path, std::ios_base::app); + info_stream << stamped_message.str() << std::endl; + } + } +} + void Game::print_error(const std::string& message) { sfw::print_error(message); @@ -288,6 +307,23 @@ void Game::load_gl_context() log_display_mode(); } +/* Send an info priority message to SDL's log output function, which is overridden by our own member + * function. Category will default to SDL's custom category. + */ +void Game::log(const std::string& message, const int category) const +{ + SDL_LogInfo(category, "%s", message.c_str()); +} + +/* Send a debug priority message to SDL's log output function, which is overridden by our own member + * function. Category will default to SDL's custom category. Using the default category will ensure + * that debug level statements are handled according to the options in the global configuration. + */ +void Game::debug(const std::string& message, const int category) const +{ + SDL_LogDebug(category, "%s", message.c_str()); +} + void Game::log_renderer_info(SDL_RendererInfo& info) { SDL_Log("renderer name: %s, flags: %i, texture formats: %i, " diff --git a/src/Game.hpp b/src/Game.hpp index f2abf37..36c6d43 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -42,16 +42,21 @@ struct FramerateIndicator : Sprite }; -void log(void*, int, SDL_LogPriority, const char*); - -struct Game : Node +class Game : public Node { +private: + + static void sdl_log_override(void*, int, SDL_LogPriority, const char*); + +public: + Game(const Game&) = delete; Game& operator=(const Game&) = delete; Game(Game&&) = delete; Game& operator=(Game&&) = delete; + static const int DEFAULT_SDL_LOG_CATEGORY = SDL_LOG_CATEGORY_CUSTOM; SDL_Window* window; SDL_Renderer* renderer = NULL; SDL_GLContext glcontext = NULL; @@ -78,6 +83,8 @@ struct Game : Node void print_frame_length_history(); void load_sdl_context(); void load_gl_context(); + void log(const std::string&, const int = DEFAULT_SDL_LOG_CATEGORY) const; + void debug(const std::string&, const int = DEFAULT_SDL_LOG_CATEGORY) const; void log_renderer_info(SDL_RendererInfo&); bool log_gl_errors(std::string); void log_display_mode(); @@ -98,7 +105,7 @@ struct Game : Node void set_framerate(int); void handle_quit_event(SDL_Event&); void quit(); - std::string get_class_name() { return "Game"; } + virtual std::string get_class_name() const { return "Game"; } template float weight(T amount) diff --git a/src/Input.hpp b/src/Input.hpp index d316d30..e9feff1 100644 --- a/src/Input.hpp +++ b/src/Input.hpp @@ -60,7 +60,7 @@ struct Input : Node void suppress(); void unsuppress(); bool is_suppressed(); - std::string get_class_name() { return "Input"; } + virtual std::string get_class_name() const { return "Input"; } }; diff --git a/src/Node.cpp b/src/Node.cpp index 94925f9..cc2600e 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -11,8 +11,7 @@ Node::Node() : Node(nullptr) {} Node::Node(Node* parent) : parent(parent) { - std::cout << "Constructing "; - print_branch(); + debug("constructing node " + get_branch_as_string()); } void Node::set_parent(Node* other) @@ -35,6 +34,11 @@ bool Node::is_active() const return active; } +const nlohmann::json& Node::get_configuration() const +{ + return get_root()->configuration.config; +} + nlohmann::json& Node::get_configuration() { return get_root()->configuration.config; @@ -120,27 +124,34 @@ void Node::unsuppress_input() get_root()->get_input().unsuppress(); } -void Node::print_branch() +void Node::log(const std::string& message) const { - Node* current = this; + get_root()->log(message); +} + +void Node::debug(const std::string& message) const +{ + get_root()->debug(message); +} + +const std::string Node::get_branch_as_string() const +{ + const Node* current = this; + std::stringstream branch; while (current != nullptr) { - std::cout << current->get_class_name() << " @ " << current; + branch << current->get_class_name() << " @ " << current; if (current->parent != nullptr) { - std::cout << " -> "; - } - else - { - std::cout << std::endl; + branch << " -> "; } current = current->parent; } + return branch.str(); } Node::~Node() { - // std::cout << "Destructing "; - // print_branch(); + debug("destroying node " + get_branch_as_string()); get_delegate().unsubscribe(this); } diff --git a/src/Node.hpp b/src/Node.hpp index 1a11622..42306a2 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -10,16 +10,18 @@ #include "filesystem.hpp" -struct Game; +class Game; struct Delegate; struct Display; struct Input; struct Box; struct Audio; -struct Node +class Node { +public: + Node *parent; SDL_Texture* canvas = nullptr; bool active = true; @@ -33,6 +35,7 @@ struct Node void set_canvas(SDL_Texture*); SDL_Texture* get_canvas(); bool is_active() const; + const nlohmann::json& get_configuration() const; nlohmann::json& get_configuration(); Delegate& get_delegate(); const Display& get_display() const; @@ -48,8 +51,10 @@ struct Node void suppress_input(); void suppress_input_temporarily(int = 0); void unsuppress_input(); - void print_branch(); - virtual std::string get_class_name() { return "Node"; }; + void log(const std::string&) const; + void debug(const std::string&) const; + const std::string get_branch_as_string() const; + virtual std::string get_class_name() const { return "Node"; }; virtual ~Node(); template diff --git a/src/Recorder.hpp b/src/Recorder.hpp index 964de5a..8fb3f6c 100644 --- a/src/Recorder.hpp +++ b/src/Recorder.hpp @@ -62,7 +62,7 @@ struct Recorder : Node void write_mp4(); void write_audio(Uint8*, int); void update(); - std::string get_class_name() { return "Recorder"; } + virtual std::string get_class_name() const { return "Recorder"; } }; diff --git a/src/Sprite.hpp b/src/Sprite.hpp index 6517315..74792c5 100644 --- a/src/Sprite.hpp +++ b/src/Sprite.hpp @@ -17,7 +17,7 @@ #include "Animation.hpp" #include "Color.hpp" -struct Game; +class Game; struct Frameset; struct Sprite : Node @@ -143,7 +143,7 @@ struct Sprite : Node void render_subsection(SDL_Renderer*, SDL_Texture*, const Box&, const Box&); void set_to_leave_memory_allocated(); void set_to_deallocate_memory(); - virtual std::string get_class_name() { return "Sprite"; } + virtual std::string get_class_name() const { return "Sprite"; } ~Sprite() { unload(); } };