#include "EngineController.hpp" #include #include #include #include "engine.hpp" #include "coders/commons.hpp" #include "debug/Logger.hpp" #include "content/ContentReport.hpp" #include "files/WorldConverter.hpp" #include "files/WorldFiles.hpp" #include "frontend/locale.hpp" #include "frontend/menu.hpp" #include "frontend/screens/LevelScreen.hpp" #include "frontend/screens/MenuScreen.hpp" #include "graphics/ui/elements/Menu.hpp" #include "graphics/ui/gui_util.hpp" #include "interfaces/Task.hpp" #include "util/stringutil.hpp" #include "world/Level.hpp" #include "world/World.hpp" #include "LevelController.hpp" namespace fs = std::filesystem; static debug::Logger logger("engine-control"); EngineController::EngineController(Engine* engine) : engine(engine) { } void EngineController::deleteWorld(const std::string& name) { fs::path folder = engine->getPaths()->getWorldFolderByName(name); guiutil::confirm( engine->getGUI(), langs::get(L"delete-confirm", L"world") + L" (" + util::str2wstr_utf8(folder.u8string()) + L")", [=]() { logger.info() << "deleting " << folder.u8string(); fs::remove_all(folder); } ); } std::shared_ptr create_converter( Engine* engine, const std::shared_ptr& worldFiles, const Content* content, const std::shared_ptr& report, const runnable& postRunnable ) { return WorldConverter::startTask( worldFiles, content, report, [=]() { auto menu = engine->getGUI()->getMenu(); menu->reset(); menu->setPage("main", false); engine->getGUI()->postRunnable([=]() { postRunnable(); }); }, report->isUpgradeRequired(), true ); } void show_convert_request( Engine* engine, const Content* content, const std::shared_ptr& report, const std::shared_ptr& worldFiles, const runnable& postRunnable ) { auto on_confirm = [=]() { auto converter = create_converter(engine, worldFiles, content, report, postRunnable); menus::show_process_panel( engine, converter, L"Converting world..." ); }; std::wstring message = L"world.convert-request"; if (report->hasUpdatedLayouts()) { message = L"world.convert-block-layouts"; } if (report->isUpgradeRequired()) { message = L"world.upgrade-request"; } else if (report->hasDataLoss()) { message = L"world.convert-with-loss"; std::wstring text; for (const auto& line : report->getDataLoss()) { text += util::str2wstr_utf8(line) + L"\n"; } guiutil::confirmWithMemo( engine->getGUI(), langs::get(message), text, on_confirm, L"", langs::get(L"Cancel") ); return; } guiutil::confirm( engine->getGUI(), langs::get(message), on_confirm, L"", langs::get(L"Cancel") ); } static void show_content_missing( Engine* engine, const std::shared_ptr& report ) { auto root = dv::object(); auto& contentEntries = root.list("content"); for (auto& entry : report->getMissingContent()) { std::string contentName = contenttype_name(entry.type); auto& contentEntry = contentEntries.object(); contentEntry["type"] = contentName; contentEntry["name"] = entry.name; } menus::show(engine, "reports/missing_content", {std::move(root)}); } static bool loadWorldContent(Engine* engine, const fs::path& folder) { return menus::call(engine, [engine, folder]() { engine->loadWorldContent(folder); }); } static void loadWorld(Engine* engine, const std::shared_ptr& worldFiles) { try { auto content = engine->getContent(); auto& packs = engine->getContentPacks(); auto& settings = engine->getSettings(); auto level = World::load(worldFiles, settings, content, packs); engine->setScreen( std::make_shared(engine, std::move(level)) ); } catch (const world_load_error& error) { guiutil::alert( engine->getGUI(), langs::get(L"Error") + L": " + util::str2wstr_utf8(error.what()) ); return; } } void EngineController::openWorld(const std::string& name, bool confirmConvert) { auto paths = engine->getPaths(); auto folder = paths->getWorldsFolder() / fs::u8path(name); if (!loadWorldContent(engine, folder)) { return; } auto* content = engine->getContent(); auto worldFiles = std::make_shared( folder, engine->getSettings().debug); if (auto report = World::checkIndices(worldFiles, content)) { if (report->hasMissingContent()) { engine->setScreen(std::make_shared(engine)); show_content_missing(engine, report); } else { if (confirmConvert) { menus::show_process_panel( engine, create_converter( engine, worldFiles, content, report, [=]() { openWorld(name, false); } ), L"Converting world..." ); } else { show_convert_request(engine, content, report, std::move(worldFiles), [=]() { openWorld(name, false); }); } } return; } loadWorld(engine, std::move(worldFiles)); } inline uint64_t str2seed(const std::string& seedstr) { if (util::is_integer(seedstr)) { try { return std::stoull(seedstr); } catch (const std::out_of_range& err) { std::hash hash; return hash(seedstr); } } else { std::hash hash; return hash(seedstr); } } void EngineController::createWorld( const std::string& name, const std::string& seedstr, const std::string& generatorID ) { uint64_t seed = str2seed(seedstr); EnginePaths* paths = engine->getPaths(); auto folder = paths->getWorldsFolder() / fs::u8path(name); if (!menus::call(engine, [this, paths, folder]() { engine->loadContent(); paths->setCurrentWorldFolder(folder); })) { return; } auto level = World::create( name, generatorID, folder, seed, engine->getSettings(), engine->getContent(), engine->getContentPacks() ); engine->setScreen(std::make_shared(engine, std::move(level))); } void EngineController::reopenWorld(World* world) { std::string wname = world->wfile->getFolder().filename().u8string(); engine->setScreen(nullptr); engine->setScreen(std::make_shared(engine)); openWorld(wname, true); } void EngineController::reconfigPacks( LevelController* controller, const std::vector& packsToAdd, const std::vector& packsToRemove ) { auto content = engine->getContent(); bool hasIndices = false; std::stringstream ss; for (const auto& id : packsToRemove) { auto runtime = content->getPackRuntime(id); if (runtime && runtime->getStats().hasSavingContent()) { if (hasIndices) { ss << ", "; } hasIndices = true; ss << id; } } runnable removeFunc = [=]() { if (controller == nullptr) { try { auto manager = engine->createPacksManager(fs::path("")); manager.scan(); std::vector names = PacksManager::getNames(engine->getContentPacks()); for (const auto& id : packsToAdd) { names.push_back(id); } for (const auto& id : packsToRemove) { manager.exclude(id); names.erase(std::find(names.begin(), names.end(), id)); } names = manager.assembly(names); engine->getContentPacks() = manager.getAll(names); } catch (const contentpack_error& err) { throw std::runtime_error( std::string(err.what()) + " [" + err.getPackId() + "]" ); } } else { auto world = controller->getLevel()->getWorld(); auto wfile = world->wfile.get(); controller->saveWorld(); auto manager = engine->createPacksManager(wfile->getFolder()); manager.scan(); auto names = PacksManager::getNames(world->getPacks()); for (const auto& id : packsToAdd) { names.push_back(id); } for (const auto& id : packsToRemove) { manager.exclude(id); names.erase(std::find(names.begin(), names.end(), id)); } wfile->removeIndices(packsToRemove); wfile->writePacks(manager.getAll(names)); reopenWorld(world); } }; if (hasIndices) { guiutil::confirm( engine->getGUI(), langs::get(L"remove-confirm", L"pack") + L" (" + util::str2wstr_utf8(ss.str()) + L")", [=]() { removeFunc(); } ); } else { removeFunc(); } }