From 29aaa69791a13e7e1b3fdfaaef3b5a14d67412a0 Mon Sep 17 00:00:00 2001
From: Joseph Walton-Rivers <joseph@walton-rivers.uk>
Date: Mon, 27 Jun 2022 12:09:16 +0100
Subject: [PATCH] replace global services with subsystems

---
 demo/demo/main.cpp                           |  30 ++--
 demo/demo/rollball.cpp                       |   6 +-
 fggl/app.cpp                                 |   7 +-
 fggl/audio/openal/audio.cpp                  |  10 +-
 fggl/gfx/ogl/renderer.cpp                    |   8 +-
 fggl/gfx/ogl/shader.cpp                      |   2 +-
 fggl/gfx/ogl4/canvas.cpp                     |   6 +-
 fggl/gfx/window.cpp                          |   4 +-
 fggl/scenes/game.cpp                         |   3 +-
 fggl/scenes/menu.cpp                         |   3 +-
 include/fggl/app.hpp                         |  10 +-
 include/fggl/audio/audio.hpp                 |   4 +
 include/fggl/audio/null_audio.hpp            |  38 +++++
 include/fggl/audio/openal/audio.hpp          |   3 +
 include/fggl/gfx/compat.hpp                  |   2 +-
 include/fggl/gfx/ogl/renderer.hpp            |   4 +-
 include/fggl/gfx/ogl/shader.hpp              |   2 +-
 include/fggl/gfx/ogl4/canvas.hpp             |   4 +-
 include/fggl/gfx/window.hpp                  |   2 +-
 include/fggl/gfx/window_input.hpp            |   4 +-
 include/fggl/gfx/windowing.hpp               |   6 +-
 include/fggl/gui/fonts.hpp                   |   8 +-
 include/fggl/scenes/game.hpp                 |   2 +-
 include/fggl/scenes/menu.hpp                 |   2 +-
 include/fggl/subsystem/cursed/god_object.hpp | 140 +++++++++++++++++++
 include/fggl/subsystem/provider.hpp          |  40 ++++++
 include/fggl/subsystem/subsystem.hpp         |  32 +++++
 include/fggl/subsystem/types.hpp             |  36 +++++
 include/fggl/util/service.hpp                |   3 +
 29 files changed, 363 insertions(+), 58 deletions(-)
 create mode 100644 include/fggl/audio/null_audio.hpp
 create mode 100644 include/fggl/subsystem/cursed/god_object.hpp
 create mode 100644 include/fggl/subsystem/provider.hpp
 create mode 100644 include/fggl/subsystem/subsystem.hpp
 create mode 100644 include/fggl/subsystem/types.hpp

diff --git a/demo/demo/main.cpp b/demo/demo/main.cpp
index 38942cf..59a6cb0 100644
--- a/demo/demo/main.cpp
+++ b/demo/demo/main.cpp
@@ -25,6 +25,8 @@
 	#include "fggl/phys/bullet/bullet.hpp"
 #endif
 
+#include "fggl/subsystem/subsystem.hpp"
+
 #include "fggl/app.hpp"
 #include "fggl/audio/openal/audio.hpp"
 
@@ -74,32 +76,28 @@ static void test_atlas_api() {
 	auto *atlas = fggl::gfx::ImageAtlas<char>::pack(images);
 }
 
-static void setup_service_locators(fggl::util::ServiceLocator& locator) {
+int main(int argc, const char* argv[]) {
 	auto pathConfig = fggl::platform::calc_engine_paths("fggl-demo");
 
-	// FIXME: janky API(s)
-	auto inputs = locator.supply<fggl::input::Input>(std::make_shared<fggl::input::Input>());
-	auto storage = locator.supply<fggl::data::Storage>(std::make_shared<fggl::data::Storage>(pathConfig));
+	fggl::subsystem::SubsystemManager manager;
+	manager.provide<fggl::data::Storage>(new fggl::data::Storage(pathConfig));
+	manager.provide<fggl::audio::AudioService>(new fggl::audio::openAL());
 
-	locator.supply<fggl::gui::FontLibrary>(std::make_shared<fggl::gui::FontLibrary>());
-	locator.supply<fggl::ecs3::TypeRegistry>(std::make_shared<fggl::ecs3::TypeRegistry>());
-	locator.supply<fggl::audio::AudioService>(std::make_shared<fggl::audio::openAL>());
-}
-
-int main(int argc, const char* argv[]) {
+	manager.setTypeRegistry(new fggl::ecs3::TypeRegistry());
+	manager.setFontLibrary(new fggl::gui::FontLibrary());
+	manager.setInput(new fggl::input::Input());
 
-	auto& locator = fggl::util::ServiceLocator::instance();
-	setup_service_locators(locator);
+	manager.setup();
 
 	// create the application
-	fggl::App app( "fggl-demo" );
+	fggl::App app( &manager, "fggl-demo" );
 
 	// Would be nice to not take args like this, it messes with lifetimes
-	auto& windowing = app.use<fggl::gfx::ecsGlfwModule>(locator.get<fggl::input::Input>());
+	auto& windowing = app.use<fggl::gfx::ecsGlfwModule>( manager.getInput() );
 
     // -- should not be our problem - this is a broken api
     auto window = windowing.createWindow("Demo Game");
-	window->make_graphics<fggl::gfx::OpenGL4>();
+	window->make_graphics<fggl::gfx::OpenGL4>(manager.getStorage(), manager.getFontLibrary());
     window->fullscreen( true );
     app.setWindow( std::move(window) );
 
@@ -116,7 +114,7 @@ int main(int argc, const char* argv[]) {
     auto *menu = app.add_state<fggl::scenes::BasicMenu>("menu");
 
 	// add some menu items for the game states
-	auto audio = locator.get<fggl::audio::AudioService>();
+	auto* audio = manager.getAudio();
     menu->add("terrain", [&app, &audio]() {
 		audio->play("click.ogg", false);
 		app.change_state("game");
diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp
index d22a7ba..8271336 100644
--- a/demo/demo/rollball.cpp
+++ b/demo/demo/rollball.cpp
@@ -34,9 +34,7 @@ struct Prefabs {
 	fggl::ecs3::entity_t player;
 };
 
-static void setup_prefabs(fggl::ecs3::World& world, Prefabs& prefabs) {
-
-	auto storage = fggl::util::ServiceLocator::instance().get<fggl::data::Storage>();
+static void setup_prefabs(fggl::data::Storage* storage, fggl::ecs3::World& world, Prefabs& prefabs) {
 	fggl::ecs3::load_prototype_file(world, *storage, "rollball.yml");
 
 	{
@@ -148,7 +146,7 @@ namespace demo {
 		Game::activate();
 
 		Prefabs prefabs{};
-		setup_prefabs(world(), prefabs);
+		setup_prefabs(m_owner.services()->getStorage(), world(), prefabs);
 
 		// collectable callbacks
 		auto* collectableCallbacks = world().get<fggl::phys::CollisionCallbacks>(prefabs.collectable);
diff --git a/fggl/app.cpp b/fggl/app.cpp
index 7d1c223..db9fbd3 100644
--- a/fggl/app.cpp
+++ b/fggl/app.cpp
@@ -24,15 +24,16 @@
 
 namespace fggl {
 
-    App::App(const Identifer& name) : App::App( name, name ) {
+    App::App(subsystem::ServiceProvider* services, const Identifer& name) : App::App( services, name, name ) {
     }
 
-    App::App(const Identifer& name, const Identifer& folder ) :
+    App::App(subsystem::ServiceProvider* services, const Identifer& name, const Identifer& folder ) :
         m_running(true),
         m_types(std::make_unique<ecs3::TypeRegistry>()),
         m_modules(std::make_unique<ecs3::ModuleManager>(*m_types)),
 		m_window(nullptr),
-        m_states() {}
+        m_states(),
+		m_subsystems(services){}
 
     int App::run(int argc, const char** argv) {
 
diff --git a/fggl/audio/openal/audio.cpp b/fggl/audio/openal/audio.cpp
index e7777a9..d79ae61 100644
--- a/fggl/audio/openal/audio.cpp
+++ b/fggl/audio/openal/audio.cpp
@@ -30,6 +30,7 @@
 
 #include "fggl/util/service.hpp"
 #include "fggl/data/storage.hpp"
+#include "fggl/subsystem/provider.hpp"
 
 namespace fggl::audio::openal {
 
@@ -37,11 +38,10 @@ namespace fggl::audio::openal {
 
 		// load audio clip into temp storage
 		AudioClip clip;
-		auto& locator = util::ServiceLocator::instance();
-		auto storage = locator.get<data::Storage>();
-		bool result = storage->load(data::StorageType::Data, filename, &clip);
+		bool result = m_storage->load(data::StorageType::Data, filename, &clip);
 		if ( !result ) {
 			std::cerr << "error: can't load audio data" << std::endl;
+			return;
 		}
 
 		play(clip, looping);
@@ -53,4 +53,8 @@ namespace fggl::audio::openal {
 			m_defaultSource->play(clip, looping);
 		}
 	}
+
+	void AudioServiceOAL::setup(data::Storage* storage) {
+		m_storage = storage;
+	}
 }
\ No newline at end of file
diff --git a/fggl/gfx/ogl/renderer.cpp b/fggl/gfx/ogl/renderer.cpp
index 2ded83f..564a754 100644
--- a/fggl/gfx/ogl/renderer.cpp
+++ b/fggl/gfx/ogl/renderer.cpp
@@ -135,7 +135,7 @@ namespace fggl::gfx {
 	using data::Mesh2D;
 	using data::Vertex2D;
 
-	OpenGL4Backend::OpenGL4Backend(const Window &owner) : fggl::gfx::Graphics(), m_canvasPipeline(INVALID_SHADER_ID) {
+	OpenGL4Backend::OpenGL4Backend(const Window &owner, data::Storage* storage, gui::FontLibrary* fonts) : fggl::gfx::Graphics(), m_canvasPipeline(INVALID_SHADER_ID), m_storage(storage) {
 		// initialise GLEW, or fail
 		// FIXME this binds the graphics stack to GLFW :'(
 		int version = gladLoadGLLoader( (GLADloadproc)glfwGetProcAddress );
@@ -159,9 +159,7 @@ namespace fggl::gfx {
 		glViewport(0, 0, fbSize.x, fbSize.y);
 
 		// setup the shader cache
-		auto &locator = util::ServiceLocator::instance();
-		auto storage = locator.get<data::Storage>();
-		m_cache = std::make_unique<ShaderCache>(storage);
+		m_cache = std::make_unique<ShaderCache>(m_storage);
 
 		// setup 2D rendering system
 		ShaderConfig shader2DConfig = ShaderFromName("canvas");
@@ -175,7 +173,7 @@ namespace fggl::gfx {
 		m_cache->load(shader3DConfig);
 
 		// rendering helpers
-		m_canvasRenderer = std::make_unique<ogl4::CanvasRenderer>();
+		m_canvasRenderer = std::make_unique<ogl4::CanvasRenderer>(fonts);
 		m_modelRenderer = std::make_unique<ogl4::StaticModelRenderer>(m_cache.get());
 
 		m_debugRenderer = std::make_unique<ogl4::DebugRenderer>(m_cache->getOrLoad(ShaderFromName("debug")));
diff --git a/fggl/gfx/ogl/shader.cpp b/fggl/gfx/ogl/shader.cpp
index dcba3cd..a38aec9 100644
--- a/fggl/gfx/ogl/shader.cpp
+++ b/fggl/gfx/ogl/shader.cpp
@@ -57,7 +57,7 @@ bool ShaderCache::readAndCompileShader(const std::string &filename, GLuint shade
 	return compileShaderFromSource(source, shader);
 }
 
-ShaderCache::ShaderCache(std::shared_ptr<fggl::data::Storage> storage) : m_storage(storage), m_shaders(), m_binary(true) {
+ShaderCache::ShaderCache(fggl::data::Storage* storage) : m_storage(storage), m_shaders(), m_binary(true) {
 
 	if ( !GLAD_GL_ARB_get_program_binary ) {
 		spdlog::warn("the graphics card doesn support shader caching, disabling");
diff --git a/fggl/gfx/ogl4/canvas.cpp b/fggl/gfx/ogl4/canvas.cpp
index 189a0d8..59f1bab 100644
--- a/fggl/gfx/ogl4/canvas.cpp
+++ b/fggl/gfx/ogl4/canvas.cpp
@@ -85,8 +85,9 @@ namespace fggl::gfx::ogl4 {
 		}
 	}
 
-	CanvasRenderer::CanvasRenderer() :
+	CanvasRenderer::CanvasRenderer(fggl::gui::FontLibrary* fonts) :
 		m_bounds({0.0F, 1920.F, 1080.0F, 0.0F}),
+		m_fonts(fonts),
 		m_fontTex(ogl::TextureType::Tex2D) {
 		m_vao.bind();
 
@@ -151,8 +152,7 @@ namespace fggl::gfx::ogl4 {
 
 
 		// get the expected font
-		auto fontFactory = util::ServiceLocator::instance().get<gui::FontLibrary>();
-		std::shared_ptr<gui::FontFace> face = fontFactory->getFont("LiberationSans-Regular.ttf");
+		std::shared_ptr<gui::FontFace> face = m_fonts->getFont("LiberationSans-Regular.ttf");
 		if ( face == nullptr ){
 			// we don't know about that font...
 			return;
diff --git a/fggl/gfx/window.cpp b/fggl/gfx/window.cpp
index 379e555..1a7a117 100644
--- a/fggl/gfx/window.cpp
+++ b/fggl/gfx/window.cpp
@@ -122,10 +122,10 @@ static void fggl_joystick(int jid, int state) {
 	}
 }
 
-GlfwContext::GlfwContext( std::shared_ptr<fggl::input::Input> input ) {
+GlfwContext::GlfwContext( fggl::input::Input* input ) {
     spdlog::debug("[glfw] context creation stated");
 	auto& glfwCallbacks = GlfwInputManager::instance();
-	glfwCallbacks.setup( std::move(input) );
+	glfwCallbacks.setup( input );
 
 	glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE);
     glfwSetErrorCallback(glfw_error);
diff --git a/fggl/scenes/game.cpp b/fggl/scenes/game.cpp
index 6059ad5..8f749c9 100644
--- a/fggl/scenes/game.cpp
+++ b/fggl/scenes/game.cpp
@@ -26,8 +26,7 @@
 namespace fggl::scenes {
 
 	Game::Game(fggl::App &app) : AppState(app) {
-		auto& locator = fggl::util::ServiceLocator::instance();
-		m_input = locator.get<fggl::input::Input>();
+		m_input = app.services()->getInput();
 	}
 
 	void Game::activate() {
diff --git a/fggl/scenes/menu.cpp b/fggl/scenes/menu.cpp
index 5f90ba2..36e2ec7 100644
--- a/fggl/scenes/menu.cpp
+++ b/fggl/scenes/menu.cpp
@@ -26,8 +26,7 @@ namespace fggl::scenes {
 
 
     BasicMenu::BasicMenu(fggl::App& app) : AppState(app), m_inputs(nullptr), m_active(), m_hover(nullptr) {
-        auto& locator = fggl::util::ServiceLocator::instance();
-        m_inputs = locator.get<input::Input>();
+        m_inputs = app.services()->getInput();
     }
 
     void BasicMenu::update() {
diff --git a/include/fggl/app.hpp b/include/fggl/app.hpp
index cca6961..5a63c0a 100644
--- a/include/fggl/app.hpp
+++ b/include/fggl/app.hpp
@@ -27,6 +27,7 @@
 #include <fggl/gfx/window.hpp>
 #include <fggl/gfx/paint.hpp>
 #include <fggl/util/states.hpp>
+#include "fggl/subsystem/provider.hpp"
 
 namespace fggl {
 
@@ -82,8 +83,8 @@ namespace fggl {
 
 	class App {
 		public:
-			explicit App(const Identifer &name);
-			App(const Identifer &name, const Identifer &folderName);
+			explicit App(subsystem::ServiceProvider* serivces, const Identifer &name);
+			App(subsystem::ServiceProvider* services, const Identifer &name, const Identifer &folderName);
 
 			// class is non copy-able
 			App(const App &app) = delete;
@@ -124,6 +125,10 @@ namespace fggl {
 				return m_states.active();
 			}
 
+			inline subsystem::ServiceProvider* services() {
+				return m_subsystems;
+			}
+
 			inline ecs3::TypeRegistry* registry() {
 				return m_types.get();
 			}
@@ -143,6 +148,7 @@ namespace fggl {
 			std::unique_ptr<gfx::Window> m_window;
 			AppMachine m_states;
 			Identifer m_expectedScene;
+			subsystem::ServiceProvider* m_subsystems;
 	};
 
 }
diff --git a/include/fggl/audio/audio.hpp b/include/fggl/audio/audio.hpp
index 3e91b65..e1d1651 100644
--- a/include/fggl/audio/audio.hpp
+++ b/include/fggl/audio/audio.hpp
@@ -16,6 +16,8 @@
 #define FGGL_AUDIO_AUDIO_HPP
 
 #include <string>
+#include "fggl/subsystem/types.hpp"
+#include "fggl/data/storage.hpp"
 
 namespace fggl::audio {
 
@@ -36,6 +38,8 @@ namespace fggl::audio {
 			AudioService() = default;
 			virtual ~AudioService() = default;
 
+			virtual void setup(data::Storage* provider) = 0;
+
 			virtual void play(const std::string& filename, bool looping = false) = 0;
 			virtual void play(AudioClip& clip, bool looping = false) = 0;
 	};
diff --git a/include/fggl/audio/null_audio.hpp b/include/fggl/audio/null_audio.hpp
new file mode 100644
index 0000000..ccefb33
--- /dev/null
+++ b/include/fggl/audio/null_audio.hpp
@@ -0,0 +1,38 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_AUDIO_NULL_AUDIO_HPP
+#define FGGL_AUDIO_NULL_AUDIO_HPP
+
+#include "fggl/audio/audio.hpp"
+
+namespace fggl::audio {
+
+	class NullAudioService : public AudioService {
+		public:
+			NullAudioService() = default;
+			virtual ~NullAudioService() = default;
+
+			void setup(data::Storage* storage) override {}
+			void play(const std::string& filename, bool looping = false) override {}
+			void play(AudioClip& clip, bool looping = false) override {}
+	};
+
+} // namespace fggl::audio
+
+#endif //FGGL_AUDIO_NULL_AUDIO_HPP
diff --git a/include/fggl/audio/openal/audio.hpp b/include/fggl/audio/openal/audio.hpp
index ec17ab1..88e59d0 100644
--- a/include/fggl/audio/openal/audio.hpp
+++ b/include/fggl/audio/openal/audio.hpp
@@ -23,6 +23,7 @@
 #include <AL/alc.h>
 
 #include "fggl/audio/audio.hpp"
+#include "fggl/data/storage.hpp"
 #include "fggl/math/types.hpp"
 
 #include <string>
@@ -171,6 +172,7 @@ namespace fggl::audio::openal {
 				}
 			}
 
+			void setup(data::Storage* storage) override;
 			void play(const std::string& filename, bool looping = false) override;
 			void play(AudioClip& clip, bool looping = false) override;
 
@@ -178,6 +180,7 @@ namespace fggl::audio::openal {
 			ALCdevice* m_device;
 			ALCcontext* m_context{nullptr};
 			std::unique_ptr<AudioSource> m_defaultSource{nullptr};
+			data::Storage* m_storage = nullptr;
 	};
 
 
diff --git a/include/fggl/gfx/compat.hpp b/include/fggl/gfx/compat.hpp
index eefc4d0..0e860e1 100644
--- a/include/fggl/gfx/compat.hpp
+++ b/include/fggl/gfx/compat.hpp
@@ -38,7 +38,7 @@ namespace fggl::gfx {
 	struct ecsGlfwModule : ecs3::Module {
 
 		inline explicit
-		ecsGlfwModule(std::shared_ptr<fggl::input::Input> inputs) : context( std::make_shared<GlfwContext>(std::move(inputs))) {
+		ecsGlfwModule(fggl::input::Input* inputs) : context( std::make_shared<GlfwContext>(inputs)) {
 		}
 
 		inline
diff --git a/include/fggl/gfx/ogl/renderer.hpp b/include/fggl/gfx/ogl/renderer.hpp
index 8abc93a..4838655 100644
--- a/include/fggl/gfx/ogl/renderer.hpp
+++ b/include/fggl/gfx/ogl/renderer.hpp
@@ -67,7 +67,7 @@ namespace fggl::gfx {
 	 */
 	class OpenGL4Backend : public Graphics {
 		public:
-			explicit OpenGL4Backend(const Window &owner);
+			explicit OpenGL4Backend(const Window &owner, data::Storage* storage, gui::FontLibrary* fonts);
 			~OpenGL4Backend() override = default;
 
 			// copy bad
@@ -120,6 +120,8 @@ namespace fggl::gfx {
 			std::unique_ptr<ogl4::DebugRenderer> m_debugRenderer;
 			std::unique_ptr<ShaderCache> m_cache;
 			GLuint m_canvasPipeline;
+			data::Storage* m_storage;
+			gui::FontLibrary* m_fontLibrary;
 	};
 
 	using OpenGL4 = OpenGL4Backend;
diff --git a/include/fggl/gfx/ogl/shader.hpp b/include/fggl/gfx/ogl/shader.hpp
index ba3bd18..ef9d895 100644
--- a/include/fggl/gfx/ogl/shader.hpp
+++ b/include/fggl/gfx/ogl/shader.hpp
@@ -61,7 +61,7 @@ namespace fggl::gfx {
 
 	class ShaderCache {
 		public:
-			ShaderCache(std::shared_ptr<fggl::data::Storage> storage);
+			ShaderCache(fggl::data::Storage* storage);
 			~ShaderCache() = default;
 
 			GLuint load(const ShaderConfig &config);
diff --git a/include/fggl/gfx/ogl4/canvas.hpp b/include/fggl/gfx/ogl4/canvas.hpp
index 3e9de49..b060e68 100644
--- a/include/fggl/gfx/ogl4/canvas.hpp
+++ b/include/fggl/gfx/ogl4/canvas.hpp
@@ -29,7 +29,7 @@ namespace fggl::gfx::ogl4 {
 
 	class CanvasRenderer {
 		public:
-			CanvasRenderer();
+			CanvasRenderer(gui::FontLibrary* fonts);
 			void render(GLuint shader, const gfx::Paint& paint);
 
 			inline gfx::Bounds bounds() const {
@@ -41,7 +41,7 @@ namespace fggl::gfx::ogl4 {
 			ogl::VertexArray m_vao;
 			ogl::ArrayBuffer m_vertexList;
 			ogl::ElementBuffer m_indexList;
-			gui::FontLibrary m_fonts;
+			gui::FontLibrary* m_fonts;
 			ogl::Texture m_fontTex;
 
 			void renderShapes(const Paint &paint, GLuint shader);
diff --git a/include/fggl/gfx/window.hpp b/include/fggl/gfx/window.hpp
index 262ee1c..a51a456 100644
--- a/include/fggl/gfx/window.hpp
+++ b/include/fggl/gfx/window.hpp
@@ -30,7 +30,7 @@ namespace fggl::gfx {
 
 	class GlfwContext {
 		public:
-			explicit GlfwContext(std::shared_ptr<fggl::input::Input> input);
+			explicit GlfwContext(fggl::input::Input* input);
 			~GlfwContext();
 
 			void pollEvents();
diff --git a/include/fggl/gfx/window_input.hpp b/include/fggl/gfx/window_input.hpp
index 31edd34..ca74bec 100644
--- a/include/fggl/gfx/window_input.hpp
+++ b/include/fggl/gfx/window_input.hpp
@@ -30,7 +30,7 @@ namespace fggl::gfx {
 				return *instance;
 			}
 
-			inline void setup(std::shared_ptr<input::Input> input) {
+			inline void setup(input::Input* input) {
 				m_inputs = input;
 			}
 
@@ -81,7 +81,7 @@ namespace fggl::gfx {
 			}
 
 		private:
-			std::shared_ptr<input::Input> m_inputs;
+			input::Input* m_inputs;
 
 	};
 
diff --git a/include/fggl/gfx/windowing.hpp b/include/fggl/gfx/windowing.hpp
index 5e8fbf5..250edbf 100644
--- a/include/fggl/gfx/windowing.hpp
+++ b/include/fggl/gfx/windowing.hpp
@@ -45,10 +45,10 @@ namespace fggl::gfx {
 			virtual ~Window() = default;
 			virtual void activate() const = 0;
 
-			template<typename T>
-			void make_graphics() {
+			template<typename T, typename ...Args>
+			void make_graphics(Args... args) {
 				activate();
-				m_graphics = std::make_unique<T>(*this);
+				m_graphics = std::make_unique<T>(*this, args...);
 			}
 
 			// window-related getters
diff --git a/include/fggl/gui/fonts.hpp b/include/fggl/gui/fonts.hpp
index 332467d..7a36a60 100644
--- a/include/fggl/gui/fonts.hpp
+++ b/include/fggl/gui/fonts.hpp
@@ -77,6 +77,10 @@ namespace fggl::gui {
 			FontLibrary& operator=(const FontLibrary&) = delete;
 			FontLibrary& operator=(FontLibrary&&) = delete;
 
+			void setup(data::Storage* storage) {
+				m_storage = storage;
+			}
+
 			inline std::shared_ptr<FontFace> getFont(const std::string& name) {
 				auto fontItr = m_cache.find(name);
 				if ( fontItr != m_cache.end() ) {
@@ -84,8 +88,7 @@ namespace fggl::gui {
 				}
 
 				// need to load the font...
-				auto storage = util::ServiceLocator::instance().get<data::Storage>();
-				auto path = storage->resolvePath(data::StorageType::Data, name);
+				auto path = m_storage->resolvePath(data::StorageType::Data, name);
 
 				FT_Face face;
 				if ( FT_New_Face(m_context, path.string().c_str(), 0, &face) ) {
@@ -101,6 +104,7 @@ namespace fggl::gui {
 
 		private:
 			FT_Library m_context;
+			data::Storage* m_storage;
 			std::map<const std::string, std::shared_ptr<FontFace>> m_cache;
 	};
 
diff --git a/include/fggl/scenes/game.hpp b/include/fggl/scenes/game.hpp
index 660589b..6d88372 100644
--- a/include/fggl/scenes/game.hpp
+++ b/include/fggl/scenes/game.hpp
@@ -45,7 +45,7 @@ namespace fggl::scenes {
 			}
 
 		private:
-			std::shared_ptr<input::Input> m_input;
+			input::Input* m_input;
 			std::unique_ptr<ecs3::World> m_world;
 			std::unique_ptr<phys::PhysicsEngine> m_phys;
 			std::string m_previous = "menu";
diff --git a/include/fggl/scenes/menu.hpp b/include/fggl/scenes/menu.hpp
index 770d91b..ecc06ea 100644
--- a/include/fggl/scenes/menu.hpp
+++ b/include/fggl/scenes/menu.hpp
@@ -41,7 +41,7 @@ namespace fggl::scenes {
 			void add(const std::string &label, callback cb);
 
 		private:
-			std::shared_ptr<input::Input> m_inputs;
+			input::Input* m_inputs;
 			std::map<const std::string, callback> m_items;
 
 			// menu state
diff --git a/include/fggl/subsystem/cursed/god_object.hpp b/include/fggl/subsystem/cursed/god_object.hpp
new file mode 100644
index 0000000..01e7a2a
--- /dev/null
+++ b/include/fggl/subsystem/cursed/god_object.hpp
@@ -0,0 +1,140 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+// This is the 'quick and dirty' subsystem approach, recommended in 'Game Engine Architecture'
+// Architecturally, this is an abomination, but it works (at least until I build something dependency-based).
+//
+
+#ifndef FGGL_SUBSYSTEM_CURSED_GOD_OBJECT_HPP
+#define FGGL_SUBSYSTEM_CURSED_GOD_OBJECT_HPP
+
+#include <memory>
+
+#include "fggl/subsystem/types.hpp"
+#include "fggl/subsystem/provider.hpp"
+
+#include "fggl/audio/null_audio.hpp"
+
+namespace fggl::subsystem {
+
+	template<typename T>
+	class ServiceContainer {
+		T* m_service = nullptr;
+
+		public:
+			inline void set(T* service) {
+				m_service = service;
+			}
+
+			void release() {
+				if ( m_service != nullptr ) {
+					delete m_service;
+					m_service = nullptr;
+				}
+			}
+
+			[[nodiscard]]
+			inline T* getPtr() const {
+				return m_service;
+			}
+
+			[[nodiscard]]
+			inline T& getRef() const {
+				return *m_service;
+			}
+	};
+
+	class GodObject : public ServiceProvider,
+		private ServiceContainer<data::Storage>,
+		private ServiceContainer<audio::AudioService>
+		{
+		public:
+			GodObject() = default;
+
+			inline void setup() {
+				m_setup = true;
+				ServiceContainer<audio::AudioService>::getPtr()->setup(
+					ServiceContainer<data::Storage>::getPtr()
+				);
+				m_fonts->setup(
+					ServiceContainer<data::Storage>::getPtr()
+				);
+			}
+			inline void teardown() {}
+
+			template<typename T>
+			inline void provide(T* service) {
+				assert(service != nullptr);
+				assert( !m_setup );
+				ServiceContainer<T>::set(service);
+			}
+
+			template<typename T>
+			[[nodiscard]]
+			inline T* get() {
+				return ServiceContainer<T>::get();
+			}
+
+			inline audio::AudioService* getAudio() override {
+				assert(m_setup);
+				return ServiceContainer<audio::AudioService>::getPtr();
+			}
+
+			inline void setFontLibrary(gui::FontLibrary* fontLibrary) {
+				assert( !m_setup );
+				m_fonts = fontLibrary;
+			}
+
+			inline gui::FontLibrary* getFontLibrary() override {
+				assert(m_setup);
+				return m_fonts;
+			}
+
+			inline void setInput(input::Input* input){
+				assert( !m_setup );
+				m_input = input;
+			}
+
+			input::Input* getInput() override {
+				assert(m_setup);
+				return m_input;
+			}
+
+			data::Storage* getStorage() override {
+				assert(m_setup);
+				return ServiceContainer<data::Storage>::getPtr();
+			}
+
+			inline void setTypeRegistry(ecs3::TypeRegistry* registry) {
+				assert( !m_setup );
+				m_types = registry;
+			}
+
+			ecs3::TypeRegistry* getTypeRegistry() override {
+				assert(m_setup);
+				return m_types;
+			}
+
+		private:
+			bool m_setup = false;
+			gui::FontLibrary* m_fonts = nullptr;
+			ecs3::TypeRegistry* m_types = nullptr;
+			input::Input* m_input = nullptr;
+	};
+
+} // namespace fggl::subsystem
+
+#endif //FGGL_SUBSYSTEM_CURSED_GOD_OBJECT_HPP
diff --git a/include/fggl/subsystem/provider.hpp b/include/fggl/subsystem/provider.hpp
new file mode 100644
index 0000000..2367bff
--- /dev/null
+++ b/include/fggl/subsystem/provider.hpp
@@ -0,0 +1,40 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_SUBSYSTEM_PROVIDER_HPP
+#define FGGL_SUBSYSTEM_PROVIDER_HPP
+
+#include "fggl/audio/audio.hpp"
+#include "fggl/gui/gui.hpp"
+#include "fggl/ecs3/types.hpp"
+#include "fggl/input/input.hpp"
+
+namespace fggl::subsystem {
+
+	class ServiceProvider {
+		public:
+			virtual audio::AudioService* getAudio() = 0;
+			virtual gui::FontLibrary* getFontLibrary() = 0;
+			virtual input::Input* getInput() = 0;
+			virtual data::Storage* getStorage() = 0;
+			virtual ecs3::TypeRegistry* getTypeRegistry() = 0;
+	};
+
+} // namespace fggl::subsystem
+
+#endif //FGGL_SUBSYSTEM_PROVIDER_HPP
diff --git a/include/fggl/subsystem/subsystem.hpp b/include/fggl/subsystem/subsystem.hpp
new file mode 100644
index 0000000..6765f02
--- /dev/null
+++ b/include/fggl/subsystem/subsystem.hpp
@@ -0,0 +1,32 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_SUBSYSTEM_SUBSYSTEM_HPP
+#define FGGL_SUBSYSTEM_SUBSYSTEM_HPP
+
+#include "fggl/subsystem/cursed/god_object.hpp"
+
+namespace fggl::subsystem {
+
+	using SubsystemManager = GodObject;
+
+
+
+} // namespace fggl::subsystem
+
+#endif //FGGL_SUBSYSTEM_SUBSYSTEM_HPP
diff --git a/include/fggl/subsystem/types.hpp b/include/fggl/subsystem/types.hpp
new file mode 100644
index 0000000..e9d58cb
--- /dev/null
+++ b/include/fggl/subsystem/types.hpp
@@ -0,0 +1,36 @@
+/*
+ * This file is part of FGGL.
+ *
+ * FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * FGGL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License along with FGGL.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//
+// Created by webpigeon on 27/06/22.
+//
+
+#ifndef FGGL_SUBSYSTEM_TYPES_HPP
+#define FGGL_SUBSYSTEM_TYPES_HPP
+
+
+namespace fggl::subsystem {
+
+	class ServiceProvider;
+
+	class Subsystem {
+			virtual ~Subsystem() = default;
+
+			virtual void setup(ServiceProvider& provider) = 0;
+			virtual void teardown(ServiceProvider& provider) = 0;
+	};
+
+} // namespace fggl::subsystem
+
+#endif //FGGL_SUBSYSTEM_TYPES_HPP
diff --git a/include/fggl/util/service.hpp b/include/fggl/util/service.hpp
index bff74fc..a22bd92 100644
--- a/include/fggl/util/service.hpp
+++ b/include/fggl/util/service.hpp
@@ -16,6 +16,9 @@
 // Created by webpigeon on 20/11/2021.
 //
 
+// quick and dirty way to disable the class
+#define FGGL_UTIL_SERVICE_HPP
+
 #ifndef FGGL_UTIL_SERVICE_HPP
 #define FGGL_UTIL_SERVICE_HPP
 
-- 
GitLab