From 6fd7cec7d4bbf5aeb361b3f1337671bb0f9af61b Mon Sep 17 00:00:00 2001
From: Loek Le Blansch <loek@pipeframe.xyz>
Date: Thu, 28 Nov 2024 10:13:11 +0100
Subject: manager mediator refactor

---
 src/crepe/CMakeLists.txt               |   4 +-
 src/crepe/ComponentManager.cpp         |  60 ------------
 src/crepe/ComponentManager.h           | 159 ------------------------------
 src/crepe/ComponentManager.hpp         | 161 ------------------------------
 src/crepe/api/BehaviorScript.cpp       |   6 +-
 src/crepe/api/BehaviorScript.h         |   9 +-
 src/crepe/api/BehaviorScript.hpp       |   4 +-
 src/crepe/api/CMakeLists.txt           |   8 --
 src/crepe/api/EventManager.cpp         |  46 ---------
 src/crepe/api/EventManager.h           | 161 ------------------------------
 src/crepe/api/EventManager.hpp         |  36 -------
 src/crepe/api/GameObject.hpp           |   2 +-
 src/crepe/api/IKeyListener.h           |   3 +-
 src/crepe/api/IMouseListener.h         |   3 +-
 src/crepe/api/LoopManager.cpp          |  25 ++---
 src/crepe/api/LoopManager.h            |  19 +++-
 src/crepe/api/LoopManager.hpp          |   7 +-
 src/crepe/api/SaveManager.cpp          | 173 ---------------------------------
 src/crepe/api/SaveManager.h            | 114 ----------------------
 src/crepe/api/Scene.h                  |   7 +-
 src/crepe/api/SceneManager.cpp         |  34 -------
 src/crepe/api/SceneManager.h           |  53 ----------
 src/crepe/api/SceneManager.hpp         |  25 -----
 src/crepe/api/Script.cpp               |  17 +++-
 src/crepe/api/Script.h                 |  16 +--
 src/crepe/api/Script.hpp               |   8 +-
 src/crepe/manager/CMakeLists.txt       |  20 ++++
 src/crepe/manager/ComponentManager.cpp |  63 ++++++++++++
 src/crepe/manager/ComponentManager.h   | 161 ++++++++++++++++++++++++++++++
 src/crepe/manager/ComponentManager.hpp | 161 ++++++++++++++++++++++++++++++
 src/crepe/manager/EventManager.cpp     |  46 +++++++++
 src/crepe/manager/EventManager.h       | 161 ++++++++++++++++++++++++++++++
 src/crepe/manager/EventManager.hpp     |  36 +++++++
 src/crepe/manager/Manager.cpp          |   6 ++
 src/crepe/manager/Manager.h            |  17 ++++
 src/crepe/manager/Mediator.h           |  29 ++++++
 src/crepe/manager/SaveManager.cpp      | 173 +++++++++++++++++++++++++++++++++
 src/crepe/manager/SaveManager.h        | 114 ++++++++++++++++++++++
 src/crepe/manager/SceneManager.cpp     |  35 +++++++
 src/crepe/manager/SceneManager.h       |  52 ++++++++++
 src/crepe/manager/SceneManager.hpp     |  25 +++++
 src/crepe/system/AnimatorSystem.cpp    |   6 +-
 src/crepe/system/ParticleSystem.cpp    |   6 +-
 src/crepe/system/PhysicsSystem.cpp     |   2 +-
 src/crepe/system/RenderSystem.cpp      |   3 +-
 src/crepe/system/ScriptSystem.cpp      |   2 +-
 src/crepe/system/System.cpp            |   2 +-
 src/crepe/system/System.h              |   4 +-
 48 files changed, 1196 insertions(+), 1088 deletions(-)
 delete mode 100644 src/crepe/ComponentManager.cpp
 delete mode 100644 src/crepe/ComponentManager.h
 delete mode 100644 src/crepe/ComponentManager.hpp
 delete mode 100644 src/crepe/api/EventManager.cpp
 delete mode 100644 src/crepe/api/EventManager.h
 delete mode 100644 src/crepe/api/EventManager.hpp
 delete mode 100644 src/crepe/api/SaveManager.cpp
 delete mode 100644 src/crepe/api/SaveManager.h
 delete mode 100644 src/crepe/api/SceneManager.cpp
 delete mode 100644 src/crepe/api/SceneManager.h
 delete mode 100644 src/crepe/api/SceneManager.hpp
 create mode 100644 src/crepe/manager/CMakeLists.txt
 create mode 100644 src/crepe/manager/ComponentManager.cpp
 create mode 100644 src/crepe/manager/ComponentManager.h
 create mode 100644 src/crepe/manager/ComponentManager.hpp
 create mode 100644 src/crepe/manager/EventManager.cpp
 create mode 100644 src/crepe/manager/EventManager.h
 create mode 100644 src/crepe/manager/EventManager.hpp
 create mode 100644 src/crepe/manager/Manager.cpp
 create mode 100644 src/crepe/manager/Manager.h
 create mode 100644 src/crepe/manager/Mediator.h
 create mode 100644 src/crepe/manager/SaveManager.cpp
 create mode 100644 src/crepe/manager/SaveManager.h
 create mode 100644 src/crepe/manager/SceneManager.cpp
 create mode 100644 src/crepe/manager/SceneManager.h
 create mode 100644 src/crepe/manager/SceneManager.hpp

(limited to 'src/crepe')

diff --git a/src/crepe/CMakeLists.txt b/src/crepe/CMakeLists.txt
index 7e176e7..da9d492 100644
--- a/src/crepe/CMakeLists.txt
+++ b/src/crepe/CMakeLists.txt
@@ -1,13 +1,10 @@
 target_sources(crepe PUBLIC
 	Particle.cpp
-	ComponentManager.cpp
 	Component.cpp
 	Collider.cpp
 )
 
 target_sources(crepe PUBLIC FILE_SET HEADERS FILES
-	ComponentManager.h
-	ComponentManager.hpp
 	Component.h
 	Collider.h
 	ValueBroker.h
@@ -16,6 +13,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
 
 add_subdirectory(api)
 add_subdirectory(facade)
+add_subdirectory(manager)
 add_subdirectory(system)
 add_subdirectory(util)
 
diff --git a/src/crepe/ComponentManager.cpp b/src/crepe/ComponentManager.cpp
deleted file mode 100644
index 5b73009..0000000
--- a/src/crepe/ComponentManager.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#include "api/GameObject.h"
-#include "util/Log.h"
-
-#include "ComponentManager.h"
-#include "types.h"
-
-using namespace crepe;
-using namespace std;
-
-ComponentManager::ComponentManager() { dbg_trace(); }
-ComponentManager::~ComponentManager() { dbg_trace(); }
-
-void ComponentManager::delete_all_components_of_id(game_object_id_t id) {
-	// Do not delete persistent objects
-	if (this->persistent[id]) {
-		return;
-	}
-
-	// Loop through all the types (in the unordered_map<>)
-	for (auto & [type, component_array] : this->components) {
-		// Make sure that the id (that we are looking for) is within the boundaries of the vector<>
-		if (id < component_array.size()) {
-			// Clear the components at this specific id
-			component_array[id].clear();
-		}
-	}
-}
-
-void ComponentManager::delete_all_components() {
-	// Loop through all the types (in the unordered_map<>)
-	for (auto & [type, component_array] : this->components) {
-		// Loop through all the ids (in the vector<>)
-		for (game_object_id_t id = 0; id < component_array.size(); id++) {
-			// Do not delete persistent objects
-			if (!this->persistent[id]) {
-				// Clear the components at this specific id
-				component_array[id].clear();
-			}
-		}
-	}
-
-	this->next_id = 0;
-}
-
-GameObject ComponentManager::new_object(const string & name, const string & tag,
-										const vec2 & position, double rotation, double scale) {
-	// Find the first available id (taking persistent objects into account)
-	while (this->persistent[this->next_id]) {
-		this->next_id++;
-	}
-
-	GameObject object{*this, this->next_id, name, tag, position, rotation, scale};
-	this->next_id++;
-
-	return object;
-}
-
-void ComponentManager::set_persistent(game_object_id_t id, bool persistent) {
-	this->persistent[id] = persistent;
-}
diff --git a/src/crepe/ComponentManager.h b/src/crepe/ComponentManager.h
deleted file mode 100644
index 480124f..0000000
--- a/src/crepe/ComponentManager.h
+++ /dev/null
@@ -1,159 +0,0 @@
-#pragma once
-
-#include <memory>
-#include <typeindex>
-#include <unordered_map>
-#include <vector>
-
-#include "Component.h"
-#include "types.h"
-
-namespace crepe {
-
-class GameObject;
-
-/**
- * \brief Manages all components
- * 
- * This class manages all components. It provides methods to add, delete and get components.
- */
-class ComponentManager {
-	// TODO: This relation should be removed! I (loek) believe that the scene manager should
-	// create/destroy components because the GameObject's are stored in concrete Scene classes,
-	// which will in turn call GameObject's destructor, which will in turn call
-	// ComponentManager::delete_components_by_id or something. This is a pretty major change, so
-	// here is a comment and temporary fix instead :tada:
-	friend class SceneManager;
-
-public:
-	ComponentManager(); // dbg_trace
-	~ComponentManager(); // dbg_trace
-
-	/**
-	 * \brief Create a new game object using the component manager
-	 *
-	 * \param name Metadata::name (required)
-	 * \param tag Metadata::tag (optional, empty by default)
-	 * \param position Transform::position (optional, origin by default)
-	 * \param rotation Transform::rotation (optional, 0 by default)
-	 * \param scale Transform::scale (optional, 1 by default)
-	 *
-	 * \returns GameObject interface
-	 *
-	 * \note This method automatically assigns a new entity ID
-	 */
-	GameObject new_object(const std::string & name, const std::string & tag = "",
-						  const vec2 & position = {0, 0}, double rotation = 0,
-						  double scale = 1);
-
-protected:
-	/**
-	 * GameObject is used as an interface to add/remove components, and the game programmer is
-	 * supposed to use it instead of interfacing with the component manager directly.
-	 */
-	friend class GameObject;
-	/**
-	 * \brief Add a component to the ComponentManager
-	 * 
-	 * This method adds a component to the ComponentManager. The component is created with the
-	 * given arguments and added to the ComponentManager.
-	 * 
-	 * \tparam T The type of the component
-	 * \tparam Args The types of the arguments
-	 * \param id The id of the GameObject this component belongs to
-	 * \param args The arguments to create the component
-	 * \return The created component
-	 */
-	template <typename T, typename... Args>
-	T & add_component(game_object_id_t id, Args &&... args);
-	/**
-	 * \brief Delete all components of a specific type and id
-	 * 
-	 * This method deletes all components of a specific type and id.
-	 * 
-	 * \tparam T The type of the component
-	 * \param id The id of the GameObject this component belongs to
-	 */
-	template <typename T>
-	void delete_components_by_id(game_object_id_t id);
-	/**
-	 * \brief Delete all components of a specific type
-	 * 
-	 * This method deletes all components of a specific type.
-	 * 
-	 * \tparam T The type of the component
-	 */
-	template <typename T>
-	void delete_components();
-	/**
-	 * \brief Delete all components of a specific id
-	 * 
-	 * This method deletes all components of a specific id.
-	 * 
-	 * \param id The id of the GameObject this component belongs to
-	 */
-	void delete_all_components_of_id(game_object_id_t id);
-	/**
-	 * \brief Delete all components
-	 * 
-	 * This method deletes all components.
-	 */
-	void delete_all_components();
-	/**
-	 * \brief Set a GameObject as persistent
-	 *
-	 * This method sets a GameObject as persistent. If a GameObject is persistent, its
-	 * components will not be deleted.
-	 *
-	 * \param id The id of the GameObject to set as persistent
-	 * \param persistent The persistent flag
-	 */
-	void set_persistent(game_object_id_t id, bool persistent);
-
-public:
-	/**
-	 * \brief Get all components of a specific type and id
-	 * 
-	 * This method gets all components of a specific type and id.
-	 * 
-	 * \tparam T The type of the component
-	 * \param id The id of the GameObject this component belongs to
-	 * \return A vector of all components of the specific type and id
-	 */
-	template <typename T>
-	RefVector<T> get_components_by_id(game_object_id_t id) const;
-	/**
-	 * \brief Get all components of a specific type
-	 * 
-	 * This method gets all components of a specific type.
-	 * 
-	 * \tparam T The type of the component
-	 * \return A vector of all components of the specific type
-	 */
-	template <typename T>
-	RefVector<T> get_components_by_type() const;
-
-private:
-	/**
-	 * \brief The components
-	 * 
-	 * This unordered_map stores all components. The key is the type of the component and the
-	 * value is a vector of vectors of unique pointers to the components.
-	 *
-	 * Every component type has its own vector of vectors of unique pointers to the components.
-	 * The first vector is for the ids of the GameObjects and the second vector is for the
-	 * components (because a GameObject might have multiple components).
-	 */
-	std::unordered_map<std::type_index, std::vector<std::vector<std::unique_ptr<Component>>>>
-		components;
-
-	//! Persistent flag for each GameObject
-	std::unordered_map<game_object_id_t, bool> persistent;
-
-	//! ID of next GameObject allocated by \c ComponentManager::new_object
-	game_object_id_t next_id = 0;
-};
-
-} // namespace crepe
-
-#include "ComponentManager.hpp"
diff --git a/src/crepe/ComponentManager.hpp b/src/crepe/ComponentManager.hpp
deleted file mode 100644
index ffb38ec..0000000
--- a/src/crepe/ComponentManager.hpp
+++ /dev/null
@@ -1,161 +0,0 @@
-#pragma once
-
-#include <type_traits>
-
-#include "ComponentManager.h"
-#include "types.h"
-
-namespace crepe {
-
-template <class T, typename... Args>
-T & ComponentManager::add_component(game_object_id_t id, Args &&... args) {
-	using namespace std;
-
-	static_assert(is_base_of<Component, T>::value,
-				  "add_component must recieve a derivative class of Component");
-
-	// Determine the type of T (this is used as the key of the unordered_map<>)
-	type_index type = typeid(T);
-
-	// Check if this component type is already in the unordered_map<>
-	if (this->components.find(type) == this->components.end()) {
-		//If not, create a new (empty) vector<> of vector<unique_ptr<Component>>
-		this->components[type] = vector<vector<unique_ptr<Component>>>();
-	}
-
-	// Resize the vector<> if the id is greater than the current size
-	if (id >= this->components[type].size()) {
-		// Initialize new slots to nullptr (resize does automatically init to nullptr)
-		this->components[type].resize(id + 1);
-	}
-
-	// Create a new component of type T (arguments directly forwarded). The
-	// constructor must be called by ComponentManager.
-	T * instance_ptr = new T(id, forward<Args>(args)...);
-	if (instance_ptr == nullptr) throw std::bad_alloc();
-
-	T & instance_ref = *instance_ptr;
-	unique_ptr<T> instance = unique_ptr<T>(instance_ptr);
-
-	// Check if the vector size is not greater than get_instances_max
-	int max_instances = instance->get_instances_max();
-	if (max_instances != -1 && components[type][id].size() >= max_instances) {
-		throw std::runtime_error(
-			"Exceeded maximum number of instances for this component type");
-	}
-
-	// store its unique_ptr in the vector<>
-	this->components[type][id].push_back(std::move(instance));
-
-	return instance_ref;
-}
-
-template <typename T>
-void ComponentManager::delete_components_by_id(game_object_id_t id) {
-	using namespace std;
-
-	// Do not delete persistent objects
-	if (this->persistent[id]) {
-		return;
-	}
-
-	// Determine the type of T (this is used as the key of the unordered_map<>)
-	type_index type = typeid(T);
-
-	// Find the type (in the unordered_map<>)
-	if (this->components.find(type) != this->components.end()) {
-		// Get the correct vector<>
-		vector<vector<unique_ptr<Component>>> & component_array = this->components[type];
-
-		// Make sure that the id (that we are looking for) is within the boundaries of the vector<>
-		if (id < component_array.size()) {
-			// Clear the whole vector<> of this specific type and id
-			component_array[id].clear();
-		}
-	}
-}
-
-template <typename T>
-void ComponentManager::delete_components() {
-	// Determine the type of T (this is used as the key of the unordered_map<>)
-	std::type_index type = typeid(T);
-
-	if (this->components.find(type) == this->components.end()) return;
-
-	// Loop through the whole vector<> of this specific type
-	for (game_object_id_t i = 0; i < this->components[type].size(); ++i) {
-		// Do not delete persistent objects
-		if (!this->persistent[i]) {
-			this->components[type][i].clear();
-		}
-	}
-}
-
-template <typename T>
-RefVector<T> ComponentManager::get_components_by_id(game_object_id_t id) const {
-	using namespace std;
-
-	// Determine the type of T (this is used as the key of the unordered_map<>)
-	type_index type = typeid(T);
-
-	// Create an empty vector<>
-	RefVector<T> component_vector;
-
-	if (this->components.find(type) == this->components.end()) return component_vector;
-
-	// Get the correct vector<>
-	const vector<vector<unique_ptr<Component>>> & component_array = this->components.at(type);
-
-	// Make sure that the id (that we are looking for) is within the boundaries of the vector<>
-	if (id >= component_array.size()) return component_vector;
-
-	// Loop trough the whole vector<>
-	for (const unique_ptr<Component> & component_ptr : component_array[id]) {
-		// Cast the unique_ptr to a raw pointer
-		T * casted_component = static_cast<T *>(component_ptr.get());
-
-		if (casted_component == nullptr) continue;
-
-		// Add the dereferenced raw pointer to the vector<>
-		component_vector.push_back(*casted_component);
-	}
-
-	return component_vector;
-}
-
-template <typename T>
-RefVector<T> ComponentManager::get_components_by_type() const {
-	using namespace std;
-
-	// Determine the type of T (this is used as the key of the unordered_map<>)
-	type_index type = typeid(T);
-
-	// Create an empty vector<>
-	RefVector<T> component_vector;
-
-	// Find the type (in the unordered_map<>)
-	if (this->components.find(type) == this->components.end()) return component_vector;
-
-	// Get the correct vector<>
-	const vector<vector<unique_ptr<Component>>> & component_array = this->components.at(type);
-
-	// Loop through the whole vector<>
-	for (const vector<unique_ptr<Component>> & component : component_array) {
-		// Loop trough the whole vector<>
-		for (const unique_ptr<Component> & component_ptr : component) {
-			// Cast the unique_ptr to a raw pointer
-			T * casted_component = static_cast<T *>(component_ptr.get());
-
-			// Ensure that the cast was successful
-			if (casted_component == nullptr) continue;
-
-			// Add the dereferenced raw pointer to the vector<>
-			component_vector.emplace_back(ref(*casted_component));
-		}
-	}
-
-	// Return the vector<>
-	return component_vector;
-}
-
-} // namespace crepe
diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp
index 7bbace0..d22afdf 100644
--- a/src/crepe/api/BehaviorScript.cpp
+++ b/src/crepe/api/BehaviorScript.cpp
@@ -4,12 +4,12 @@
 
 using namespace crepe;
 
-BehaviorScript::BehaviorScript(game_object_id_t id, ComponentManager & mgr)
+BehaviorScript::BehaviorScript(game_object_id_t id, Mediator & mediator)
 	: Component(id),
-	  component_manager(mgr) {}
+	  mediator(mediator) {}
 
 template <>
 BehaviorScript & GameObject::add_component<BehaviorScript>() {
 	ComponentManager & mgr = this->component_manager;
-	return mgr.add_component<BehaviorScript>(this->id, mgr);
+	return mgr.add_component<BehaviorScript>(this->id, mgr.mediator);
 }
diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h
index d556fe5..3909b96 100644
--- a/src/crepe/api/BehaviorScript.h
+++ b/src/crepe/api/BehaviorScript.h
@@ -23,14 +23,13 @@ class BehaviorScript : public Component {
 protected:
 	/**
 	 * \param id Parent \c GameObject id
-	 * \param component_manager Reference to component manager (passed through to \c Script
-	 * instance)
+	 * \param mediator Mediator reference
 	 *
 	 * \note Calls to this constructor (should) always pass through \c GameObject::add_component,
 	 * which has an exception for this specific component type. This was done so the user does
 	 * not have to pass references used within \c Script to each \c BehaviorScript instance.
 	 */
-	BehaviorScript(game_object_id_t id, ComponentManager & component_manager);
+	BehaviorScript(game_object_id_t id, Mediator & mediator);
 	//! Only ComponentManager is allowed to instantiate BehaviorScript
 	friend class ComponentManager;
 
@@ -55,8 +54,8 @@ protected:
 	friend class ScriptSystem;
 
 protected:
-	//! Reference to component manager (passed to Script)
-	ComponentManager & component_manager;
+	//! Reference mediator
+	Mediator & mediator;
 };
 
 /**
diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp
index bd59337..6de0157 100644
--- a/src/crepe/api/BehaviorScript.hpp
+++ b/src/crepe/api/BehaviorScript.hpp
@@ -14,11 +14,11 @@ BehaviorScript & BehaviorScript::set_script(Args &&... args) {
 	dbg_trace();
 	static_assert(std::is_base_of<Script, T>::value);
 	Script * s = new T(std::forward<Args>(args)...);
+	Mediator & mediator = this->mediator;
 
 	s->game_object_id = this->game_object_id;
 	s->active = this->active;
-	s->component_manager = this->component_manager;
-	s->event_manager = EventManager::get_instance();
+	s->mediator = mediator;
 
 	this->script = std::unique_ptr<Script>(s);
 	return *this;
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index 50c51ed..7da9dca 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -9,13 +9,10 @@ target_sources(crepe PUBLIC
 	Texture.cpp
 	AssetManager.cpp
 	Sprite.cpp
-	SaveManager.cpp
 	Config.cpp
 	Metadata.cpp
-	SceneManager.cpp
 	Camera.cpp
 	Animator.cpp
-	EventManager.cpp
 	IKeyListener.cpp
 	IMouseListener.cpp
 	LoopManager.cpp
@@ -41,15 +38,10 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
 	Texture.h 
 	AssetManager.h 
 	AssetManager.hpp
-	SaveManager.h
 	Scene.h
 	Metadata.h
-	SceneManager.h
-	SceneManager.hpp
 	Camera.h
 	Animator.h
-	EventManager.h
-	EventManager.hpp
 	EventHandler.h
 	EventHandler.hpp
 	Event.h
diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/api/EventManager.cpp
deleted file mode 100644
index 20f0dd3..0000000
--- a/src/crepe/api/EventManager.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-#include "EventManager.h"
-
-using namespace crepe;
-using namespace std;
-
-EventManager & EventManager::get_instance() {
-	static EventManager instance;
-	return instance;
-}
-
-void EventManager::dispatch_events() {
-	for (auto & event : this->events_queue) {
-		this->handle_event(event.type, event.channel, *event.event.get());
-	}
-	this->events_queue.clear();
-}
-
-void EventManager::handle_event(type_index type, event_channel_t channel, const Event & data) {
-	auto handlers_it = this->subscribers.find(type);
-	if (handlers_it == this->subscribers.end()) return;
-
-	vector<CallbackEntry> & handlers = handlers_it->second;
-	for (auto & handler : handlers) {
-		bool check_channel = handler.channel != CHANNEL_ALL || channel != CHANNEL_ALL;
-		if (check_channel && handler.channel != channel) continue;
-
-		bool handled = handler.callback->exec(data);
-		if (handled) return;
-	}
-}
-
-void EventManager::clear() {
-	this->subscribers.clear();
-	this->events_queue.clear();
-}
-
-void EventManager::unsubscribe(subscription_t id) {
-	for (auto & [event_type, handlers] : this->subscribers) {
-		for (auto it = handlers.begin(); it != handlers.end(); it++) {
-			// find listener with subscription id
-			if ((*it).id != id) continue;
-			it = handlers.erase(it);
-			return;
-		}
-	}
-}
diff --git a/src/crepe/api/EventManager.h b/src/crepe/api/EventManager.h
deleted file mode 100644
index 1a33023..0000000
--- a/src/crepe/api/EventManager.h
+++ /dev/null
@@ -1,161 +0,0 @@
-#pragma once
-
-#include <memory>
-#include <typeindex>
-#include <unordered_map>
-#include <vector>
-
-#include "Event.h"
-#include "EventHandler.h"
-
-namespace crepe {
-
-//! Event listener unique ID
-typedef size_t subscription_t;
-
-/**
- * \brief Event channel
- *
- * Events can be sent to a specific channel, which prevents listeners on other channels from
- * being called. The default channel is EventManager::CHANNEL_ALL, which calls all listeners.
- */
-typedef size_t event_channel_t;
-
-/**
- * \class EventManager
- * \brief Manages event subscriptions, triggers, and queues, enabling decoupled event handling.
- * 
- * The `EventManager` acts as a centralized event system. It allows for registering callbacks
- * for specific event types, triggering events synchronously, queueing events for later
- * processing, and managing subscriptions via unique identifiers.
- */
-class EventManager {
-public:
-	static constexpr const event_channel_t CHANNEL_ALL = -1;
-
-	/**
-	 * \brief Get the singleton instance of the EventManager.
-	 * 
-	 * This method returns the unique instance of the EventManager, creating it if it
-	 * doesn't already exist. Ensures only one instance is active in the program.
-	 * 
-	 * \return Reference to the singleton instance of the EventManager.
-	 */
-	static EventManager & get_instance();
-
-	/**
-	 * \brief Subscribe to a specific event type.
-	 * 
-	 * Registers a callback for a given event type and optional channel. Each callback
-	 * is assigned a unique subscription ID that can be used for later unsubscription.
-	 * 
-	 * \tparam EventType The type of the event to subscribe to.
-	 * \param callback The callback function to be invoked when the event is triggered.
-	 * \param channel The channel number to subscribe to (default is CHANNEL_ALL, which listens to all channels).
-	 * \return A unique subscription ID associated with the registered callback.
-	 */
-	template <typename EventType>
-	subscription_t subscribe(const EventHandler<EventType> & callback,
-							 event_channel_t channel = CHANNEL_ALL);
-
-	/**
-	 * \brief Unsubscribe a previously registered callback.
-	 * 
-	 * Removes a callback from the subscription list based on its unique subscription ID.
-	 * 
-	 * \param event_id The unique subscription ID of the callback to remove.
-	 */
-	void unsubscribe(subscription_t event_id);
-
-	/**
-	 * \brief Trigger an event immediately.
-	 * 
-	 * Synchronously invokes all registered callbacks for the given event type on the specified channel.
-	 * 
-	 * \tparam EventType The type of the event to trigger.
-	 * \param event The event instance to pass to the callbacks.
-	 * \param channel The channel to trigger the event on (default is CHANNEL_ALL, which triggers on all channels).
-	 */
-	template <typename EventType>
-	void trigger_event(const EventType & event = {}, event_channel_t channel = CHANNEL_ALL);
-
-	/**
-	 * \brief Queue an event for later processing.
-	 * 
-	 * Adds an event to the event queue to be processed during the next call to `dispatch_events`.
-	 * 
-	 * \tparam EventType The type of the event to queue.
-	 * \param event The event instance to queue.
-	 * \param channel The channel to associate with the event (default is CHANNEL_ALL).
-	 */
-	template <typename EventType>
-	void queue_event(const EventType & event = {}, event_channel_t channel = CHANNEL_ALL);
-
-	/**
-	 * \brief Process all queued events.
-	 * 
-	 * Iterates through the event queue and triggers callbacks for each queued event.
-	 * Events are removed from the queue once processed.
-	 */
-	void dispatch_events();
-
-	/**
-	 * \brief Clear all subscriptions.
-	 * 
-	 * Removes all registered event handlers and clears the subscription list.
-	 */
-	void clear();
-
-private:
-	/**
-	 * \brief Default constructor for the EventManager.
-	 * 
-	 * Constructor is private to enforce the singleton pattern.
-	 */
-	EventManager() = default;
-
-	/**
-	 * \struct QueueEntry
-	 * \brief Represents an entry in the event queue.
-	 */
-	struct QueueEntry {
-		std::unique_ptr<Event> event; ///< The event instance.
-		event_channel_t channel = CHANNEL_ALL; ///< The channel associated with the event.
-		std::type_index type; ///< The type of the event.
-	};
-
-	/**
-	 * \brief Internal event handler
-	 *
-	 * This function processes a single event, and is used to process events both during
-	 * EventManager::dispatch_events and inside EventManager::trigger_event
-	 *
-	 * \param type \c typeid of concrete Event class
-	 * \param channel Event channel
-	 * \param data Event data
-	 */
-	void handle_event(std::type_index type, event_channel_t channel, const Event & data);
-
-	/**
-	 * \struct CallbackEntry
-	 * \brief Represents a registered event handler callback.
-	 */
-	struct CallbackEntry {
-		std::unique_ptr<IEventHandlerWrapper> callback; ///< The callback function wrapper.
-		event_channel_t channel = CHANNEL_ALL; ///< The channel this callback listens to.
-		subscription_t id = -1; ///< Unique subscription ID.
-	};
-
-	//! The queue of events to be processed during dispatch.
-	std::vector<QueueEntry> events_queue;
-
-	//! A map of event type to registered callbacks.
-	std::unordered_map<std::type_index, std::vector<CallbackEntry>> subscribers;
-
-	//! Counter to generate unique subscription IDs.
-	subscription_t subscription_counter = 0;
-};
-
-} // namespace crepe
-
-#include "EventManager.hpp"
diff --git a/src/crepe/api/EventManager.hpp b/src/crepe/api/EventManager.hpp
deleted file mode 100644
index a5f4556..0000000
--- a/src/crepe/api/EventManager.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-#include "EventManager.h"
-
-namespace crepe {
-
-template <typename EventType>
-subscription_t EventManager::subscribe(const EventHandler<EventType> & callback,
-									   event_channel_t channel) {
-	subscription_counter++;
-	std::type_index event_type = typeid(EventType);
-	std::unique_ptr<EventHandlerWrapper<EventType>> handler
-		= std::make_unique<EventHandlerWrapper<EventType>>(callback);
-	std::vector<CallbackEntry> & handlers = this->subscribers[event_type];
-	handlers.emplace_back(CallbackEntry{
-		.callback = std::move(handler), .channel = channel, .id = subscription_counter});
-	return subscription_counter;
-}
-
-template <typename EventType>
-void EventManager::queue_event(const EventType & event, event_channel_t channel) {
-	static_assert(std::is_base_of<Event, EventType>::value,
-				  "EventType must derive from Event");
-	this->events_queue.push_back(QueueEntry{
-		.event = std::make_unique<EventType>(event),
-		.channel = channel,
-		.type = typeid(EventType),
-	});
-}
-
-template <typename EventType>
-void EventManager::trigger_event(const EventType & event, event_channel_t channel) {
-	this->handle_event(typeid(EventType), channel, event);
-}
-
-} // namespace crepe
diff --git a/src/crepe/api/GameObject.hpp b/src/crepe/api/GameObject.hpp
index 17b17d7..a6b45b0 100644
--- a/src/crepe/api/GameObject.hpp
+++ b/src/crepe/api/GameObject.hpp
@@ -1,6 +1,6 @@
 #pragma once
 
-#include "../ComponentManager.h"
+#include "../manager/ComponentManager.h"
 
 #include "GameObject.h"
 
diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h
index 328a4c2..6ded107 100644
--- a/src/crepe/api/IKeyListener.h
+++ b/src/crepe/api/IKeyListener.h
@@ -1,8 +1,9 @@
 #pragma once
 
+#include "../manager/EventManager.h"
+
 #include "Event.h"
 #include "EventHandler.h"
-#include "EventManager.h"
 
 namespace crepe {
 
diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h
index 15e1619..9e4fdf7 100644
--- a/src/crepe/api/IMouseListener.h
+++ b/src/crepe/api/IMouseListener.h
@@ -1,8 +1,9 @@
 #pragma once
 
+#include "../manager/EventManager.h"
+
 #include "Event.h"
 #include "EventHandler.h"
-#include "EventManager.h"
 
 namespace crepe {
 
diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp
index 7edf4d1..b277185 100644
--- a/src/crepe/api/LoopManager.cpp
+++ b/src/crepe/api/LoopManager.cpp
@@ -1,5 +1,3 @@
-#include "../facade/SDLContext.h"
-
 #include "../system/AnimatorSystem.h"
 #include "../system/CollisionSystem.h"
 #include "../system/ParticleSystem.h"
@@ -8,12 +6,14 @@
 #include "../system/ScriptSystem.h"
 
 #include "LoopManager.h"
-#include "LoopTimer.h"
 
 using namespace crepe;
 using namespace std;
 
 LoopManager::LoopManager() {
+	this->mediator.component_manager = this->component_manager;
+	this->mediator.scene_manager = this->scene_manager;
+
 	this->load_system<AnimatorSystem>();
 	this->load_system<CollisionSystem>();
 	this->load_system<ParticleSystem>();
@@ -23,7 +23,7 @@ LoopManager::LoopManager() {
 }
 
 void LoopManager::process_input() {
-	SDLContext::get_instance().handle_events(this->game_running);
+	this->sdl_context.handle_events(this->game_running);
 }
 
 void LoopManager::start() {
@@ -35,7 +35,7 @@ void LoopManager::set_running(bool running) { this->game_running = running; }
 void LoopManager::fixed_update() {}
 
 void LoopManager::loop() {
-	LoopTimer & timer = LoopTimer::get_instance();
+	LoopTimer & timer = this->loop_timer;
 	timer.start();
 
 	while (game_running) {
@@ -55,15 +55,18 @@ void LoopManager::loop() {
 }
 
 void LoopManager::setup() {
+	LoopTimer & timer = this->loop_timer;
+
 	this->game_running = true;
-	LoopTimer::get_instance().start();
-	LoopTimer::get_instance().set_fps(200);
+	timer.start();
+	timer.set_fps(200);
 }
 
 void LoopManager::render() {
-	if (this->game_running) {
-		this->get_system<RenderSystem>().update();
-	}
+	if (!this->game_running) return;
+
+	this->get_system<RenderSystem>().update();
 }
 
-void LoopManager::update() { LoopTimer & timer = LoopTimer::get_instance(); }
+void LoopManager::update() {}
+
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
index 13e6dac..6ea5ccc 100644
--- a/src/crepe/api/LoopManager.h
+++ b/src/crepe/api/LoopManager.h
@@ -2,9 +2,12 @@
 
 #include <memory>
 
-#include "../ComponentManager.h"
+#include "../manager/ComponentManager.h"
+#include "../manager/SceneManager.h"
 #include "../system/System.h"
-#include "api/SceneManager.h"
+#include "../facade/SDLContext.h"
+
+#include "LoopTimer.h"
 
 namespace crepe {
 
@@ -85,10 +88,18 @@ private:
 	bool game_running = false;
 
 private:
+	//! Global context
+	Mediator mediator;
+
 	//! Component manager instance
-	ComponentManager component_manager{};
+	ComponentManager component_manager{mediator};
 	//! Scene manager instance
-	SceneManager scene_manager{component_manager};
+	SceneManager scene_manager{mediator};
+
+	//! SDL context \todo no more singletons!
+	SDLContext & sdl_context = SDLContext::get_instance();
+	//! Loop timer \todo no more singletons!
+	LoopTimer & loop_timer = LoopTimer::get_instance();
 
 private:
 	/**
diff --git a/src/crepe/api/LoopManager.hpp b/src/crepe/api/LoopManager.hpp
index 9cf470b..cd6bd02 100644
--- a/src/crepe/api/LoopManager.hpp
+++ b/src/crepe/api/LoopManager.hpp
@@ -38,8 +38,11 @@ void LoopManager::load_system() {
 	static_assert(is_base_of<System, T>::value,
 				  "load_system must recieve a derivative class of System");
 
-	System * system = new T(this->component_manager);
-	this->systems[typeid(T)] = unique_ptr<System>(system);
+	System * system = new T(this->mediator);
+	const type_info & type = typeid(T);
+	if (this->systems.contains(type))
+		throw runtime_error(format("LoopManager: {} is already initialized", type.name()));
+	this->systems[type] = unique_ptr<System>(system);
 }
 
 } // namespace crepe
diff --git a/src/crepe/api/SaveManager.cpp b/src/crepe/api/SaveManager.cpp
deleted file mode 100644
index c5f43ea..0000000
--- a/src/crepe/api/SaveManager.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-#include "../facade/DB.h"
-#include "../util/Log.h"
-
-#include "Config.h"
-#include "SaveManager.h"
-#include "ValueBroker.h"
-
-using namespace std;
-using namespace crepe;
-
-template <>
-string SaveManager::serialize(const string & value) const noexcept {
-	return value;
-}
-template <typename T>
-string SaveManager::serialize(const T & value) const noexcept {
-	return to_string(value);
-}
-template string SaveManager::serialize(const uint8_t &) const noexcept;
-template string SaveManager::serialize(const int8_t &) const noexcept;
-template string SaveManager::serialize(const uint16_t &) const noexcept;
-template string SaveManager::serialize(const int16_t &) const noexcept;
-template string SaveManager::serialize(const uint32_t &) const noexcept;
-template string SaveManager::serialize(const int32_t &) const noexcept;
-template string SaveManager::serialize(const uint64_t &) const noexcept;
-template string SaveManager::serialize(const int64_t &) const noexcept;
-template string SaveManager::serialize(const float &) const noexcept;
-template string SaveManager::serialize(const double &) const noexcept;
-
-template <>
-uint64_t SaveManager::deserialize(const string & value) const noexcept {
-	try {
-		return stoul(value);
-	} catch (std::invalid_argument &) {
-		return 0;
-	}
-}
-template <>
-int64_t SaveManager::deserialize(const string & value) const noexcept {
-	try {
-		return stol(value);
-	} catch (std::invalid_argument &) {
-		return 0;
-	}
-}
-template <>
-float SaveManager::deserialize(const string & value) const noexcept {
-	try {
-		return stof(value);
-	} catch (std::invalid_argument &) {
-		return 0;
-	}
-	return stof(value);
-}
-template <>
-double SaveManager::deserialize(const string & value) const noexcept {
-	try {
-		return stod(value);
-	} catch (std::invalid_argument &) {
-		return 0;
-	}
-}
-template <>
-string SaveManager::deserialize(const string & value) const noexcept {
-	return value;
-}
-
-template <>
-uint8_t SaveManager::deserialize(const string & value) const noexcept {
-	return deserialize<uint64_t>(value);
-}
-template <>
-int8_t SaveManager::deserialize(const string & value) const noexcept {
-	return deserialize<int64_t>(value);
-}
-template <>
-uint16_t SaveManager::deserialize(const string & value) const noexcept {
-	return deserialize<uint64_t>(value);
-}
-template <>
-int16_t SaveManager::deserialize(const string & value) const noexcept {
-	return deserialize<int64_t>(value);
-}
-template <>
-uint32_t SaveManager::deserialize(const string & value) const noexcept {
-	return deserialize<uint64_t>(value);
-}
-template <>
-int32_t SaveManager::deserialize(const string & value) const noexcept {
-	return deserialize<int64_t>(value);
-}
-
-SaveManager::SaveManager() { dbg_trace(); }
-
-SaveManager & SaveManager::get_instance() {
-	dbg_trace();
-	static SaveManager instance;
-	return instance;
-}
-
-DB & SaveManager::get_db() {
-	Config & cfg = Config::get_instance();
-	// TODO: make this path relative to XDG_DATA_HOME on Linux and whatever the
-	// default equivalent is on Windows using some third party library
-	static DB db(cfg.savemgr.location);
-	return db;
-}
-
-bool SaveManager::has(const string & key) {
-	DB & db = this->get_db();
-	return db.has(key);
-}
-
-template <>
-void SaveManager::set(const string & key, const string & value) {
-	DB & db = this->get_db();
-	db.set(key, value);
-}
-template <typename T>
-void SaveManager::set(const string & key, const T & value) {
-	DB & db = this->get_db();
-	db.set(key, std::to_string(value));
-}
-template void SaveManager::set(const string &, const uint8_t &);
-template void SaveManager::set(const string &, const int8_t &);
-template void SaveManager::set(const string &, const uint16_t &);
-template void SaveManager::set(const string &, const int16_t &);
-template void SaveManager::set(const string &, const uint32_t &);
-template void SaveManager::set(const string &, const int32_t &);
-template void SaveManager::set(const string &, const uint64_t &);
-template void SaveManager::set(const string &, const int64_t &);
-template void SaveManager::set(const string &, const float &);
-template void SaveManager::set(const string &, const double &);
-
-template <typename T>
-ValueBroker<T> SaveManager::get(const string & key, const T & default_value) {
-	if (!this->has(key)) this->set<T>(key, default_value);
-	return this->get<T>(key);
-}
-template ValueBroker<uint8_t> SaveManager::get(const string &, const uint8_t &);
-template ValueBroker<int8_t> SaveManager::get(const string &, const int8_t &);
-template ValueBroker<uint16_t> SaveManager::get(const string &, const uint16_t &);
-template ValueBroker<int16_t> SaveManager::get(const string &, const int16_t &);
-template ValueBroker<uint32_t> SaveManager::get(const string &, const uint32_t &);
-template ValueBroker<int32_t> SaveManager::get(const string &, const int32_t &);
-template ValueBroker<uint64_t> SaveManager::get(const string &, const uint64_t &);
-template ValueBroker<int64_t> SaveManager::get(const string &, const int64_t &);
-template ValueBroker<float> SaveManager::get(const string &, const float &);
-template ValueBroker<double> SaveManager::get(const string &, const double &);
-template ValueBroker<string> SaveManager::get(const string &, const string &);
-
-template <typename T>
-ValueBroker<T> SaveManager::get(const string & key) {
-	T value;
-	return {
-		[this, key](const T & target) { this->set<T>(key, target); },
-		[this, key, value]() mutable -> const T & {
-			value = this->deserialize<T>(this->get_db().get(key));
-			return value;
-		},
-	};
-}
-template ValueBroker<uint8_t> SaveManager::get(const string &);
-template ValueBroker<int8_t> SaveManager::get(const string &);
-template ValueBroker<uint16_t> SaveManager::get(const string &);
-template ValueBroker<int16_t> SaveManager::get(const string &);
-template ValueBroker<uint32_t> SaveManager::get(const string &);
-template ValueBroker<int32_t> SaveManager::get(const string &);
-template ValueBroker<uint64_t> SaveManager::get(const string &);
-template ValueBroker<int64_t> SaveManager::get(const string &);
-template ValueBroker<float> SaveManager::get(const string &);
-template ValueBroker<double> SaveManager::get(const string &);
-template ValueBroker<string> SaveManager::get(const string &);
diff --git a/src/crepe/api/SaveManager.h b/src/crepe/api/SaveManager.h
deleted file mode 100644
index 3d8c852..0000000
--- a/src/crepe/api/SaveManager.h
+++ /dev/null
@@ -1,114 +0,0 @@
-#pragma once
-
-#include <memory>
-
-#include "../ValueBroker.h"
-
-namespace crepe {
-
-class DB;
-
-/**
- * \brief Save data manager
- *
- * This class provides access to a simple key-value store that stores
- * - integers (8-64 bit, signed or unsigned)
- * - real numbers (float or double)
- * - string (std::string)
- *
- * The underlying database is a key-value store.
- */
-class SaveManager {
-public:
-	/**
-	 * \brief Get a read/write reference to a value and initialize it if it does not yet exist
-	 *
-	 * \param key  The value key
-	 * \param default_value  Value to initialize \c key with if it does not already exist in the
-	 * database
-	 *
-	 * \return Read/write reference to the value
-	 */
-	template <typename T>
-	ValueBroker<T> get(const std::string & key, const T & default_value);
-
-	/**
-	 * \brief Get a read/write reference to a value
-	 *
-	 * \param key  The value key
-	 *
-	 * \return Read/write reference to the value
-	 *
-	 * \note Attempting to read this value before it is initialized (i.e. set) will result in an
-	 * exception
-	 */
-	template <typename T>
-	ValueBroker<T> get(const std::string & key);
-
-	/**
-	 * \brief Set a value directly
-	 *
-	 * \param key  The value key
-	 * \param value  The value to store
-	 */
-	template <typename T>
-	void set(const std::string & key, const T & value);
-
-	/**
-	 * \brief Check if the save file has a value for this \c key
-	 *
-	 * \param key  The value key
-	 *
-	 * \returns True if the key exists, or false if it does not
-	 */
-	bool has(const std::string & key);
-
-private:
-	SaveManager();
-	virtual ~SaveManager() = default;
-
-private:
-	/**
-	 * \brief Serialize an arbitrary value to STL string
-	 *
-	 * \tparam T  Type of arbitrary value
-	 *
-	 * \returns String representation of value
-	 */
-	template <typename T>
-	std::string serialize(const T &) const noexcept;
-
-	/**
-	 * \brief Deserialize an STL string back to type \c T
-	 *
-	 * \tparam T  Type of value
-	 * \param value  Serialized value
-	 *
-	 * \returns Deserialized value
-	 */
-	template <typename T>
-	T deserialize(const std::string & value) const noexcept;
-
-public:
-	// singleton
-	static SaveManager & get_instance();
-	SaveManager(const SaveManager &) = delete;
-	SaveManager(SaveManager &&) = delete;
-	SaveManager & operator=(const SaveManager &) = delete;
-	SaveManager & operator=(SaveManager &&) = delete;
-
-private:
-	/**
-	 * \brief Create an instance of DB and return its reference
-	 *
-	 * \returns DB instance
-	 *
-	 * This function exists because DB is a facade class, which can't directly be used in the API
-	 * without workarounds
-	 *
-	 * TODO: better solution
-	 */
-	static DB & get_db();
-};
-
-} // namespace crepe
diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h
index f6fdb2a..66dad17 100644
--- a/src/crepe/api/Scene.h
+++ b/src/crepe/api/Scene.h
@@ -3,6 +3,7 @@
 #include <string>
 
 #include "../util/OptionalRef.h"
+#include "../manager/Mediator.h"
 
 namespace crepe {
 
@@ -34,6 +35,8 @@ public:
 	 */
 	virtual std::string get_name() const = 0;
 
+	// TODO: Late references should ALWAYS be private! This is currently kept as-is so unit tests
+	// keep passing, but this reference should not be directly accessible by the user!!!
 protected:
 	/**
 	 * \name Late references
@@ -46,8 +49,8 @@ protected:
 	 *
 	 * \{
 	 */
-	//! Reference to the ComponentManager
-	OptionalRef<ComponentManager> component_manager;
+	//! Mediator reference
+	OptionalRef<Mediator> mediator;
 	//! \}
 };
 
diff --git a/src/crepe/api/SceneManager.cpp b/src/crepe/api/SceneManager.cpp
deleted file mode 100644
index 1f783ad..0000000
--- a/src/crepe/api/SceneManager.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#include <algorithm>
-#include <memory>
-
-#include "../ComponentManager.h"
-
-#include "SceneManager.h"
-
-using namespace crepe;
-using namespace std;
-
-SceneManager::SceneManager(ComponentManager & mgr) : component_manager(mgr) {}
-
-void SceneManager::set_next_scene(const string & name) { next_scene = name; }
-
-void SceneManager::load_next_scene() {
-	// next scene not set
-	if (this->next_scene.empty()) return;
-
-	auto it = find_if(this->scenes.begin(), this->scenes.end(),
-					  [&next_scene = this->next_scene](unique_ptr<Scene> & scene) {
-						  return scene.get()->get_name() == next_scene;
-					  });
-
-	// next scene not found
-	if (it == this->scenes.end()) return;
-	unique_ptr<Scene> & scene = *it;
-
-	// Delete all components of the current scene
-	ComponentManager & mgr = this->component_manager;
-	mgr.delete_all_components();
-
-	// Load the new scene
-	scene->load_scene();
-}
diff --git a/src/crepe/api/SceneManager.h b/src/crepe/api/SceneManager.h
deleted file mode 100644
index f6f62cd..0000000
--- a/src/crepe/api/SceneManager.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#pragma once
-
-#include <memory>
-#include <vector>
-
-#include "Scene.h"
-
-namespace crepe {
-
-class ComponentManager;
-
-/**
- * \brief Manages scenes
- *
- * This class manages scenes. It can add new scenes and load them. It also manages the current scene
- * and the next scene.
- */
-class SceneManager {
-public:
-	//! \param mgr  Reference to the ComponentManager
-	SceneManager(ComponentManager & mgr);
-
-public:
-	/**
-	 * \brief Add a new concrete scene to the scene manager
-	 *
-	 * \tparam T  Type of concrete scene
-	 */
-	template <typename T, typename... Args>
-	void add_scene(Args &&... args);
-	/**
-	 * \brief Set the next scene
-	 *
-	 * This scene will be loaded at the end of the frame
-	 *
-	 * \param name  Name of the next scene
-	 */
-	void set_next_scene(const std::string & name);
-	//! Load a new scene (if there is one)
-	void load_next_scene();
-
-private:
-	//! Vector of concrete scenes (added by add_scene())
-	std::vector<std::unique_ptr<Scene>> scenes;
-	//! Next scene to load
-	std::string next_scene;
-	//! Reference to the ComponentManager
-	ComponentManager & component_manager;
-};
-
-} // namespace crepe
-
-#include "SceneManager.hpp"
diff --git a/src/crepe/api/SceneManager.hpp b/src/crepe/api/SceneManager.hpp
deleted file mode 100644
index 5c8e417..0000000
--- a/src/crepe/api/SceneManager.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include "SceneManager.h"
-
-namespace crepe {
-
-template <typename T, typename... Args>
-void SceneManager::add_scene(Args &&... args) {
-	using namespace std;
-	static_assert(is_base_of<Scene, T>::value, "T must be derived from Scene");
-
-	Scene * scene = new T(std::forward<Args>(args)...);
-	unique_ptr<Scene> unique_scene(scene);
-
-	unique_scene->component_manager = this->component_manager;
-
-	this->scenes.emplace_back(std::move(unique_scene));
-
-	// The first scene added, is the one that will be loaded at the beginning
-	if (next_scene.empty()) {
-		next_scene = scene->get_name();
-	}
-}
-
-} // namespace crepe
diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp
index fcbe4c7..a27838e 100644
--- a/src/crepe/api/Script.cpp
+++ b/src/crepe/api/Script.cpp
@@ -1,11 +1,17 @@
+#include <string>
+
+#include "../manager/SceneManager.h"
+
 #include "Script.h"
 
 using namespace crepe;
+using namespace std;
 
 Script::~Script() {
-	EventManager & evmgr = this->event_manager;
+	Mediator & mediator = this->mediator;
+	EventManager & mgr = mediator.event_manager;
 	for (auto id : this->listeners) {
-		evmgr.unsubscribe(id);
+		mgr.unsubscribe(id);
 	}
 }
 
@@ -13,3 +19,10 @@ template <>
 void Script::subscribe(const EventHandler<CollisionEvent> & callback) {
 	this->subscribe_internal(callback, this->game_object_id);
 }
+
+void Script::set_next_scene(const string & name) {
+	Mediator & mediator = this->mediator;
+	SceneManager & mgr = mediator.scene_manager;
+	mgr.set_next_scene(name);
+}
+
diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h
index a0870cb..e1f86b2 100644
--- a/src/crepe/api/Script.h
+++ b/src/crepe/api/Script.h
@@ -4,8 +4,8 @@
 
 #include "../types.h"
 #include "../util/OptionalRef.h"
-
-#include "EventManager.h"
+#include "../manager/Mediator.h"
+#include "../manager/EventManager.h"
 
 namespace crepe {
 
@@ -106,6 +106,12 @@ protected:
 	template <typename EventType>
 	void subscribe(const EventHandler<EventType> & callback);
 
+	/**
+	 * \brief Set the next scene using SceneManager
+	 * \see SceneManager::set_next_scene
+	 */
+	void set_next_scene(const std::string & name);
+
 	//! \}
 
 private:
@@ -160,10 +166,8 @@ private:
 	game_object_id_t game_object_id;
 	//! Reference to parent component
 	OptionalRef<bool> active;
-	//! Reference to component manager instance
-	OptionalRef<ComponentManager> component_manager;
-	//! Reference to event manager instance
-	OptionalRef<EventManager> event_manager;
+	//! Mediator reference
+	OptionalRef<Mediator> mediator;
 	//! \}
 
 private:
diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp
index a2463bf..45f1ff1 100644
--- a/src/crepe/api/Script.hpp
+++ b/src/crepe/api/Script.hpp
@@ -1,6 +1,6 @@
 #pragma once
 
-#include "../ComponentManager.h"
+#include "../manager/ComponentManager.h"
 
 #include "BehaviorScript.h"
 #include "Script.h"
@@ -20,7 +20,8 @@ T & Script::get_component() const {
 
 template <typename T>
 RefVector<T> Script::get_components() const {
-	ComponentManager & mgr = this->component_manager;
+	Mediator & mediator = this->mediator;
+	ComponentManager & mgr = mediator.component_manager;
 
 	return mgr.get_components_by_id<T>(this->game_object_id);
 }
@@ -33,7 +34,8 @@ void Script::logf(Args &&... args) {
 template <typename EventType>
 void Script::subscribe_internal(const EventHandler<EventType> & callback,
 								event_channel_t channel) {
-	EventManager & mgr = this->event_manager;
+	Mediator & mediator = this->mediator;
+	EventManager & mgr = mediator.event_manager;
 	subscription_t listener = mgr.subscribe<EventType>(
 		[this, callback](const EventType & data) -> bool {
 			bool & active = this->active;
diff --git a/src/crepe/manager/CMakeLists.txt b/src/crepe/manager/CMakeLists.txt
new file mode 100644
index 0000000..517b8a2
--- /dev/null
+++ b/src/crepe/manager/CMakeLists.txt
@@ -0,0 +1,20 @@
+target_sources(crepe PUBLIC
+	ComponentManager.cpp
+	EventManager.cpp
+	Manager.cpp
+	SaveManager.cpp
+	SceneManager.cpp
+)
+
+target_sources(crepe PUBLIC FILE_SET HEADERS FILES
+	ComponentManager.h
+	ComponentManager.hpp
+	EventManager.h
+	EventManager.hpp
+	Manager.h
+	Mediator.h
+	SaveManager.h
+	SceneManager.h
+	SceneManager.hpp
+)
+
diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp
new file mode 100644
index 0000000..5a96158
--- /dev/null
+++ b/src/crepe/manager/ComponentManager.cpp
@@ -0,0 +1,63 @@
+#include "../api/GameObject.h"
+#include "../util/Log.h"
+#include "../types.h"
+
+#include "ComponentManager.h"
+
+using namespace crepe;
+using namespace std;
+
+ComponentManager::ComponentManager(Mediator & mediator) : Manager(mediator) {
+	mediator.component_manager = *this;
+	dbg_trace();
+}
+ComponentManager::~ComponentManager() { dbg_trace(); }
+
+void ComponentManager::delete_all_components_of_id(game_object_id_t id) {
+	// Do not delete persistent objects
+	if (this->persistent[id]) {
+		return;
+	}
+
+	// Loop through all the types (in the unordered_map<>)
+	for (auto & [type, component_array] : this->components) {
+		// Make sure that the id (that we are looking for) is within the boundaries of the vector<>
+		if (id < component_array.size()) {
+			// Clear the components at this specific id
+			component_array[id].clear();
+		}
+	}
+}
+
+void ComponentManager::delete_all_components() {
+	// Loop through all the types (in the unordered_map<>)
+	for (auto & [type, component_array] : this->components) {
+		// Loop through all the ids (in the vector<>)
+		for (game_object_id_t id = 0; id < component_array.size(); id++) {
+			// Do not delete persistent objects
+			if (!this->persistent[id]) {
+				// Clear the components at this specific id
+				component_array[id].clear();
+			}
+		}
+	}
+
+	this->next_id = 0;
+}
+
+GameObject ComponentManager::new_object(const string & name, const string & tag,
+										const vec2 & position, double rotation, double scale) {
+	// Find the first available id (taking persistent objects into account)
+	while (this->persistent[this->next_id]) {
+		this->next_id++;
+	}
+
+	GameObject object{*this, this->next_id, name, tag, position, rotation, scale};
+	this->next_id++;
+
+	return object;
+}
+
+void ComponentManager::set_persistent(game_object_id_t id, bool persistent) {
+	this->persistent[id] = persistent;
+}
diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h
new file mode 100644
index 0000000..ad37586
--- /dev/null
+++ b/src/crepe/manager/ComponentManager.h
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <memory>
+#include <typeindex>
+#include <unordered_map>
+#include <vector>
+
+#include "../Component.h"
+#include "../types.h"
+
+#include "Manager.h"
+
+namespace crepe {
+
+class GameObject;
+
+/**
+ * \brief Manages all components
+ * 
+ * This class manages all components. It provides methods to add, delete and get components.
+ */
+class ComponentManager : public Manager {
+	// TODO: This relation should be removed! I (loek) believe that the scene manager should
+	// create/destroy components because the GameObject's are stored in concrete Scene classes,
+	// which will in turn call GameObject's destructor, which will in turn call
+	// ComponentManager::delete_components_by_id or something. This is a pretty major change, so
+	// here is a comment and temporary fix instead :tada:
+	friend class SceneManager;
+
+public:
+	ComponentManager(Mediator & mediator);
+	~ComponentManager(); // dbg_trace
+
+	/**
+	 * \brief Create a new game object using the component manager
+	 *
+	 * \param name Metadata::name (required)
+	 * \param tag Metadata::tag (optional, empty by default)
+	 * \param position Transform::position (optional, origin by default)
+	 * \param rotation Transform::rotation (optional, 0 by default)
+	 * \param scale Transform::scale (optional, 1 by default)
+	 *
+	 * \returns GameObject interface
+	 *
+	 * \note This method automatically assigns a new entity ID
+	 */
+	GameObject new_object(const std::string & name, const std::string & tag = "",
+						  const vec2 & position = {0, 0}, double rotation = 0,
+						  double scale = 1);
+
+protected:
+	/**
+	 * GameObject is used as an interface to add/remove components, and the game programmer is
+	 * supposed to use it instead of interfacing with the component manager directly.
+	 */
+	friend class GameObject;
+	/**
+	 * \brief Add a component to the ComponentManager
+	 * 
+	 * This method adds a component to the ComponentManager. The component is created with the
+	 * given arguments and added to the ComponentManager.
+	 * 
+	 * \tparam T The type of the component
+	 * \tparam Args The types of the arguments
+	 * \param id The id of the GameObject this component belongs to
+	 * \param args The arguments to create the component
+	 * \return The created component
+	 */
+	template <typename T, typename... Args>
+	T & add_component(game_object_id_t id, Args &&... args);
+	/**
+	 * \brief Delete all components of a specific type and id
+	 * 
+	 * This method deletes all components of a specific type and id.
+	 * 
+	 * \tparam T The type of the component
+	 * \param id The id of the GameObject this component belongs to
+	 */
+	template <typename T>
+	void delete_components_by_id(game_object_id_t id);
+	/**
+	 * \brief Delete all components of a specific type
+	 * 
+	 * This method deletes all components of a specific type.
+	 * 
+	 * \tparam T The type of the component
+	 */
+	template <typename T>
+	void delete_components();
+	/**
+	 * \brief Delete all components of a specific id
+	 * 
+	 * This method deletes all components of a specific id.
+	 * 
+	 * \param id The id of the GameObject this component belongs to
+	 */
+	void delete_all_components_of_id(game_object_id_t id);
+	/**
+	 * \brief Delete all components
+	 * 
+	 * This method deletes all components.
+	 */
+	void delete_all_components();
+	/**
+	 * \brief Set a GameObject as persistent
+	 *
+	 * This method sets a GameObject as persistent. If a GameObject is persistent, its
+	 * components will not be deleted.
+	 *
+	 * \param id The id of the GameObject to set as persistent
+	 * \param persistent The persistent flag
+	 */
+	void set_persistent(game_object_id_t id, bool persistent);
+
+public:
+	/**
+	 * \brief Get all components of a specific type and id
+	 * 
+	 * This method gets all components of a specific type and id.
+	 * 
+	 * \tparam T The type of the component
+	 * \param id The id of the GameObject this component belongs to
+	 * \return A vector of all components of the specific type and id
+	 */
+	template <typename T>
+	RefVector<T> get_components_by_id(game_object_id_t id) const;
+	/**
+	 * \brief Get all components of a specific type
+	 * 
+	 * This method gets all components of a specific type.
+	 * 
+	 * \tparam T The type of the component
+	 * \return A vector of all components of the specific type
+	 */
+	template <typename T>
+	RefVector<T> get_components_by_type() const;
+
+private:
+	/**
+	 * \brief The components
+	 * 
+	 * This unordered_map stores all components. The key is the type of the component and the
+	 * value is a vector of vectors of unique pointers to the components.
+	 *
+	 * Every component type has its own vector of vectors of unique pointers to the components.
+	 * The first vector is for the ids of the GameObjects and the second vector is for the
+	 * components (because a GameObject might have multiple components).
+	 */
+	std::unordered_map<std::type_index, std::vector<std::vector<std::unique_ptr<Component>>>>
+		components;
+
+	//! Persistent flag for each GameObject
+	std::unordered_map<game_object_id_t, bool> persistent;
+
+	//! ID of next GameObject allocated by \c ComponentManager::new_object
+	game_object_id_t next_id = 0;
+};
+
+} // namespace crepe
+
+#include "ComponentManager.hpp"
diff --git a/src/crepe/manager/ComponentManager.hpp b/src/crepe/manager/ComponentManager.hpp
new file mode 100644
index 0000000..ffb38ec
--- /dev/null
+++ b/src/crepe/manager/ComponentManager.hpp
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <type_traits>
+
+#include "ComponentManager.h"
+#include "types.h"
+
+namespace crepe {
+
+template <class T, typename... Args>
+T & ComponentManager::add_component(game_object_id_t id, Args &&... args) {
+	using namespace std;
+
+	static_assert(is_base_of<Component, T>::value,
+				  "add_component must recieve a derivative class of Component");
+
+	// Determine the type of T (this is used as the key of the unordered_map<>)
+	type_index type = typeid(T);
+
+	// Check if this component type is already in the unordered_map<>
+	if (this->components.find(type) == this->components.end()) {
+		//If not, create a new (empty) vector<> of vector<unique_ptr<Component>>
+		this->components[type] = vector<vector<unique_ptr<Component>>>();
+	}
+
+	// Resize the vector<> if the id is greater than the current size
+	if (id >= this->components[type].size()) {
+		// Initialize new slots to nullptr (resize does automatically init to nullptr)
+		this->components[type].resize(id + 1);
+	}
+
+	// Create a new component of type T (arguments directly forwarded). The
+	// constructor must be called by ComponentManager.
+	T * instance_ptr = new T(id, forward<Args>(args)...);
+	if (instance_ptr == nullptr) throw std::bad_alloc();
+
+	T & instance_ref = *instance_ptr;
+	unique_ptr<T> instance = unique_ptr<T>(instance_ptr);
+
+	// Check if the vector size is not greater than get_instances_max
+	int max_instances = instance->get_instances_max();
+	if (max_instances != -1 && components[type][id].size() >= max_instances) {
+		throw std::runtime_error(
+			"Exceeded maximum number of instances for this component type");
+	}
+
+	// store its unique_ptr in the vector<>
+	this->components[type][id].push_back(std::move(instance));
+
+	return instance_ref;
+}
+
+template <typename T>
+void ComponentManager::delete_components_by_id(game_object_id_t id) {
+	using namespace std;
+
+	// Do not delete persistent objects
+	if (this->persistent[id]) {
+		return;
+	}
+
+	// Determine the type of T (this is used as the key of the unordered_map<>)
+	type_index type = typeid(T);
+
+	// Find the type (in the unordered_map<>)
+	if (this->components.find(type) != this->components.end()) {
+		// Get the correct vector<>
+		vector<vector<unique_ptr<Component>>> & component_array = this->components[type];
+
+		// Make sure that the id (that we are looking for) is within the boundaries of the vector<>
+		if (id < component_array.size()) {
+			// Clear the whole vector<> of this specific type and id
+			component_array[id].clear();
+		}
+	}
+}
+
+template <typename T>
+void ComponentManager::delete_components() {
+	// Determine the type of T (this is used as the key of the unordered_map<>)
+	std::type_index type = typeid(T);
+
+	if (this->components.find(type) == this->components.end()) return;
+
+	// Loop through the whole vector<> of this specific type
+	for (game_object_id_t i = 0; i < this->components[type].size(); ++i) {
+		// Do not delete persistent objects
+		if (!this->persistent[i]) {
+			this->components[type][i].clear();
+		}
+	}
+}
+
+template <typename T>
+RefVector<T> ComponentManager::get_components_by_id(game_object_id_t id) const {
+	using namespace std;
+
+	// Determine the type of T (this is used as the key of the unordered_map<>)
+	type_index type = typeid(T);
+
+	// Create an empty vector<>
+	RefVector<T> component_vector;
+
+	if (this->components.find(type) == this->components.end()) return component_vector;
+
+	// Get the correct vector<>
+	const vector<vector<unique_ptr<Component>>> & component_array = this->components.at(type);
+
+	// Make sure that the id (that we are looking for) is within the boundaries of the vector<>
+	if (id >= component_array.size()) return component_vector;
+
+	// Loop trough the whole vector<>
+	for (const unique_ptr<Component> & component_ptr : component_array[id]) {
+		// Cast the unique_ptr to a raw pointer
+		T * casted_component = static_cast<T *>(component_ptr.get());
+
+		if (casted_component == nullptr) continue;
+
+		// Add the dereferenced raw pointer to the vector<>
+		component_vector.push_back(*casted_component);
+	}
+
+	return component_vector;
+}
+
+template <typename T>
+RefVector<T> ComponentManager::get_components_by_type() const {
+	using namespace std;
+
+	// Determine the type of T (this is used as the key of the unordered_map<>)
+	type_index type = typeid(T);
+
+	// Create an empty vector<>
+	RefVector<T> component_vector;
+
+	// Find the type (in the unordered_map<>)
+	if (this->components.find(type) == this->components.end()) return component_vector;
+
+	// Get the correct vector<>
+	const vector<vector<unique_ptr<Component>>> & component_array = this->components.at(type);
+
+	// Loop through the whole vector<>
+	for (const vector<unique_ptr<Component>> & component : component_array) {
+		// Loop trough the whole vector<>
+		for (const unique_ptr<Component> & component_ptr : component) {
+			// Cast the unique_ptr to a raw pointer
+			T * casted_component = static_cast<T *>(component_ptr.get());
+
+			// Ensure that the cast was successful
+			if (casted_component == nullptr) continue;
+
+			// Add the dereferenced raw pointer to the vector<>
+			component_vector.emplace_back(ref(*casted_component));
+		}
+	}
+
+	// Return the vector<>
+	return component_vector;
+}
+
+} // namespace crepe
diff --git a/src/crepe/manager/EventManager.cpp b/src/crepe/manager/EventManager.cpp
new file mode 100644
index 0000000..20f0dd3
--- /dev/null
+++ b/src/crepe/manager/EventManager.cpp
@@ -0,0 +1,46 @@
+#include "EventManager.h"
+
+using namespace crepe;
+using namespace std;
+
+EventManager & EventManager::get_instance() {
+	static EventManager instance;
+	return instance;
+}
+
+void EventManager::dispatch_events() {
+	for (auto & event : this->events_queue) {
+		this->handle_event(event.type, event.channel, *event.event.get());
+	}
+	this->events_queue.clear();
+}
+
+void EventManager::handle_event(type_index type, event_channel_t channel, const Event & data) {
+	auto handlers_it = this->subscribers.find(type);
+	if (handlers_it == this->subscribers.end()) return;
+
+	vector<CallbackEntry> & handlers = handlers_it->second;
+	for (auto & handler : handlers) {
+		bool check_channel = handler.channel != CHANNEL_ALL || channel != CHANNEL_ALL;
+		if (check_channel && handler.channel != channel) continue;
+
+		bool handled = handler.callback->exec(data);
+		if (handled) return;
+	}
+}
+
+void EventManager::clear() {
+	this->subscribers.clear();
+	this->events_queue.clear();
+}
+
+void EventManager::unsubscribe(subscription_t id) {
+	for (auto & [event_type, handlers] : this->subscribers) {
+		for (auto it = handlers.begin(); it != handlers.end(); it++) {
+			// find listener with subscription id
+			if ((*it).id != id) continue;
+			it = handlers.erase(it);
+			return;
+		}
+	}
+}
diff --git a/src/crepe/manager/EventManager.h b/src/crepe/manager/EventManager.h
new file mode 100644
index 0000000..d634f54
--- /dev/null
+++ b/src/crepe/manager/EventManager.h
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <memory>
+#include <typeindex>
+#include <unordered_map>
+#include <vector>
+
+#include "../api/Event.h"
+#include "../api/EventHandler.h"
+
+namespace crepe {
+
+//! Event listener unique ID
+typedef size_t subscription_t;
+
+/**
+ * \brief Event channel
+ *
+ * Events can be sent to a specific channel, which prevents listeners on other channels from
+ * being called. The default channel is EventManager::CHANNEL_ALL, which calls all listeners.
+ */
+typedef size_t event_channel_t;
+
+/**
+ * \class EventManager
+ * \brief Manages event subscriptions, triggers, and queues, enabling decoupled event handling.
+ * 
+ * The `EventManager` acts as a centralized event system. It allows for registering callbacks
+ * for specific event types, triggering events synchronously, queueing events for later
+ * processing, and managing subscriptions via unique identifiers.
+ */
+class EventManager {
+public:
+	static constexpr const event_channel_t CHANNEL_ALL = -1;
+
+	/**
+	 * \brief Get the singleton instance of the EventManager.
+	 * 
+	 * This method returns the unique instance of the EventManager, creating it if it
+	 * doesn't already exist. Ensures only one instance is active in the program.
+	 * 
+	 * \return Reference to the singleton instance of the EventManager.
+	 */
+	static EventManager & get_instance();
+
+	/**
+	 * \brief Subscribe to a specific event type.
+	 * 
+	 * Registers a callback for a given event type and optional channel. Each callback
+	 * is assigned a unique subscription ID that can be used for later unsubscription.
+	 * 
+	 * \tparam EventType The type of the event to subscribe to.
+	 * \param callback The callback function to be invoked when the event is triggered.
+	 * \param channel The channel number to subscribe to (default is CHANNEL_ALL, which listens to all channels).
+	 * \return A unique subscription ID associated with the registered callback.
+	 */
+	template <typename EventType>
+	subscription_t subscribe(const EventHandler<EventType> & callback,
+							 event_channel_t channel = CHANNEL_ALL);
+
+	/**
+	 * \brief Unsubscribe a previously registered callback.
+	 * 
+	 * Removes a callback from the subscription list based on its unique subscription ID.
+	 * 
+	 * \param event_id The unique subscription ID of the callback to remove.
+	 */
+	void unsubscribe(subscription_t event_id);
+
+	/**
+	 * \brief Trigger an event immediately.
+	 * 
+	 * Synchronously invokes all registered callbacks for the given event type on the specified channel.
+	 * 
+	 * \tparam EventType The type of the event to trigger.
+	 * \param event The event instance to pass to the callbacks.
+	 * \param channel The channel to trigger the event on (default is CHANNEL_ALL, which triggers on all channels).
+	 */
+	template <typename EventType>
+	void trigger_event(const EventType & event = {}, event_channel_t channel = CHANNEL_ALL);
+
+	/**
+	 * \brief Queue an event for later processing.
+	 * 
+	 * Adds an event to the event queue to be processed during the next call to `dispatch_events`.
+	 * 
+	 * \tparam EventType The type of the event to queue.
+	 * \param event The event instance to queue.
+	 * \param channel The channel to associate with the event (default is CHANNEL_ALL).
+	 */
+	template <typename EventType>
+	void queue_event(const EventType & event = {}, event_channel_t channel = CHANNEL_ALL);
+
+	/**
+	 * \brief Process all queued events.
+	 * 
+	 * Iterates through the event queue and triggers callbacks for each queued event.
+	 * Events are removed from the queue once processed.
+	 */
+	void dispatch_events();
+
+	/**
+	 * \brief Clear all subscriptions.
+	 * 
+	 * Removes all registered event handlers and clears the subscription list.
+	 */
+	void clear();
+
+private:
+	/**
+	 * \brief Default constructor for the EventManager.
+	 * 
+	 * Constructor is private to enforce the singleton pattern.
+	 */
+	EventManager() = default;
+
+	/**
+	 * \struct QueueEntry
+	 * \brief Represents an entry in the event queue.
+	 */
+	struct QueueEntry {
+		std::unique_ptr<Event> event; ///< The event instance.
+		event_channel_t channel = CHANNEL_ALL; ///< The channel associated with the event.
+		std::type_index type; ///< The type of the event.
+	};
+
+	/**
+	 * \brief Internal event handler
+	 *
+	 * This function processes a single event, and is used to process events both during
+	 * EventManager::dispatch_events and inside EventManager::trigger_event
+	 *
+	 * \param type \c typeid of concrete Event class
+	 * \param channel Event channel
+	 * \param data Event data
+	 */
+	void handle_event(std::type_index type, event_channel_t channel, const Event & data);
+
+	/**
+	 * \struct CallbackEntry
+	 * \brief Represents a registered event handler callback.
+	 */
+	struct CallbackEntry {
+		std::unique_ptr<IEventHandlerWrapper> callback; ///< The callback function wrapper.
+		event_channel_t channel = CHANNEL_ALL; ///< The channel this callback listens to.
+		subscription_t id = -1; ///< Unique subscription ID.
+	};
+
+	//! The queue of events to be processed during dispatch.
+	std::vector<QueueEntry> events_queue;
+
+	//! A map of event type to registered callbacks.
+	std::unordered_map<std::type_index, std::vector<CallbackEntry>> subscribers;
+
+	//! Counter to generate unique subscription IDs.
+	subscription_t subscription_counter = 0;
+};
+
+} // namespace crepe
+
+#include "EventManager.hpp"
diff --git a/src/crepe/manager/EventManager.hpp b/src/crepe/manager/EventManager.hpp
new file mode 100644
index 0000000..a5f4556
--- /dev/null
+++ b/src/crepe/manager/EventManager.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "EventManager.h"
+
+namespace crepe {
+
+template <typename EventType>
+subscription_t EventManager::subscribe(const EventHandler<EventType> & callback,
+									   event_channel_t channel) {
+	subscription_counter++;
+	std::type_index event_type = typeid(EventType);
+	std::unique_ptr<EventHandlerWrapper<EventType>> handler
+		= std::make_unique<EventHandlerWrapper<EventType>>(callback);
+	std::vector<CallbackEntry> & handlers = this->subscribers[event_type];
+	handlers.emplace_back(CallbackEntry{
+		.callback = std::move(handler), .channel = channel, .id = subscription_counter});
+	return subscription_counter;
+}
+
+template <typename EventType>
+void EventManager::queue_event(const EventType & event, event_channel_t channel) {
+	static_assert(std::is_base_of<Event, EventType>::value,
+				  "EventType must derive from Event");
+	this->events_queue.push_back(QueueEntry{
+		.event = std::make_unique<EventType>(event),
+		.channel = channel,
+		.type = typeid(EventType),
+	});
+}
+
+template <typename EventType>
+void EventManager::trigger_event(const EventType & event, event_channel_t channel) {
+	this->handle_event(typeid(EventType), channel, event);
+}
+
+} // namespace crepe
diff --git a/src/crepe/manager/Manager.cpp b/src/crepe/manager/Manager.cpp
new file mode 100644
index 0000000..fe7c936
--- /dev/null
+++ b/src/crepe/manager/Manager.cpp
@@ -0,0 +1,6 @@
+#include "Manager.h"
+
+using namespace crepe;
+
+Manager::Manager(Mediator & mediator) : mediator(mediator) { }
+
diff --git a/src/crepe/manager/Manager.h b/src/crepe/manager/Manager.h
new file mode 100644
index 0000000..9adfd0b
--- /dev/null
+++ b/src/crepe/manager/Manager.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "Mediator.h"
+
+namespace crepe {
+
+class Manager {
+public:
+	Manager(Mediator & mediator);
+	virtual ~Manager() = default;
+
+protected:
+	Mediator & mediator;
+};
+
+}
+
diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h
new file mode 100644
index 0000000..ce35d5c
--- /dev/null
+++ b/src/crepe/manager/Mediator.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "../util/OptionalRef.h"
+
+// TODO: remove these singletons:
+#include "SaveManager.h"
+#include "EventManager.h"
+
+namespace crepe {
+
+class ComponentManager;
+class SceneManager;
+
+/**
+ * Struct to pass references to classes that would otherwise need to be singletons down to
+ * other classes within the engine hierarchy. Made to prevent constant changes to subclasses to
+ * pass specific references through dependency injection. All references on this struct
+ * *should* be explicitly checked for availability as this struct does not guarantee anything.
+ *
+ * \todo Find better solution
+ */
+struct Mediator {
+	OptionalRef<ComponentManager> component_manager;
+	OptionalRef<SceneManager> scene_manager;
+	OptionalRef<SaveManager> save_manager = SaveManager::get_instance();
+	OptionalRef<EventManager> event_manager = EventManager::get_instance();
+};
+
+}
diff --git a/src/crepe/manager/SaveManager.cpp b/src/crepe/manager/SaveManager.cpp
new file mode 100644
index 0000000..121d017
--- /dev/null
+++ b/src/crepe/manager/SaveManager.cpp
@@ -0,0 +1,173 @@
+#include "../facade/DB.h"
+#include "../util/Log.h"
+#include "../api/Config.h"
+#include "../ValueBroker.h"
+
+#include "SaveManager.h"
+
+using namespace std;
+using namespace crepe;
+
+template <>
+string SaveManager::serialize(const string & value) const noexcept {
+	return value;
+}
+template <typename T>
+string SaveManager::serialize(const T & value) const noexcept {
+	return to_string(value);
+}
+template string SaveManager::serialize(const uint8_t &) const noexcept;
+template string SaveManager::serialize(const int8_t &) const noexcept;
+template string SaveManager::serialize(const uint16_t &) const noexcept;
+template string SaveManager::serialize(const int16_t &) const noexcept;
+template string SaveManager::serialize(const uint32_t &) const noexcept;
+template string SaveManager::serialize(const int32_t &) const noexcept;
+template string SaveManager::serialize(const uint64_t &) const noexcept;
+template string SaveManager::serialize(const int64_t &) const noexcept;
+template string SaveManager::serialize(const float &) const noexcept;
+template string SaveManager::serialize(const double &) const noexcept;
+
+template <>
+uint64_t SaveManager::deserialize(const string & value) const noexcept {
+	try {
+		return stoul(value);
+	} catch (std::invalid_argument &) {
+		return 0;
+	}
+}
+template <>
+int64_t SaveManager::deserialize(const string & value) const noexcept {
+	try {
+		return stol(value);
+	} catch (std::invalid_argument &) {
+		return 0;
+	}
+}
+template <>
+float SaveManager::deserialize(const string & value) const noexcept {
+	try {
+		return stof(value);
+	} catch (std::invalid_argument &) {
+		return 0;
+	}
+	return stof(value);
+}
+template <>
+double SaveManager::deserialize(const string & value) const noexcept {
+	try {
+		return stod(value);
+	} catch (std::invalid_argument &) {
+		return 0;
+	}
+}
+template <>
+string SaveManager::deserialize(const string & value) const noexcept {
+	return value;
+}
+
+template <>
+uint8_t SaveManager::deserialize(const string & value) const noexcept {
+	return deserialize<uint64_t>(value);
+}
+template <>
+int8_t SaveManager::deserialize(const string & value) const noexcept {
+	return deserialize<int64_t>(value);
+}
+template <>
+uint16_t SaveManager::deserialize(const string & value) const noexcept {
+	return deserialize<uint64_t>(value);
+}
+template <>
+int16_t SaveManager::deserialize(const string & value) const noexcept {
+	return deserialize<int64_t>(value);
+}
+template <>
+uint32_t SaveManager::deserialize(const string & value) const noexcept {
+	return deserialize<uint64_t>(value);
+}
+template <>
+int32_t SaveManager::deserialize(const string & value) const noexcept {
+	return deserialize<int64_t>(value);
+}
+
+SaveManager::SaveManager() { dbg_trace(); }
+
+SaveManager & SaveManager::get_instance() {
+	dbg_trace();
+	static SaveManager instance;
+	return instance;
+}
+
+DB & SaveManager::get_db() {
+	Config & cfg = Config::get_instance();
+	// TODO: make this path relative to XDG_DATA_HOME on Linux and whatever the
+	// default equivalent is on Windows using some third party library
+	static DB db(cfg.savemgr.location);
+	return db;
+}
+
+bool SaveManager::has(const string & key) {
+	DB & db = this->get_db();
+	return db.has(key);
+}
+
+template <>
+void SaveManager::set(const string & key, const string & value) {
+	DB & db = this->get_db();
+	db.set(key, value);
+}
+template <typename T>
+void SaveManager::set(const string & key, const T & value) {
+	DB & db = this->get_db();
+	db.set(key, std::to_string(value));
+}
+template void SaveManager::set(const string &, const uint8_t &);
+template void SaveManager::set(const string &, const int8_t &);
+template void SaveManager::set(const string &, const uint16_t &);
+template void SaveManager::set(const string &, const int16_t &);
+template void SaveManager::set(const string &, const uint32_t &);
+template void SaveManager::set(const string &, const int32_t &);
+template void SaveManager::set(const string &, const uint64_t &);
+template void SaveManager::set(const string &, const int64_t &);
+template void SaveManager::set(const string &, const float &);
+template void SaveManager::set(const string &, const double &);
+
+template <typename T>
+ValueBroker<T> SaveManager::get(const string & key, const T & default_value) {
+	if (!this->has(key)) this->set<T>(key, default_value);
+	return this->get<T>(key);
+}
+template ValueBroker<uint8_t> SaveManager::get(const string &, const uint8_t &);
+template ValueBroker<int8_t> SaveManager::get(const string &, const int8_t &);
+template ValueBroker<uint16_t> SaveManager::get(const string &, const uint16_t &);
+template ValueBroker<int16_t> SaveManager::get(const string &, const int16_t &);
+template ValueBroker<uint32_t> SaveManager::get(const string &, const uint32_t &);
+template ValueBroker<int32_t> SaveManager::get(const string &, const int32_t &);
+template ValueBroker<uint64_t> SaveManager::get(const string &, const uint64_t &);
+template ValueBroker<int64_t> SaveManager::get(const string &, const int64_t &);
+template ValueBroker<float> SaveManager::get(const string &, const float &);
+template ValueBroker<double> SaveManager::get(const string &, const double &);
+template ValueBroker<string> SaveManager::get(const string &, const string &);
+
+template <typename T>
+ValueBroker<T> SaveManager::get(const string & key) {
+	T value;
+	return {
+		[this, key](const T & target) { this->set<T>(key, target); },
+		[this, key, value]() mutable -> const T & {
+			value = this->deserialize<T>(this->get_db().get(key));
+			return value;
+		},
+	};
+}
+template ValueBroker<uint8_t> SaveManager::get(const string &);
+template ValueBroker<int8_t> SaveManager::get(const string &);
+template ValueBroker<uint16_t> SaveManager::get(const string &);
+template ValueBroker<int16_t> SaveManager::get(const string &);
+template ValueBroker<uint32_t> SaveManager::get(const string &);
+template ValueBroker<int32_t> SaveManager::get(const string &);
+template ValueBroker<uint64_t> SaveManager::get(const string &);
+template ValueBroker<int64_t> SaveManager::get(const string &);
+template ValueBroker<float> SaveManager::get(const string &);
+template ValueBroker<double> SaveManager::get(const string &);
+template ValueBroker<string> SaveManager::get(const string &);
diff --git a/src/crepe/manager/SaveManager.h b/src/crepe/manager/SaveManager.h
new file mode 100644
index 0000000..3d8c852
--- /dev/null
+++ b/src/crepe/manager/SaveManager.h
@@ -0,0 +1,114 @@
+#pragma once
+
+#include <memory>
+
+#include "../ValueBroker.h"
+
+namespace crepe {
+
+class DB;
+
+/**
+ * \brief Save data manager
+ *
+ * This class provides access to a simple key-value store that stores
+ * - integers (8-64 bit, signed or unsigned)
+ * - real numbers (float or double)
+ * - string (std::string)
+ *
+ * The underlying database is a key-value store.
+ */
+class SaveManager {
+public:
+	/**
+	 * \brief Get a read/write reference to a value and initialize it if it does not yet exist
+	 *
+	 * \param key  The value key
+	 * \param default_value  Value to initialize \c key with if it does not already exist in the
+	 * database
+	 *
+	 * \return Read/write reference to the value
+	 */
+	template <typename T>
+	ValueBroker<T> get(const std::string & key, const T & default_value);
+
+	/**
+	 * \brief Get a read/write reference to a value
+	 *
+	 * \param key  The value key
+	 *
+	 * \return Read/write reference to the value
+	 *
+	 * \note Attempting to read this value before it is initialized (i.e. set) will result in an
+	 * exception
+	 */
+	template <typename T>
+	ValueBroker<T> get(const std::string & key);
+
+	/**
+	 * \brief Set a value directly
+	 *
+	 * \param key  The value key
+	 * \param value  The value to store
+	 */
+	template <typename T>
+	void set(const std::string & key, const T & value);
+
+	/**
+	 * \brief Check if the save file has a value for this \c key
+	 *
+	 * \param key  The value key
+	 *
+	 * \returns True if the key exists, or false if it does not
+	 */
+	bool has(const std::string & key);
+
+private:
+	SaveManager();
+	virtual ~SaveManager() = default;
+
+private:
+	/**
+	 * \brief Serialize an arbitrary value to STL string
+	 *
+	 * \tparam T  Type of arbitrary value
+	 *
+	 * \returns String representation of value
+	 */
+	template <typename T>
+	std::string serialize(const T &) const noexcept;
+
+	/**
+	 * \brief Deserialize an STL string back to type \c T
+	 *
+	 * \tparam T  Type of value
+	 * \param value  Serialized value
+	 *
+	 * \returns Deserialized value
+	 */
+	template <typename T>
+	T deserialize(const std::string & value) const noexcept;
+
+public:
+	// singleton
+	static SaveManager & get_instance();
+	SaveManager(const SaveManager &) = delete;
+	SaveManager(SaveManager &&) = delete;
+	SaveManager & operator=(const SaveManager &) = delete;
+	SaveManager & operator=(SaveManager &&) = delete;
+
+private:
+	/**
+	 * \brief Create an instance of DB and return its reference
+	 *
+	 * \returns DB instance
+	 *
+	 * This function exists because DB is a facade class, which can't directly be used in the API
+	 * without workarounds
+	 *
+	 * TODO: better solution
+	 */
+	static DB & get_db();
+};
+
+} // namespace crepe
diff --git a/src/crepe/manager/SceneManager.cpp b/src/crepe/manager/SceneManager.cpp
new file mode 100644
index 0000000..50a9fbb
--- /dev/null
+++ b/src/crepe/manager/SceneManager.cpp
@@ -0,0 +1,35 @@
+#include <algorithm>
+#include <memory>
+
+#include "ComponentManager.h"
+#include "SceneManager.h"
+
+using namespace crepe;
+using namespace std;
+
+SceneManager::SceneManager(Mediator & mediator) : Manager(mediator) {
+	mediator.scene_manager = *this;
+}
+
+void SceneManager::set_next_scene(const string & name) { next_scene = name; }
+
+void SceneManager::load_next_scene() {
+	// next scene not set
+	if (this->next_scene.empty()) return;
+
+	auto it = find_if(this->scenes.begin(), this->scenes.end(),
+					  [&next_scene = this->next_scene](unique_ptr<Scene> & scene) {
+						  return scene.get()->get_name() == next_scene;
+					  });
+
+	// next scene not found
+	if (it == this->scenes.end()) return;
+	unique_ptr<Scene> & scene = *it;
+
+	// Delete all components of the current scene
+	ComponentManager & mgr = this->mediator.component_manager;
+	mgr.delete_all_components();
+
+	// Load the new scene
+	scene->load_scene();
+}
diff --git a/src/crepe/manager/SceneManager.h b/src/crepe/manager/SceneManager.h
new file mode 100644
index 0000000..e0955c2
--- /dev/null
+++ b/src/crepe/manager/SceneManager.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "../api/Scene.h"
+
+#include "Manager.h"
+
+namespace crepe {
+
+class ComponentManager;
+
+/**
+ * \brief Manages scenes
+ *
+ * This class manages scenes. It can add new scenes and load them. It also manages the current scene
+ * and the next scene.
+ */
+class SceneManager : public Manager {
+public:
+	SceneManager(Mediator & mediator);
+
+public:
+	/**
+	 * \brief Add a new concrete scene to the scene manager
+	 *
+	 * \tparam T  Type of concrete scene
+	 */
+	template <typename T, typename... Args>
+	void add_scene(Args &&... args);
+	/**
+	 * \brief Set the next scene
+	 *
+	 * This scene will be loaded at the end of the frame
+	 *
+	 * \param name  Name of the next scene
+	 */
+	void set_next_scene(const std::string & name);
+	//! Load a new scene (if there is one)
+	void load_next_scene();
+
+private:
+	//! Vector of concrete scenes (added by add_scene())
+	std::vector<std::unique_ptr<Scene>> scenes;
+	//! Next scene to load
+	std::string next_scene;
+};
+
+} // namespace crepe
+
+#include "SceneManager.hpp"
diff --git a/src/crepe/manager/SceneManager.hpp b/src/crepe/manager/SceneManager.hpp
new file mode 100644
index 0000000..dff4e51
--- /dev/null
+++ b/src/crepe/manager/SceneManager.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "SceneManager.h"
+
+namespace crepe {
+
+template <typename T, typename... Args>
+void SceneManager::add_scene(Args &&... args) {
+	using namespace std;
+	static_assert(is_base_of<Scene, T>::value, "T must be derived from Scene");
+
+	Scene * scene = new T(std::forward<Args>(args)...);
+	unique_ptr<Scene> unique_scene(scene);
+
+	unique_scene->mediator = this->mediator;
+
+	this->scenes.emplace_back(std::move(unique_scene));
+
+	// The first scene added, is the one that will be loaded at the beginning
+	if (next_scene.empty()) {
+		next_scene = scene->get_name();
+	}
+}
+
+} // namespace crepe
diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp
index 676e485..cdeac57 100644
--- a/src/crepe/system/AnimatorSystem.cpp
+++ b/src/crepe/system/AnimatorSystem.cpp
@@ -1,10 +1,10 @@
 #include <cstdint>
 
-#include "api/Animator.h"
-#include "facade/SDLContext.h"
+#include "../api/Animator.h"
+#include "../facade/SDLContext.h"
+#include "../manager/ComponentManager.h"
 
 #include "AnimatorSystem.h"
-#include "ComponentManager.h"
 
 using namespace crepe;
 
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index 0e62a57..ebae56b 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -2,10 +2,10 @@
 #include <cstdlib>
 #include <ctime>
 
-#include "api/ParticleEmitter.h"
-#include "api/Transform.h"
+#include "../api/ParticleEmitter.h"
+#include "../api/Transform.h"
+#include "../manager/ComponentManager.h"
 
-#include "ComponentManager.h"
 #include "ParticleSystem.h"
 
 using namespace crepe;
diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp
index 514a4b3..071ca21 100644
--- a/src/crepe/system/PhysicsSystem.cpp
+++ b/src/crepe/system/PhysicsSystem.cpp
@@ -1,6 +1,6 @@
 #include <cmath>
 
-#include "../ComponentManager.h"
+#include "../manager/ComponentManager.h"
 #include "../api/Config.h"
 #include "../api/Rigidbody.h"
 #include "../api/Transform.h"
diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp
index ad510f5..6c65e10 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -2,11 +2,10 @@
 #include <cassert>
 #include <cmath>
 #include <functional>
-#include <iostream>
 #include <stdexcept>
 #include <vector>
 
-#include "../ComponentManager.h"
+#include "../manager/ComponentManager.h"
 #include "../api/ParticleEmitter.h"
 #include "../api/Sprite.h"
 #include "../api/Transform.h"
diff --git a/src/crepe/system/ScriptSystem.cpp b/src/crepe/system/ScriptSystem.cpp
index 20a83f7..7ecbe78 100644
--- a/src/crepe/system/ScriptSystem.cpp
+++ b/src/crepe/system/ScriptSystem.cpp
@@ -1,4 +1,4 @@
-#include "../ComponentManager.h"
+#include "../manager/ComponentManager.h"
 #include "../api/BehaviorScript.h"
 #include "../api/Script.h"
 
diff --git a/src/crepe/system/System.cpp b/src/crepe/system/System.cpp
index 937a423..392d920 100644
--- a/src/crepe/system/System.cpp
+++ b/src/crepe/system/System.cpp
@@ -4,4 +4,4 @@
 
 using namespace crepe;
 
-System::System(ComponentManager & mgr) : component_manager(mgr) { dbg_trace(); }
+System::System(const Mediator & mediator) : component_manager(mediator.component_manager) { dbg_trace(); }
diff --git a/src/crepe/system/System.h b/src/crepe/system/System.h
index 28ea20e..92bfd50 100644
--- a/src/crepe/system/System.h
+++ b/src/crepe/system/System.h
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "../manager/Mediator.h"
+
 namespace crepe {
 
 class ComponentManager;
@@ -19,7 +21,7 @@ public:
 	virtual void update() = 0;
 
 public:
-	System(ComponentManager &);
+	System(const Mediator & m);
 	virtual ~System() = default;
 
 protected:
-- 
cgit v1.2.3