#include "WorldConverter.hpp" #include "WorldFiles.hpp" #include "../content/ContentLUT.hpp" #include "../data/dynamic.hpp" #include "../debug/Logger.hpp" #include "../files/files.hpp" #include "../objects/Player.hpp" #include "../util/ThreadPool.hpp" #include "../voxels/Chunk.hpp" #include #include #include namespace fs = std::filesystem; static debug::Logger logger("world-converter"); class ConverterWorker : public util::Worker { std::shared_ptr converter; public: ConverterWorker(std::shared_ptr converter) : converter(converter) {} int operator()(const std::shared_ptr& task) override { converter->convert(*task); return 0; } }; WorldConverter::WorldConverter( fs::path folder, const Content* content, std::shared_ptr lut ) : wfile(std::make_unique(folder)), lut(lut), content(content) { fs::path regionsFolder = wfile->getRegions().getRegionsFolder(REGION_LAYER_VOXELS); if (!fs::is_directory(regionsFolder)) { logger.error() << "nothing to convert"; return; } tasks.push(convert_task {convert_task_type::player, wfile->getPlayerFile()}); for (auto file : fs::directory_iterator(regionsFolder)) { tasks.push(convert_task {convert_task_type::region, file.path()}); } } WorldConverter::~WorldConverter() { } std::shared_ptr WorldConverter::startTask( fs::path folder, const Content* content, std::shared_ptr lut, runnable onDone, bool multithreading ) { auto converter = std::make_shared(folder, content, lut); if (!multithreading) { converter->setOnComplete([=]() { converter->write(); onDone(); }); return converter; } auto pool = std::make_shared>( "converter-pool", [=](){return std::make_shared(converter);}, [=](int&) {} ); while (!converter->tasks.empty()) { const convert_task& task = converter->tasks.front(); auto ptr = std::make_shared(task); pool->enqueueJob(ptr); converter->tasks.pop(); } pool->setOnComplete([=]() { converter->write(); onDone(); }); return pool; } void WorldConverter::convertRegion(fs::path file) const { int x, z; std::string name = file.stem().string(); if (!WorldRegions::parseRegionFilename(name, x, z)) { logger.error() << "could not parse name " << name; return; } logger.info() << "converting region " << name; wfile->getRegions().processRegionVoxels(x, z, [=](ubyte* data) { if (lut) { Chunk::convert(data, lut.get()); } return true; }); } void WorldConverter::convertPlayer(fs::path file) const { logger.info() << "converting player " << file.u8string(); auto map = files::read_json(file); Player::convert(map.get(), lut.get()); files::write_json(file, map.get()); } void WorldConverter::convert(convert_task task) const { if (!fs::is_regular_file(task.file)) return; switch (task.type) { case convert_task_type::region: convertRegion(task.file); break; case convert_task_type::player: convertPlayer(task.file); break; } } void WorldConverter::convertNext() { if (tasks.empty()) { throw std::runtime_error("no more regions to convert"); } convert_task task = tasks.front(); tasks.pop(); tasksDone++; convert(task); } void WorldConverter::setOnComplete(runnable callback) { this->onComplete = callback; } void WorldConverter::update() { convertNext(); if (onComplete && tasks.empty()) { onComplete(); } } void WorldConverter::terminate() { tasks = {}; } bool WorldConverter::isActive() const { return !tasks.empty(); } void WorldConverter::write() { logger.info() << "writing world"; wfile->write(nullptr, content); } void WorldConverter::waitForEnd() { while (isActive()) { update(); } } uint WorldConverter::getWorkTotal() const { return tasks.size() + tasksDone; } uint WorldConverter::getWorkDone() const { return tasksDone; }