1 /** 2 * Retrograde Engine 3 * 4 * Authors: 5 * Mike Bierlee, m.bierlee@lostmoment.com 6 * Copyright: 2014-2023 Mike Bierlee 7 * License: 8 * This software is licensed under the terms of the MIT license. 9 * The full terms of the license can be found in the LICENSE.txt file. 10 */ 11 12 module retrograde.bootstrap; 13 14 import retrograde.core.runtime : EngineRuntime, StandardEngineRuntime; 15 import retrograde.core.game : Game; 16 import retrograde.core.entity : EntityManager; 17 import retrograde.core.messaging : MessageHandler; 18 import retrograde.core.platform : Platform, PlatformSettings, NullPlatform; 19 import retrograde.core.logging : StdoutLogger; 20 import retrograde.core.input : InputMapper; 21 import retrograde.core.rendering : RenderSystem, ShaderProgram, GraphicsApi, NullGraphicsApi, DepthTestingMode; 22 import retrograde.core.storage : StorageSystem, GenericStorageSystem; 23 24 import retrograde.rendering.generic : GenericRenderSystem; 25 26 import retrograde.ai.generative.stabilityai : StabilityAiApi, StabilityAiApiImpl; 27 28 import poodinis : Inject, DependencyContainer, ResolveOption, initializedBy, existingInstance; 29 import poodinis.valueinjector.mirage : loadConfig, parseIniConfig; 30 31 import std.logger : Logger, MultiLogger, sharedLog; 32 import std.file : exists; 33 34 struct GraphicsApiSettings { 35 /* 36 * Set the initial depth testing mode. 37 * Setting it up in the bootstrap prevents unnecesary recompilation of shaders. 38 */ 39 DepthTestingMode initialDepthTestingMode = DepthTestingMode.apiDefault; 40 41 //TODO: Add more settings? 42 } 43 44 version (Have_glfw_d) { 45 import retrograde.platform.glfw : GlfwPlatform, GlfwPlatformSettings; 46 47 alias DefaultPlatform = GlfwPlatform; 48 alias DefaultPlatformSettings = GlfwPlatformSettings; 49 } else { 50 alias DefaultPlatform = NullPlatform; 51 alias DefaultPlatformSettings = PlatformSettings; 52 } 53 54 version (Have_bindbc_opengl) { 55 import retrograde.rendering.api.opengl : OpenGlGraphicsApi; 56 57 alias DefaultGraphicsApi = OpenGlGraphicsApi; 58 } else { 59 alias DefaultGraphicsApi = NullGraphicsApi; 60 } 61 62 /** 63 * Bootstrap function for quickly setting up the DI framework with all required and typically 64 * used engine functionality. After set-up, the game is started. 65 */ 66 public void startGame(GameType : Game, EngineRuntimeType: 67 EngineRuntime = StandardEngineRuntime, PlatformType: 68 Platform = DefaultPlatform, 69 RenderSystemType: 70 RenderSystem = GenericRenderSystem, 71 GraphicsApiType: 72 GraphicsApi = DefaultGraphicsApi)( 73 const PlatformSettings platformSettings = new DefaultPlatformSettings(), 74 const GraphicsApiSettings graphicsApiSettings = GraphicsApiSettings(), 75 shared DependencyContainer dependencies = new shared DependencyContainer(), 76 const bool suppressBootstrapWarnings = false 77 ) { 78 79 dependencies.setPersistentResolveOptions(ResolveOption.registerBeforeResolving); 80 81 dependencies.register!(EngineRuntime, EngineRuntimeType); 82 dependencies.register!(Game, GameType); 83 dependencies.register!(RenderSystem, RenderSystemType); 84 dependencies.register!(GraphicsApi, GraphicsApiType); 85 dependencies.register!(Platform, PlatformType); 86 dependencies.register!(StorageSystem, GenericStorageSystem); 87 dependencies.register!(StabilityAiApi, StabilityAiApiImpl); 88 89 const configPath = "./engine.ini"; 90 if (exists(configPath)) { 91 dependencies.loadConfig(configPath); 92 } else { 93 string defaultConfig = import(configPath); 94 dependencies.parseIniConfig(defaultConfig); 95 } 96 97 dependencies.register!Logger.initializedBy({ 98 auto logger = new MultiLogger(); // Cannot be shared, but must be for sharedLog 99 100 auto stdoutLogger = new StdoutLogger(); 101 if (stdoutLogger.stdoutIsAvailable()) { 102 logger.insertLogger("stdoutLogger", stdoutLogger); 103 } 104 105 // sharedLog = stdoutLogger; //Creating shared loggers is broken. Fix when phobos fixes it. 106 return logger; 107 }); 108 109 static if (is(DefaultPlatform == NullPlatform)) { 110 if (!suppressBootstrapWarnings) { 111 dependencies.resolve!Logger.warning( 112 "No platform implementation available. Using NullPlatform. Add a suitable platform library to your project to get rid of this warning." 113 ); 114 } 115 } 116 117 static if (is(DefaultGraphicsApi == NullGraphicsApi)) { 118 if (!suppressBootstrapWarnings) { 119 dependencies.resolve!Logger.warning( 120 "No graphics api implementation available. Using NullGraphicsApi. Add a suitable graphics api library to your project to get rid of this warning." 121 ); 122 } 123 } 124 125 auto graphicsApi = dependencies.resolve!GraphicsApi; 126 graphicsApi.setDepthTestingMode(graphicsApiSettings.initialDepthTestingMode); 127 128 auto renderSystem = dependencies.resolve!RenderSystem; 129 auto entityManager = dependencies.resolve!EntityManager; 130 entityManager.addEntityProcessor(renderSystem); 131 132 auto runtime = dependencies.resolve!EngineRuntime; 133 runtime.startGame(platformSettings); 134 } 135 136 /** 137 * An implementation of the Game interface that contains the usual engine functionality. 138 * 139 * A bootstrap game takes care of initializing, updating and rendering an EntityManager, 140 * MessageHandler and InputMapper. Make sure to call super.X() when overriding any of the Game 141 * interface methods. 142 */ 143 class BootstrapGame : Game { 144 protected @Inject EntityManager entityManager; 145 protected @Inject MessageHandler messageHandler; 146 protected @Inject InputMapper inputMapper; 147 148 public void initialize() { 149 } 150 151 public void update() { 152 messageHandler.shiftStandbyToActiveQueue(); 153 inputMapper.update(); 154 entityManager.update(); 155 } 156 157 public void render(double extraPolation) { 158 entityManager.draw(); 159 } 160 161 public void terminate() { 162 } 163 } 164 165 version (unittest) { 166 class TestGame : Game { 167 public bool isInitialized; 168 169 @Inject private EngineRuntime runtime; 170 @Inject EntityManager entityManager; 171 @Inject MessageHandler messageHandler; 172 173 public override void initialize() { 174 isInitialized = true; 175 } 176 177 public override void update() { 178 runtime.terminate(); 179 } 180 181 public override void render(double extraPolation) { 182 } 183 184 public override void terminate() { 185 } 186 } 187 188 @("Bootstrap of testgame") 189 unittest { 190 auto dependencies = new shared DependencyContainer(); 191 startGame!TestGame(new PlatformSettings(), GraphicsApiSettings(), dependencies, true); 192 const game = dependencies.resolve!TestGame; 193 assert(game.isInitialized); 194 } 195 }