galaxy 1.0.0
Real-Time C++23 Game Programming Framework. Built on data-driven design principles and agile software engineering.
Loading...
Searching...
No Matches
Application.cpp
Go to the documentation of this file.
1
7
8#define BS_THREAD_POOL_NATIVE_EXTENSIONS
9
10#include <filesystem>
11#include <format>
12#include <chrono>
13
14#include <BS_thread_pool.hpp>
15#include <error.h>
16#include <entt/locator/locator.hpp>
17#include <mimalloc.h>
18#include <nlohmann/json.hpp>
19#include <SDL3/SDL.h>
20#include <SDL3_TTF/SDL_ttf.h>
21#include <SDL3_mixer/SDL_mixer.h>
22#include <sol/sol.hpp>
23
34#include "galaxy/lua/Lua.hpp"
36#include "galaxy/math/ZLib.hpp"
39#include "galaxy/time/Time.hpp"
40#include "galaxy/ui/Editor.hpp"
42
43#include "Application.hpp"
44
45using namespace std::chrono_literals;
46
47namespace galaxy
48{
49 App::App(const std::string& config_file)
50 {
51 SDL_SetMemoryFunctions(&mi_malloc, &mi_calloc, &mi_realloc, &mi_free);
52
54 setup_config(config_file);
57 setup_fs();
61 // setup_loader();
62 // setup_meta();
64 setup_ui();
66
67 // Load game assets.
68 // core::entt::locator<core::Loader>::ref().load_all();
69 }
70
72 {
73 entt::locator<BS::priority_thread_pool>::value().wait();
74
75 entt::locator<sol::state>::reset();
76 m_editor.reset();
77 entt::locator<SceneManager>::reset();
78 entt::locator<SystemFactory>::reset();
79 entt::locator<Mouse>::reset();
80 entt::locator<Keyboard>::reset();
81 entt::locator<Window>::reset();
82 entt::locator<VirtualFileSystem>::reset();
83 entt::locator<Log>::reset();
84 entt::locator<Config>::reset();
85 entt::locator<BS::priority_thread_pool>::reset();
86
87 MIX_Quit();
88 TTF_Quit();
89 SDL_Quit();
90 }
91
92 void App::load(const std::string& app_data)
93 {
94 // This just loads the scenes, the actual asset data is already loaded (and specificed) by config.
95 // and done in the VFS.
96
97 auto& fs = entt::locator<VirtualFileSystem>::value();
98 const auto data = fs.read(Settings::base_data());
99
100 if (!data.empty())
101 {
102 const auto decoded_zlib = ZLib::decode(data);
103 if (!decoded_zlib.empty())
104 {
105 const auto decoded_base64 = math::decode_base64(decoded_zlib);
106 if (!decoded_base64.empty())
107 {
108 const auto parsed = nlohmann::json::parse(decoded_base64);
109
110 if (!parsed.empty())
111 {
112 auto& sm = entt::locator<SceneManager>::value();
113 sm.deserialize(parsed);
114 }
115 else
116 {
117 GALAXY_LOG(GALAXY_ERROR, "Failed to parse scenemanger JSON data from memory.");
118 }
119 }
120 else
121 {
122 GALAXY_LOG(GALAXY_ERROR, "Failed to decode base64 app_data '{0}'.", app_data);
123 }
124 }
125 else
126 {
127 GALAXY_LOG(GALAXY_ERROR, "Failed to decode zlib app_data '{0}'.", app_data);
128 }
129 }
130 else
131 {
132 GALAXY_LOG(GALAXY_ERROR, "Failed to load app_data '{0}'.", app_data);
133 }
134 }
135
136 void App::save(const std::string& app_data)
137 {
138 auto& sm = entt::locator<SceneManager>::value();
139
140 const auto json = sm.serialize();
141 auto data = json.dump(4);
142
143 if constexpr (!GALAXY_DEBUG_BUILD)
144 {
145 data = math::encode_base64(data);
146 if (!data.empty())
147 {
148 data = ZLib::encode(data);
149 if (data.empty())
150 {
151 GALAXY_LOG(GALAXY_ERROR, "Failed to encode scenemanager to zlib '{0}'.", app_data);
152 }
153 }
154 else
155 {
156 GALAXY_LOG(GALAXY_ERROR, "Failed to encode scenemanager to base64 '{0}'.", app_data);
157 }
158
159 if (data.empty())
160 {
161 return;
162 }
163 }
164
165 auto& fs = entt::locator<VirtualFileSystem>::value();
166 if (!fs.write(data, app_data))
167 {
168 GALAXY_LOG(GALAXY_ERROR, "Failed to save '{0}' to disk.", app_data);
169 }
170 }
171
172 void App::run()
173 {
174 // https://stackoverflow.com/a/59446610
175 // We dont need 't' or 'alpha/render' sections.
176
177 auto& window = entt::locator<Window>::value();
178 auto& scenes = entt::locator<SceneManager>::value();
179
180 // The expression dt/1s simply converts the double-based chrono seconds
181 // into a double so it can participate in the physics computation.
182 constexpr const auto dt = std::chrono::duration<long long, std::ratio<1, 60>> {1};
183 time::dt(dt / 1.0s);
184
185 using clock = std::chrono::steady_clock;
186 using duration = decltype(clock::duration {} + dt);
187 using time_point = std::chrono::time_point<clock, duration>;
188
189 duration accum = 0s;
190 time_point prev = clock::now();
191 time_point now = clock::now();
192
193 auto updates = 0u;
194 auto frames = 0u;
195 duration perf = 0s;
196
197 while (window.is_open())
198 {
199 now = clock::now();
200 auto elapsed = now - prev;
201
202 // 250ms is the limit put in place on the frame time to cope with the spiral of death.
203 // It doesn't have to be 250ms exactly but it should be sufficiently high enough to deal with spikes in load.
204 if (elapsed > 250ms)
205 {
206 elapsed = 250ms;
207 }
208
209 prev = now;
210 accum += elapsed;
211
212 while (accum >= dt)
213 {
214 perf += dt;
215 accum -= dt;
216
217 while (SDL_PollEvent(&m_events))
218 {
219 switch (m_events.type)
220 {
221 case SDL_EVENT_LOCALE_CHANGED:
222 // TODO change language
223 break;
224
225 case SDL_EVENT_WINDOW_SHOWN:
226 // TODO enable input/audio
227 break;
228
229 case SDL_EVENT_WINDOW_HIDDEN:
230 case SDL_EVENT_WINDOW_MINIMIZED:
231 // TODO disable input/audio
232 break;
233
234 default:
235 window.handle_window_events(m_events);
236
238 {
239 scenes.on_event(m_events);
240 }
241 else
242 {
243 m_editor->handle_events(m_events);
244 }
245 break;
246 }
247 }
248
249 scenes.update();
250
251 updates++;
252 }
253
254 window.clear();
256 {
257 scenes.render();
258 }
259 else
260 {
261 m_editor->render();
262 }
263 window.swap();
264
265 frames++;
266
267 if (perf >= 1s)
268 {
269 window.append_title(std::format(" | UPS: {0}, FPS: {1}", updates, frames));
270
271 frames = 0;
272 updates = 0;
273 perf = 0s;
274 }
275 }
276 }
277
278 SDL_Event& App::events() noexcept
279 {
280 return m_events;
281 }
282
284 {
285 // Configure threadpool.
286
287 // Calc threads.
288 // We optimize for 6: 1 for audio, 1 for main, 4 for tasks.
289 auto system_cores = std::thread::hardware_concurrency();
290 if (system_cores < 6)
291 {
292 system_cores = std::thread::hardware_concurrency();
293 }
294
295 // Check for highest available priority.
296 BS::set_os_process_priority(BS::os_process_priority::high);
297 entt::locator<BS::priority_thread_pool>::emplace(system_cores, [](const std::size_t idx) {
298 BS::this_thread::set_os_thread_priority(BS::os_thread_priority::highest);
299 });
300 }
301
302 void App::setup_config(std::string_view config_file)
303 {
304 auto& config = entt::locator<Config>::emplace(config_file);
307 }
308
310 {
311 platform::configure_terminal();
312 if (!std::filesystem::exists(Settings::logs_dir()))
313 {
314 std::filesystem::create_directory(Settings::logs_dir());
315 }
316 entt::locator<Log>::emplace();
317
318 const auto path = std::format("{0}{1}{2}", Settings::logs_dir(), std::format("{0:%d-%m-%Y-[%H-%M]}", time::now()), ".log");
320
322 GALAXY_LOG(GALAXY_INFO, "App started.");
323
324 sl::set_log_callback([](const std::string& msg) {
325 GALAXY_LOG(GALAXY_ERROR, "simlib | {0}", msg);
326 });
327 }
328
330 {
332
333 platform::set_metadata(SDL_PROP_APP_METADATA_NAME_STRING, Settings::title().c_str());
334 platform::set_metadata(SDL_PROP_APP_METADATA_VERSION_STRING, Settings::version().c_str());
335 platform::set_metadata(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, Settings::identifier().c_str());
336 platform::set_metadata(SDL_PROP_APP_METADATA_CREATOR_STRING, Settings::creator().c_str());
337 platform::set_metadata(SDL_PROP_APP_METADATA_COPYRIGHT_STRING, Settings::copyright().c_str());
338 platform::set_metadata(SDL_PROP_APP_METADATA_URL_STRING, Settings::website().c_str());
339 platform::set_metadata(SDL_PROP_APP_METADATA_TYPE_STRING, "game");
340
341 // https://wiki.libsdl.org/SDL3/CategoryHints
342 platform::set_hint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "1");
343 platform::set_hint(SDL_HINT_ASSERT, "break");
344 platform::set_hint(SDL_HINT_AUDIO_CATEGORY, "playback");
345 platform::set_hint(SDL_HINT_AUDIO_CHANNELS, "2");
346 platform::set_hint(SDL_HINT_AUDIO_INCLUDE_MONITORS, "0");
347 platform::set_hint(SDL_HINT_AUTO_UPDATE_JOYSTICKS, "1");
348 platform::set_hint(SDL_HINT_AUTO_UPDATE_SENSORS, "1");
349 platform::set_hint(SDL_HINT_LOGGING, "warn");
350 platform::set_hint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, "0");
351 platform::set_hint(SDL_HINT_EVENT_LOGGING, "0");
352 platform::set_hint(SDL_HINT_FRAMEBUFFER_ACCELERATION, "opengl");
353 platform::set_hint(SDL_HINT_GAMECONTROLLER_SENSOR_FUSION, "1");
354 platform::set_hint(SDL_HINT_GPU_DRIVER, "vulkan");
355 platform::set_hint(SDL_HINT_HIDAPI_ENUMERATE_ONLY_CONTROLLERS, "1");
356 platform::set_hint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "0");
357 platform::set_hint(SDL_HINT_JOYSTICK_DIRECTINPUT, "1");
358 platform::set_hint(SDL_HINT_TV_REMOTE_AS_JOYSTICK, "1");
359 platform::set_hint(SDL_HINT_MOUSE_AUTO_CAPTURE, "1");
360 platform::set_hint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
361 platform::set_hint(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, "0");
362 platform::set_hint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1");
363 platform::set_hint(SDL_HINT_MOUSE_DPI_SCALE_CURSORS, "1");
364 platform::set_hint(SDL_HINT_PEN_MOUSE_EVENTS, "1");
365 platform::set_hint(SDL_HINT_PEN_TOUCH_EVENTS, "1");
366 platform::set_hint(SDL_HINT_QUIT_ON_LAST_WINDOW_CLOSE, "1");
367 platform::set_hint(SDL_HINT_RENDER_DRIVER, "opengl");
368 platform::set_hint(SDL_HINT_RENDER_GPU_DEBUG, "0");
369 platform::set_hint(SDL_HINT_RENDER_GPU_LOW_POWER, "0");
370 platform::set_hint(SDL_HINT_TOUCH_MOUSE_EVENTS, "1");
371 platform::set_hint(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY, "0");
372 platform::set_hint(SDL_HINT_THREAD_FORCE_REALTIME_TIME_CRITICAL, "0");
373 platform::set_hint(SDL_HINT_XINPUT_ENABLED, "1");
374 platform::set_hint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "0");
375 platform::set_hint(SDL_HINT_VIDEO_DUMMY_SAVE_FRAMES, "0");
376 platform::set_hint(SDL_HINT_VIDEO_MATCH_EXCLUSIVE_MODE_ON_MOVE, "1");
377 platform::set_hint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
378 platform::set_hint(SDL_HINT_VIDEO_OFFSCREEN_SAVE_FRAMES, "0");
379 platform::set_hint(SDL_HINT_VIDEO_SYNC_WINDOW_OPERATIONS, "0");
380 platform::set_hint(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, "1");
381 platform::set_hint(SDL_HINT_WINDOWS_CLOSE_ON_ALT_F4, "1");
382 platform::set_hint(SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS, "0");
383 platform::set_hint(SDL_HINT_WINDOWS_GAMEINPUT, "1");
384 platform::set_hint(SDL_HINT_WINDOWS_RAW_KEYBOARD, "0");
385 platform::set_hint(SDL_HINT_IME_IMPLEMENTED_UI, "0");
386
387 const auto vsync = Settings::vsync() ? "1" : "0";
388 platform::set_hint(SDL_HINT_RENDER_VSYNC, vsync);
389
390 const auto audio_freq = std::to_string(Settings::audio_freq());
391 platform::set_hint(SDL_HINT_AUDIO_FREQUENCY, audio_freq.c_str());
392
393 if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS | SDL_INIT_GAMEPAD | SDL_INIT_JOYSTICK))
394 {
395 GALAXY_LOG(GALAXY_FATAL, SDL_GetError());
396 }
397
398 if (!TTF_Init())
399 {
400 GALAXY_LOG(GALAXY_FATAL, SDL_GetError());
401 }
402
403 if (!MIX_Init())
404 {
405 GALAXY_LOG(GALAXY_FATAL, SDL_GetError());
406 }
407 }
408
410 {
411 entt::locator<VirtualFileSystem>::emplace();
412 }
413
415 {
416 auto& window = entt::locator<Window>::emplace();
417 window.set_icon(Settings::icon());
418 window.show();
419
420 // auto& sampler = entt::locator<Sampler>::emplace();
421
423 // if (Settings::mipmap())
424 //{
425 // switch (Settings::texture_filter())
426 // {
427 // case GLTextureFilter::NEAREST:
428 // sampler.set(GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
429 // sampler.set(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
430 // break;
431 // case GLTextureFilter::BILINEAR:
432 // sampler.set(GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
433 // sampler.set(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
434 // break;
435 // case GLTextureFilter::TRILINEAR:
436 // sampler.set(GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
437 // sampler.set(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
438 // break;
439 // }
440 // }
441 // else
442 //{
443 // if (Settings::texture_filter() == GLTextureFilter::NEAREST)
444 // {
445 // sampler.set(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
446 // sampler.set(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
447 // }
448 // else
449 // {
450 // sampler.set(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
451 // sampler.set(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
452 // }
453 // }
454
455 // auto ansio = std::clamp(Settings::ansiotrophy(), 1, 16);
456 // if (ansio == 3)
457 //{
458 // ansio = 4;
459 // }
460 // else if (ansio > 4 && ansio < 8)
461 //{
462 // ansio = 8;
463 // }
464 // else
465 //{
466 // ansio = 16;
467 // }
468 // sampler.setf(GL_TEXTURE_MAX_ANISOTROPY, static_cast<float>(ansio));
469
470 // sampler.set(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
471 // sampler.set(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
472 // sampler.set(GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
473 // sampler.set(GL_TEXTURE_LOD_BIAS, GL_NONE);
474 }
475
477 {
478 SDL_zero(m_events);
479
480 auto& keyboard = entt::locator<Keyboard>::emplace();
481 auto& mouse = entt::locator<Mouse>::emplace();
482
483 mouse.use_custom_mouse(Settings::cursor_icon(), Settings::cursor_offset());
485 {
486 mouse.show_cursor();
487 }
488 else
489 {
490 mouse.hide_cursor();
491 }
492 }
493
495 {
496 entt::locator<SystemFactory>::emplace();
497 }
498
499 /*
500 // void App::setup_loader()
501 //{
502 // // entt::locator<Loader>::make();
503 // }
504
505 void App::setup_meta()
506 {
507
508 // auto& ef = entt::locator<meta::EntityFactory>::emplace();
509
510 /*ef.register_component<components::Tag>("Tag");
511
512 em.register_component<components::Animated>("Animated");
513 em.register_component<components::Circle>("Circle");
514 em.register_component<components::Ellipse>("Ellipse");
515 em.register_component<components::Point>("Point");
516 em.register_component<components::Polygon>("Polygon");
517 em.register_component<components::Polyline>("Polyline");
518 em.register_component<components::RigidBody>("RigidBody");
519 em.register_component<components::Script>("Script");
520 em.register_component<components::Sprite>("Sprite");
521 em.register_component<components::Text>("Text");
522 em.register_component<components::TileMap>("TileMap");
523 em.register_component<components::Transform>("Transform");
524 em.register_component<flags::DenySerialization>("DenySerialization");
525 em.register_component<flags::Disabled>("Disabled");
526
527 em.register_dependencies<components::Animated, components::Sprite>();
528 em.register_dependencies<components::Circle, components::Transform>();
529 em.register_dependencies<components::Ellipse, components::Transform>();
530 em.register_dependencies<components::Point, components::Transform>();
531 em.register_dependencies<components::Polygon, components::Transform>();
532 em.register_dependencies<components::Polyline, components::Transform>();
533 em.register_dependencies<components::RigidBody, components::Transform>();
534 em.register_dependencies<components::Sprite, components::Transform>();
535 em.register_dependencies<components::Text, components::Transform>();
536 }*/
537
539 {
540 // entt::locator<media::SoundEngine>::make(listener_count);
541 // entt::locator<media::MusicEngine>::make(listener_count);
542 // entt::locator<media::VoiceEngine>::make(listener_count);
543 // entt::locator<resource::SoundCache>::make();
544 // entt::locator<resource::MusicCache>::make();
545 // entt::locator<resource::VoiceCache>::make();
546 // entt::locator<resource::VideoCache>::make();
547 // entt::locator<resource::Animations>::make();
548 // entt::locator<resource::Shaders>::make();
549 // entt::locator<resource::Fonts>::make();
550 // entt::locator<resource::Textures>::make();
551 // entt::locator<resource::Prefabs>::make();
552 // entt::locator<resource::Scripts>::make();
553 entt::locator<SceneManager>::emplace();
554 }
555
557 {
558 // ServiceLocator<ui::NuklearUI>::make();
559
561 {
562 m_editor = std::make_unique<Editor>();
563 }
564 }
565
567 {
568 auto& lua = entt::locator<sol::state>::emplace();
569 lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::coroutine, sol::lib::string, sol::lib::os, sol::lib::math, sol::lib::table, sol::lib::io, sol::lib::utf8);
570
571 //
572 // Add external libraries to Lua.
573 // Inject all configured galaxy into Lua.
574 // Add engine services to lua.
575 //
576 Lua::inject();
577 }
578} // namespace galaxy
#define GALAXY_ADD_SINK(sink,...)
Definition Log.hpp:28
#define GALAXY_INFO
Log.hpp galaxy.
Definition Log.hpp:23
#define GALAXY_LOG(level, msg,...)
Definition Log.hpp:29
#define GALAXY_FATAL
Definition Log.hpp:26
#define GALAXY_ERROR
Definition Log.hpp:25
void setup_config(std::string_view config_file)
Setup config module.
void run()
Main game loop.
SDL_Event & events() noexcept
Get event data.
App(const std::string &config_file="config.json")
Default constructor.
void save(const std::string &app_data)
Save all active scenes and sub data within those scenes.
void setup_fs()
Setup filesystem module.
SDL_Event m_events
Core event data.
void setup_logging()
Setup logging module.
void setup_ui()
Setup ui module.
void setup_async()
Setup async module.
void setup_input()
Setup input module.
void setup_factory()
Setup factory module.
void setup_rendering()
Setup graphics module.
std::unique_ptr< Editor > m_editor
Editor data.
void load(const std::string &app_data="base.galaxy")
Load app data file into scene manager.
void setup_services()
Setup services.
void setup_scripting()
Setup scripting module.
void setup_platform()
Setup platform module.
~App()
Destructor.
Logs a message to the console.
static void inject() noexcept
Inject everything into Lua.
Definition Lua.cpp:12
static std::string decode(const std::string &input)
Decompresses string into ZLib.
Definition ZLib.cpp:125
static std::string encode(const std::string &input)
Compresses string into ZLib.
Definition ZLib.cpp:82
std::string encode_base64(const std::string &input)
Compresses string into Base64.
Definition Base64.cpp:51
std::string decode_base64(const std::string &input)
Decompresses string into Base64.
Definition Base64.cpp:105
void seed_random() noexcept
Seed the cstdlib rng algos.
Definition Platform.cpp:20
void set_metadata(const char *type, const char *value) noexcept
Sets metadata.
Definition Platform.cpp:25
void set_hint(const char *hint, const char *value) noexcept
Sets SDL hints.
Definition Platform.cpp:30
double dt() noexcept
Get galaxy delta time.
Definition Time.cpp:34
auto now() noexcept -> std::chrono::local_time< std::chrono::system_clock::duration >
Current local time.
Definition Time.cpp:18
Application.hpp galaxy.
static auto icon() noexcept -> const std::string &
Window icon file in vfs.
Definition Settings.cpp:193
static auto logs_dir() noexcept -> const std::string &
Current root directory of application, unless it has been changed.
Definition Settings.cpp:248
static auto copyright() noexcept -> const std::string &
Copyright message.
Definition Settings.cpp:223
static auto vsync() noexcept -> bool
Vsync control.
Definition Settings.cpp:188
static auto set_settings_from_config() -> void
Set all our settings using the provided config file.
Definition Settings.cpp:82
static auto identifier() noexcept -> const std::string &
Game identifier i.e. com.galaxy.app.
Definition Settings.cpp:213
static auto cursor_offset() noexcept -> const glm::ivec2 &
Cursor selector point (offset).
Definition Settings.cpp:243
static auto title() noexcept -> const std::string &
Game title.
Definition Settings.cpp:203
static auto cursor_show() noexcept -> bool
Is the mouse cursor visible or not.
Definition Settings.cpp:233
static auto version() noexcept -> const std::string &
Game semver.
Definition Settings.cpp:208
static auto audio_freq() noexcept -> int
Set audio frequency to use with SDL.
Definition Settings.cpp:198
static auto website() noexcept -> const std::string &
Website URL.
Definition Settings.cpp:228
static auto set_config_to_default() -> void
Restore all config settings to default.
Definition Settings.cpp:23
static auto enable_editor() noexcept -> bool
Enable the debug editor UI.
Definition Settings.cpp:338
static auto cursor_icon() noexcept -> const std::string &
Cursor texture file in vfs.
Definition Settings.cpp:238
static auto creator() noexcept -> const std::string &
Owner.
Definition Settings.cpp:218
static auto base_data() noexcept -> const std::string &
Current root directory of application, unless it has been changed.
Definition Settings.cpp:253