add headless mode (engine initialization and finalization)

This commit is contained in:
MihailRis 2024-12-07 01:12:27 +03:00
parent 1ab3707343
commit ee1a170376
7 changed files with 101 additions and 51 deletions

View File

@ -8,6 +8,9 @@
#include "coders/wav.hpp" #include "coders/wav.hpp"
#include "AL/ALAudio.hpp" #include "AL/ALAudio.hpp"
#include "NoAudio.hpp" #include "NoAudio.hpp"
#include "debug/Logger.hpp"
static debug::Logger logger("audio");
namespace audio { namespace audio {
static speakerid_t nextId = 1; static speakerid_t nextId = 1;
@ -147,10 +150,14 @@ public:
void audio::initialize(bool enabled) { void audio::initialize(bool enabled) {
if (enabled) { if (enabled) {
logger.info() << "initializing ALAudio backend";
backend = ALAudio::create().release(); backend = ALAudio::create().release();
} }
if (backend == nullptr) { if (backend == nullptr) {
if (enabled) {
std::cerr << "could not to initialize audio" << std::endl; std::cerr << "could not to initialize audio" << std::endl;
}
logger.info() << "initializing NoAudio backend";
backend = NoAudio::create().release(); backend = NoAudio::create().release();
} }
create_channel("master"); create_channel("master");

View File

@ -69,18 +69,25 @@ static std::unique_ptr<ImageData> load_icon(const fs::path& resdir) {
return nullptr; return nullptr;
} }
Engine::Engine(EnginePaths& paths) Engine::Engine(CoreParameters coreParameters)
: settings(), settingsHandler({settings}), paths(paths), : params(std::move(coreParameters)),
settings(),
settingsHandler({settings}),
interpreter(std::make_unique<cmd::CommandsInterpreter>()), interpreter(std::make_unique<cmd::CommandsInterpreter>()),
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(); paths.prepare();
loadSettings(); loadSettings();
auto resdir = paths.getResourcesFolder(); auto resdir = paths.getResourcesFolder();
controller = std::make_unique<EngineController>(this); controller = std::make_unique<EngineController>(this);
if (Window::initialize(&this->settings.display)){ if (!params.headless) {
if (Window::initialize(&settings.display)){
throw initialize_error("could not initialize window"); throw initialize_error("could not initialize window");
} }
if (auto icon = load_icon(resdir)) { if (auto icon = load_icon(resdir)) {
@ -88,23 +95,25 @@ Engine::Engine(EnginePaths& paths)
Window::setIcon(icon.get()); Window::setIcon(icon.get());
} }
loadControls(); loadControls();
audio::initialize(settings.audio.enabled.get());
gui = std::make_unique<gui::GUI>();
if (ENGINE_DEBUG_BUILD) {
menus::create_version_label(this);
}
}
audio::initialize(settings.audio.enabled.get() && !params.headless);
create_channel(this, "master", settings.audio.volumeMaster); create_channel(this, "master", settings.audio.volumeMaster);
create_channel(this, "regular", settings.audio.volumeRegular); create_channel(this, "regular", settings.audio.volumeRegular);
create_channel(this, "music", settings.audio.volumeMusic); create_channel(this, "music", settings.audio.volumeMusic);
create_channel(this, "ambient", settings.audio.volumeAmbient); create_channel(this, "ambient", settings.audio.volumeAmbient);
create_channel(this, "ui", settings.audio.volumeUI); create_channel(this, "ui", settings.audio.volumeUI);
gui = std::make_unique<gui::GUI>();
if (settings.ui.language.get() == "auto") { if (settings.ui.language.get() == "auto") {
settings.ui.language.set(langs::locale_by_envlocale( settings.ui.language.set(langs::locale_by_envlocale(
platform::detect_locale(), platform::detect_locale(),
paths.getResourcesFolder() paths.getResourcesFolder()
)); ));
} }
if (ENGINE_DEBUG_BUILD) {
menus::create_version_label(this);
}
keepAlive(settings.ui.language.observe([=](auto lang) { keepAlive(settings.ui.language.observe([=](auto lang) {
setLanguage(lang); setLanguage(lang);
}, true)); }, true));
@ -165,6 +174,14 @@ void Engine::saveScreenshot() {
logger.info() << "saved screenshot as " << filename.u8string(); logger.info() << "saved screenshot as " << filename.u8string();
} }
void Engine::run() {
if (params.headless) {
logger.info() << "nothing to do";
} else {
mainloop();
}
}
void Engine::mainloop() { void Engine::mainloop() {
logger.info() << "starting menu screen"; logger.info() << "starting menu screen";
setScreen(std::make_shared<MenuScreen>(this)); setScreen(std::make_shared<MenuScreen>(this));
@ -219,9 +236,11 @@ void Engine::processPostRunnables() {
void Engine::saveSettings() { void Engine::saveSettings() {
logger.info() << "saving settings"; logger.info() << "saving settings";
files::write_string(paths.getSettingsFile(), toml::stringify(settingsHandler)); files::write_string(paths.getSettingsFile(), toml::stringify(settingsHandler));
if (!params.headless) {
logger.info() << "saving bindings"; logger.info() << "saving bindings";
files::write_string(paths.getControlsFile(), Events::writeBindings()); files::write_string(paths.getControlsFile(), Events::writeBindings());
} }
}
Engine::~Engine() { Engine::~Engine() {
saveSettings(); saveSettings();
@ -233,13 +252,18 @@ Engine::~Engine() {
content.reset(); content.reset();
assets.reset(); assets.reset();
interpreter.reset(); interpreter.reset();
if (gui) {
gui.reset(); gui.reset();
logger.info() << "gui finished"; logger.info() << "gui finished";
}
audio::close(); audio::close();
network.reset(); network.reset();
scripting::close(); scripting::close();
logger.info() << "scripting finished"; logger.info() << "scripting finished";
if (!params.headless) {
Window::terminate(); Window::terminate();
logger.info() << "window closed";
}
logger.info() << "engine finished"; logger.info() << "engine finished";
} }
@ -434,8 +458,10 @@ void Engine::setScreen(std::shared_ptr<Screen> screen) {
void Engine::setLanguage(std::string locale) { void Engine::setLanguage(std::string locale) {
langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks); langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks);
if (gui) {
gui->getMenu()->setPageLoader(menus::create_page_loader(this)); gui->getMenu()->setPageLoader(menus::create_page_loader(this));
} }
}
gui::GUI* Engine::getGUI() { gui::GUI* Engine::getGUI() {
return gui.get(); return gui.get();

View File

@ -45,10 +45,17 @@ public:
initialize_error(const std::string& message) : std::runtime_error(message) {} 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 { class Engine : public util::ObjectsKeeper {
CoreParameters params;
EngineSettings settings; EngineSettings settings;
SettingsHandler settingsHandler; SettingsHandler settingsHandler;
EnginePaths& paths; EnginePaths paths;
std::unique_ptr<Assets> assets; std::unique_ptr<Assets> assets;
std::shared_ptr<Screen> screen; std::shared_ptr<Screen> screen;
@ -77,9 +84,12 @@ class Engine : public util::ObjectsKeeper {
void processPostRunnables(); void processPostRunnables();
void loadAssets(); void loadAssets();
public: public:
Engine(EnginePaths& paths); Engine(CoreParameters coreParameters);
~Engine(); ~Engine();
/// @brief Start the engine
void run();
/// @brief Start main engine input/update/render loop. /// @brief Start main engine input/update/render loop.
/// Automatically sets MenuScreen /// Automatically sets MenuScreen
void mainloop(); void mainloop();

View File

@ -48,6 +48,17 @@ static std::filesystem::path toCanonic(std::filesystem::path path) {
} }
void EnginePaths::prepare() { 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; auto contentFolder = userFilesFolder / CONTENT_FOLDER;
if (!fs::is_directory(contentFolder)) { if (!fs::is_directory(contentFolder)) {
fs::create_directories(contentFolder); fs::create_directories(contentFolder);

View File

@ -1,5 +1,4 @@
#include "engine.hpp" #include "engine.hpp"
#include "files/engine_paths.hpp"
#include "util/platform.hpp" #include "util/platform.hpp"
#include "util/command_line.hpp" #include "util/command_line.hpp"
#include "debug/Logger.hpp" #include "debug/Logger.hpp"
@ -11,15 +10,14 @@ static debug::Logger logger("main");
int main(int argc, char** argv) { int main(int argc, char** argv) {
debug::Logger::init("latest.log"); debug::Logger::init("latest.log");
EnginePaths paths; CoreParameters coreParameters;
if (!parse_cmdline(argc, argv, paths)) if (!parse_cmdline(argc, argv, coreParameters)) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
}
platform::configure_encoding(); platform::configure_encoding();
try { try {
Engine(paths).mainloop(); Engine(std::move(coreParameters)).run();
} } catch (const initialize_error& err) {
catch (const initialize_error& err) {
logger.error() << "could not to initialize engine\n" << err.what(); logger.error() << "could not to initialize engine\n" << err.what();
} }
#ifdef NDEBUG #ifdef NDEBUG

View File

@ -7,6 +7,7 @@
#include <string> #include <string>
#include "files/engine_paths.hpp" #include "files/engine_paths.hpp"
#include "engine.hpp"
namespace fs = std::filesystem; namespace fs = std::filesystem;
@ -41,27 +42,24 @@ public:
}; };
static bool perform_keyword( static bool perform_keyword(
ArgsReader& reader, const std::string& keyword, EnginePaths& paths ArgsReader& reader, const std::string& keyword, CoreParameters& params
) { ) {
if (keyword == "--res") { if (keyword == "--res") {
auto token = reader.next(); auto token = reader.next();
if (!fs::is_directory(fs::path(token))) { params.resFolder = fs::u8path(token);
throw std::runtime_error(token + " is not a directory");
}
paths.setResourcesFolder(fs::path(token));
std::cout << "resources folder: " << token << std::endl;
} else if (keyword == "--dir") { } else if (keyword == "--dir") {
auto token = reader.next(); auto token = reader.next();
if (!fs::is_directory(fs::path(token))) { params.userFolder = fs::u8path(token);
fs::create_directories(fs::path(token));
}
paths.setUserFilesFolder(fs::path(token));
std::cout << "userfiles folder: " << token << std::endl;
} else if (keyword == "--help" || keyword == "-h") { } else if (keyword == "--help" || keyword == "-h") {
std::cout << "VoxelEngine command-line arguments:" << std::endl; std::cout << "VoxelEngine command-line arguments:\n";
std::cout << " --res [path] - set resources directory" << std::endl; std::cout << " --help - show help\n";
std::cout << " --dir [path] - set userfiles directory" << std::endl; 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; return false;
} else if (keyword == "--headless") {
params.headless = true;
} else { } else {
std::cerr << "unknown argument " << keyword << std::endl; std::cerr << "unknown argument " << keyword << std::endl;
return false; return false;
@ -69,13 +67,13 @@ static bool perform_keyword(
return true; return true;
} }
bool parse_cmdline(int argc, char** argv, EnginePaths& paths) { bool parse_cmdline(int argc, char** argv, CoreParameters& params) {
ArgsReader reader(argc, argv); ArgsReader reader(argc, argv);
reader.skip(); reader.skip();
while (reader.hasNext()) { while (reader.hasNext()) {
std::string token = reader.next(); std::string token = reader.next();
if (reader.isKeywordArg()) { if (reader.isKeywordArg()) {
if (!perform_keyword(reader, token, paths)) { if (!perform_keyword(reader, token, params)) {
return false; return false;
} }
} else { } else {

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
class EnginePaths; struct CoreParameters;
/// @return false if engine start can /// @return false if engine start can
bool parse_cmdline(int argc, char** argv, EnginePaths& paths); bool parse_cmdline(int argc, char** argv, CoreParameters& params);