diff --git a/meson.build b/meson.build index b7a72ed..d26cd4f 100644 --- a/meson.build +++ b/meson.build @@ -19,12 +19,19 @@ configure_file( executable( 'mornelune', sources: [ + # Core 'src/main.cpp', - 'src/Game.cpp', + 'src/Services.cpp', + 'src/Service.cpp', + # Logs 'src/logs/Logs.cpp', 'src/logs/FileLogs.cpp', + + # Events + 'src/events/Events.cpp', + 'src/events/Event.cpp', ], dependencies: [ ], diff --git a/src/Game.cpp b/src/Game.cpp index 1dd6e0c..9d35b09 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -1,9 +1,11 @@ #include "Game.hpp" +#include "logs/FileLogs.hpp" +#include "src/events/Events.hpp" +#include "src/logs/Logs.hpp" namespace ml { - /*explicit*/ Game::Game(std::shared_ptr logs) - : m_logs { logs } + /*explicit*/ Game::Game() { } @@ -13,11 +15,11 @@ namespace ml void Game::start() { - m_logs->log(LEVEL_DEBUG, "game started"); + ML_SYS(FileLogs)->log(LEVEL_INFO, "game started"); } void Game::stop() { - m_logs->log(LEVEL_DEBUG, "game stopped"); + ML_SYS(FileLogs)->log(LEVEL_INFO, "game stopped"); } } diff --git a/src/Game.hpp b/src/Game.hpp index c77f89c..644d10a 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -2,24 +2,23 @@ #define ml_GAME_HPP #include "commons.hpp" -#include "logs/Logs.hpp" +#include "Services.hpp" namespace ml { /** - * Manage services and game state. + * Manage game state. **/ class Game { public: - explicit Game(std::shared_ptr logs); + explicit Game(); virtual ~Game(); void start(); void stop(); private: - std::shared_ptr m_logs; }; } diff --git a/src/Service.cpp b/src/Service.cpp new file mode 100644 index 0000000..7dcb5aa --- /dev/null +++ b/src/Service.cpp @@ -0,0 +1,12 @@ +#include "Service.hpp" + +namespace ml +{ + /*explicit*/ Service::Service() + { + } + + /*virtual*/ Service::~Service() + { + } +} diff --git a/src/Service.hpp b/src/Service.hpp new file mode 100644 index 0000000..c9222b2 --- /dev/null +++ b/src/Service.hpp @@ -0,0 +1,19 @@ +#ifndef ml_SERVICE_HPP +#define ml_SERVICE_HPP + +namespace ml +{ + class Service + { + public: + explicit Service(); + virtual ~Service(); + + virtual void init() {}; + virtual void free() {}; + + private: + }; +} + +#endif diff --git a/src/Services.cpp b/src/Services.cpp new file mode 100644 index 0000000..242bc2b --- /dev/null +++ b/src/Services.cpp @@ -0,0 +1,24 @@ +#include "Services.hpp" + +namespace ml +{ + /*static*/ std::shared_ptr Services::instance = nullptr; + + /*static*/ std::shared_ptr Services::get() + { + if (Services::instance == nullptr) + { + Services::instance = std::shared_ptr(new Services); + } + + return Services::instance; + } + + /*explicit*/ Services::Services() + { + } + + /*virtual*/ Services::~Services() + { + } +} diff --git a/src/Services.hpp b/src/Services.hpp new file mode 100644 index 0000000..5a64692 --- /dev/null +++ b/src/Services.hpp @@ -0,0 +1,63 @@ +#ifndef ml_SERVICES_HPP +#define ml_SERVICES_HPP + +#include "commons.hpp" +#include "Service.hpp" + +namespace ml +{ + /** + * Manage game services. + **/ + class Services + { + public: + static std::shared_ptr get(); + + virtual ~Services(); + + template + size_t id(); + + template + void add(Args... args); + + template + std::shared_ptr get(); + + private: + static std::shared_ptr instance; + + size_t m_id = 0; + std::unordered_map> m_services; + + explicit Services(); + }; + + + template + size_t Services::id() + { + static size_t i = m_id++; + return i; + } + + template + void Services::add(Args... args) + { + auto service = std::make_shared(args...); + size_t i = id(); + m_services[i] = service; + } + + template + std::shared_ptr Services::get() + { + size_t i = id(); + return std::static_pointer_cast(m_services[i]); + } + +} + +#endif diff --git a/src/commons.hpp b/src/commons.hpp index 9c2383e..30c3433 100644 --- a/src/commons.hpp +++ b/src/commons.hpp @@ -1,12 +1,19 @@ #ifndef ml_COMMONS_HPP #define ml_COMMONS_HPP +#include +#include +#include #include #include #include #include #include #include +#include + +#define ML ml::Services::get() +#define ML_SYS(SYSTEM) ML->get() #define ML_GEN_ENUM(X) X #define ML_GEN_STRING(X) #X diff --git a/src/events/Event.cpp b/src/events/Event.cpp new file mode 100644 index 0000000..6abd22b --- /dev/null +++ b/src/events/Event.cpp @@ -0,0 +1,13 @@ +#include "Event.hpp" + +namespace ml +{ + /*explicit*/ Event::Event(EventType type) + : m_type { type } + { + } + + /*virtual*/ Event::~Event() + { + } +} diff --git a/src/events/Event.hpp b/src/events/Event.hpp new file mode 100644 index 0000000..c1e3dd1 --- /dev/null +++ b/src/events/Event.hpp @@ -0,0 +1,29 @@ +#ifndef ml_EVENT_HPP +#define ml_EVENT_HPP + +#include "../commons.hpp" + +#define EVENT_TYPES(G) G(EVENT_QUIT) + +namespace ml +{ + ML_ENUM(EventType, EVENT_TYPES); + + /** + * A generic event type. + * @see Events + **/ + class Event + { + public: + explicit Event(EventType type); + virtual ~Event(); + + EventType type() const { return m_type; } + + private: + EventType m_type; + }; +} + +#endif diff --git a/src/events/Events.cpp b/src/events/Events.cpp new file mode 100644 index 0000000..56f9775 --- /dev/null +++ b/src/events/Events.cpp @@ -0,0 +1,8 @@ +#include "Events.hpp" + +namespace ml +{ + /*virtual*/ Events::~Events() + { + } +} diff --git a/src/events/Events.hpp b/src/events/Events.hpp new file mode 100644 index 0000000..2bcc8df --- /dev/null +++ b/src/events/Events.hpp @@ -0,0 +1,119 @@ +#ifndef ml_EVENTS_HPP +#define ml_EVENTS_HPP + +#include "../commons.hpp" +#include "../Service.hpp" +#include "../Services.hpp" +#include "../logs/FileLogs.hpp" + +#include "Event.hpp" + +namespace ml +{ + using listener_t = std::function; + + /** + * Controls event listeners lifetime. + * @see ListenerEntry + **/ + struct EventToken {}; + + struct ListenerEntry { + listener_t listener; + std::optional> token; + }; + + /** + * Publish events to subscribed listeners. + * @see Event + * @see Service + **/ + class Events: public Service + { + public: + explicit Events() = default; + virtual ~Events(); + + template + size_t get(); + + template + void subscribe(listener_t listener, + std::shared_ptr token=nullptr); + + template + void subscribe(ObjectTy* instance, + void (ObjectTy::*method)(Event const&), + std::shared_ptr token=nullptr); + + template + void publish(EventTy const& event); + + private: + std::unordered_map> m_listeners; + size_t m_id = 0; + }; + + template + size_t Events::get() + { + static size_t i = m_id++; + return i; + } + + template + void Events::subscribe(listener_t listener, + std::shared_ptr token) + { + size_t index = get(); + + ListenerEntry entry; + entry.listener = listener; + + if (token) + { + entry.token = token; + } + + if (auto itr = m_listeners.find(index); + itr == std::end(m_listeners)) + { + m_listeners.insert({index, {entry}}); + } + else + { + itr->second.push_back(entry); + } + } + + template + void Events::subscribe(ObjectTy* instance, + void (ObjectTy::*method)(Event const&), + std::shared_ptr token) + { + auto listener = std::bind(method, instance, std::placeholders::_1); + subscribe(listener, token); + } + + template + void Events::publish(EventTy const& event) + { + size_t index = get(); + std::string name = (EventTypeStr[event.type()] + + std::strlen("EVENT_")); + + ML_SYS(FileLogs)->log(LEVEL_DEBUG, "event: " + name); + + for (auto& listener: m_listeners.at(index)) + { + if (listener.token == std::nullopt + || listener.token->lock()) + { + listener.listener(event); + } + } + } +} + +#endif diff --git a/src/logs/Logs.cpp b/src/logs/Logs.cpp index afc58f2..3353bdc 100644 --- a/src/logs/Logs.cpp +++ b/src/logs/Logs.cpp @@ -24,6 +24,7 @@ namespace ml switch (level) { case LEVEL_DEBUG: color = "34"; break; + case LEVEL_INFO: color = "32"; break; default: throw log_error {std::string("cannot find color for log level '") diff --git a/src/logs/Logs.hpp b/src/logs/Logs.hpp index 9ad59ef..9b858cc 100644 --- a/src/logs/Logs.hpp +++ b/src/logs/Logs.hpp @@ -2,8 +2,9 @@ #define ml_LOGS_HPP #include "../commons.hpp" +#include "../Service.hpp" -#define LOG_LEVELS(G) G(LEVEL_DEBUG) +#define LOG_LEVELS(G) G(LEVEL_DEBUG), G(LEVEL_INFO) namespace ml { @@ -13,7 +14,7 @@ namespace ml /** * Logs game activities into an output stream. **/ - class Logs + class Logs: public Service { public: explicit Logs(std::ostream& out); diff --git a/src/main.cpp b/src/main.cpp index 1ad5efd..0699326 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,16 +1,21 @@ #include #include #include "conf.hpp" +#include "commons.hpp" + #include "logs/FileLogs.hpp" +#include "events/Events.hpp" +#include "Services.hpp" #include "Game.hpp" int main(int, char**) { - auto logs = std::make_shared + ML->add (std::filesystem::temp_directory_path() / "mornelune.log"); + ML->add(); - ml::Game game {logs}; + ml::Game game; game.start(); game.stop();