From ee1a170376e8a2ab6cbf9a99f2cf03d6ae2f31fa Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 7 Dec 2024 01:12:27 +0300 Subject: [PATCH] add headless mode (engine initialization and finalization) --- src/audio/audio.cpp | 9 ++++- src/engine.cpp | 70 ++++++++++++++++++++++++++------------ src/engine.hpp | 16 +++++++-- src/files/engine_paths.cpp | 11 ++++++ src/main.cpp | 12 +++---- src/util/command_line.cpp | 30 ++++++++-------- src/util/command_line.hpp | 4 +-- 7 files changed, 101 insertions(+), 51 deletions(-) diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index 0dc0f591..522e3705 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -8,6 +8,9 @@ #include "coders/wav.hpp" #include "AL/ALAudio.hpp" #include "NoAudio.hpp" +#include "debug/Logger.hpp" + +static debug::Logger logger("audio"); namespace audio { static speakerid_t nextId = 1; @@ -147,10 +150,14 @@ public: void audio::initialize(bool enabled) { if (enabled) { + logger.info() << "initializing ALAudio backend"; backend = ALAudio::create().release(); } if (backend == nullptr) { - std::cerr << "could not to initialize audio" << std::endl; + if (enabled) { + std::cerr << "could not to initialize audio" << std::endl; + } + logger.info() << "initializing NoAudio backend"; backend = NoAudio::create().release(); } create_channel("master"); diff --git a/src/engine.cpp b/src/engine.cpp index 6641057d..3d0b1c21 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -69,42 +69,51 @@ static std::unique_ptr load_icon(const fs::path& resdir) { return nullptr; } -Engine::Engine(EnginePaths& paths) - : settings(), settingsHandler({settings}), paths(paths), +Engine::Engine(CoreParameters coreParameters) + : params(std::move(coreParameters)), + settings(), + settingsHandler({settings}), interpreter(std::make_unique()), - network(network::Network::create(settings.network)) -{ + network(network::Network::create(settings.network)) { + if (params.headless) { + logger.info() << "headless mode is enabled"; + } + paths.setResourcesFolder(params.resFolder); + paths.setUserFilesFolder(params.userFolder); paths.prepare(); loadSettings(); auto resdir = paths.getResourcesFolder(); controller = std::make_unique(this); - if (Window::initialize(&this->settings.display)){ - throw initialize_error("could not initialize window"); + if (!params.headless) { + if (Window::initialize(&settings.display)){ + throw initialize_error("could not initialize window"); + } + if (auto icon = load_icon(resdir)) { + icon->flipY(); + Window::setIcon(icon.get()); + } + loadControls(); + + gui = std::make_unique(); + if (ENGINE_DEBUG_BUILD) { + menus::create_version_label(this); + } } - if (auto icon = load_icon(resdir)) { - icon->flipY(); - Window::setIcon(icon.get()); - } - loadControls(); - audio::initialize(settings.audio.enabled.get()); + audio::initialize(settings.audio.enabled.get() && !params.headless); create_channel(this, "master", settings.audio.volumeMaster); create_channel(this, "regular", settings.audio.volumeRegular); create_channel(this, "music", settings.audio.volumeMusic); create_channel(this, "ambient", settings.audio.volumeAmbient); create_channel(this, "ui", settings.audio.volumeUI); - gui = std::make_unique(); if (settings.ui.language.get() == "auto") { settings.ui.language.set(langs::locale_by_envlocale( platform::detect_locale(), paths.getResourcesFolder() )); } - if (ENGINE_DEBUG_BUILD) { - menus::create_version_label(this); - } keepAlive(settings.ui.language.observe([=](auto lang) { setLanguage(lang); }, true)); @@ -165,6 +174,14 @@ void Engine::saveScreenshot() { logger.info() << "saved screenshot as " << filename.u8string(); } +void Engine::run() { + if (params.headless) { + logger.info() << "nothing to do"; + } else { + mainloop(); + } +} + void Engine::mainloop() { logger.info() << "starting menu screen"; setScreen(std::make_shared(this)); @@ -219,8 +236,10 @@ void Engine::processPostRunnables() { void Engine::saveSettings() { logger.info() << "saving settings"; files::write_string(paths.getSettingsFile(), toml::stringify(settingsHandler)); - logger.info() << "saving bindings"; - files::write_string(paths.getControlsFile(), Events::writeBindings()); + if (!params.headless) { + logger.info() << "saving bindings"; + files::write_string(paths.getControlsFile(), Events::writeBindings()); + } } Engine::~Engine() { @@ -233,13 +252,18 @@ Engine::~Engine() { content.reset(); assets.reset(); interpreter.reset(); - gui.reset(); - logger.info() << "gui finished"; + if (gui) { + gui.reset(); + logger.info() << "gui finished"; + } audio::close(); network.reset(); scripting::close(); logger.info() << "scripting finished"; - Window::terminate(); + if (!params.headless) { + Window::terminate(); + logger.info() << "window closed"; + } logger.info() << "engine finished"; } @@ -434,7 +458,9 @@ void Engine::setScreen(std::shared_ptr screen) { void Engine::setLanguage(std::string locale) { langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks); - gui->getMenu()->setPageLoader(menus::create_page_loader(this)); + if (gui) { + gui->getMenu()->setPageLoader(menus::create_page_loader(this)); + } } gui::GUI* Engine::getGUI() { diff --git a/src/engine.hpp b/src/engine.hpp index efa0ff6d..aad32100 100644 --- a/src/engine.hpp +++ b/src/engine.hpp @@ -45,10 +45,17 @@ public: initialize_error(const std::string& message) : std::runtime_error(message) {} }; +struct CoreParameters { + bool headless = false; + std::filesystem::path resFolder {"res"}; + std::filesystem::path userFolder {"."}; +}; + class Engine : public util::ObjectsKeeper { + CoreParameters params; EngineSettings settings; SettingsHandler settingsHandler; - EnginePaths& paths; + EnginePaths paths; std::unique_ptr assets; std::shared_ptr screen; @@ -77,9 +84,12 @@ class Engine : public util::ObjectsKeeper { void processPostRunnables(); void loadAssets(); public: - Engine(EnginePaths& paths); + Engine(CoreParameters coreParameters); ~Engine(); - + + /// @brief Start the engine + void run(); + /// @brief Start main engine input/update/render loop. /// Automatically sets MenuScreen void mainloop(); diff --git a/src/files/engine_paths.cpp b/src/files/engine_paths.cpp index d9251897..23de64a7 100644 --- a/src/files/engine_paths.cpp +++ b/src/files/engine_paths.cpp @@ -48,6 +48,17 @@ static std::filesystem::path toCanonic(std::filesystem::path path) { } void EnginePaths::prepare() { + logger.info() << "resources folder: " << fs::canonical(resourcesFolder).u8string(); + logger.info() << "user files folder: " << fs::canonical(userFilesFolder).u8string(); + + if (!fs::is_directory(resourcesFolder)) { + throw std::runtime_error( + resourcesFolder.u8string() + " is not a directory" + ); + } + if (!fs::is_directory(userFilesFolder)) { + fs::create_directories(userFilesFolder); + } auto contentFolder = userFilesFolder / CONTENT_FOLDER; if (!fs::is_directory(contentFolder)) { fs::create_directories(contentFolder); diff --git a/src/main.cpp b/src/main.cpp index 49ac502e..a11020de 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,4 @@ #include "engine.hpp" -#include "files/engine_paths.hpp" #include "util/platform.hpp" #include "util/command_line.hpp" #include "debug/Logger.hpp" @@ -11,15 +10,14 @@ static debug::Logger logger("main"); int main(int argc, char** argv) { debug::Logger::init("latest.log"); - EnginePaths paths; - if (!parse_cmdline(argc, argv, paths)) + CoreParameters coreParameters; + if (!parse_cmdline(argc, argv, coreParameters)) { return EXIT_SUCCESS; - + } platform::configure_encoding(); try { - Engine(paths).mainloop(); - } - catch (const initialize_error& err) { + Engine(std::move(coreParameters)).run(); + } catch (const initialize_error& err) { logger.error() << "could not to initialize engine\n" << err.what(); } #ifdef NDEBUG diff --git a/src/util/command_line.cpp b/src/util/command_line.cpp index 88caf426..e5a76197 100644 --- a/src/util/command_line.cpp +++ b/src/util/command_line.cpp @@ -7,6 +7,7 @@ #include #include "files/engine_paths.hpp" +#include "engine.hpp" namespace fs = std::filesystem; @@ -41,27 +42,24 @@ public: }; static bool perform_keyword( - ArgsReader& reader, const std::string& keyword, EnginePaths& paths + ArgsReader& reader, const std::string& keyword, CoreParameters& params ) { if (keyword == "--res") { auto token = reader.next(); - if (!fs::is_directory(fs::path(token))) { - throw std::runtime_error(token + " is not a directory"); - } - paths.setResourcesFolder(fs::path(token)); - std::cout << "resources folder: " << token << std::endl; + params.resFolder = fs::u8path(token); } else if (keyword == "--dir") { auto token = reader.next(); - if (!fs::is_directory(fs::path(token))) { - fs::create_directories(fs::path(token)); - } - paths.setUserFilesFolder(fs::path(token)); - std::cout << "userfiles folder: " << token << std::endl; + params.userFolder = fs::u8path(token); } else if (keyword == "--help" || keyword == "-h") { - std::cout << "VoxelEngine command-line arguments:" << std::endl; - std::cout << " --res [path] - set resources directory" << std::endl; - std::cout << " --dir [path] - set userfiles directory" << std::endl; + std::cout << "VoxelEngine command-line arguments:\n"; + std::cout << " --help - show help\n"; + std::cout << " --res [path] - set resources directory\n"; + std::cout << " --dir [path] - set userfiles directory\n"; + std::cout << " --headless - run in headless mode\n"; + std::cout << std::endl; return false; + } else if (keyword == "--headless") { + params.headless = true; } else { std::cerr << "unknown argument " << keyword << std::endl; return false; @@ -69,13 +67,13 @@ static bool perform_keyword( return true; } -bool parse_cmdline(int argc, char** argv, EnginePaths& paths) { +bool parse_cmdline(int argc, char** argv, CoreParameters& params) { ArgsReader reader(argc, argv); reader.skip(); while (reader.hasNext()) { std::string token = reader.next(); if (reader.isKeywordArg()) { - if (!perform_keyword(reader, token, paths)) { + if (!perform_keyword(reader, token, params)) { return false; } } else { diff --git a/src/util/command_line.hpp b/src/util/command_line.hpp index 9715c52d..ebb6741d 100644 --- a/src/util/command_line.hpp +++ b/src/util/command_line.hpp @@ -1,6 +1,6 @@ #pragma once -class EnginePaths; +struct CoreParameters; /// @return false if engine start can -bool parse_cmdline(int argc, char** argv, EnginePaths& paths); +bool parse_cmdline(int argc, char** argv, CoreParameters& params);