VoxelEngine/src/files/WorldConverter.cpp
2024-08-04 22:26:08 +03:00

176 lines
4.4 KiB
C++

#include "WorldConverter.hpp"
#include <iostream>
#include <memory>
#include <stdexcept>
#include <utility>
#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 "WorldFiles.hpp"
namespace fs = std::filesystem;
static debug::Logger logger("world-converter");
class ConverterWorker : public util::Worker<convert_task, int> {
std::shared_ptr<WorldConverter> converter;
public:
ConverterWorker(std::shared_ptr<WorldConverter> converter)
: converter(std::move(converter)) {
}
int operator()(const std::shared_ptr<convert_task>& task) override {
converter->convert(*task);
return 0;
}
};
WorldConverter::WorldConverter(
const fs::path& folder,
const Content* content,
std::shared_ptr<ContentLUT> lut
)
: wfile(std::make_unique<WorldFiles>(folder)),
lut(std::move(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 (const auto& file : fs::directory_iterator(regionsFolder)) {
tasks.push(convert_task {convert_task_type::region, file.path()});
}
}
WorldConverter::~WorldConverter() {
}
std::shared_ptr<Task> WorldConverter::startTask(
const fs::path& folder,
const Content* content,
const std::shared_ptr<ContentLUT>& lut,
const runnable& onDone,
bool multithreading
) {
auto converter = std::make_shared<WorldConverter>(folder, content, lut);
if (!multithreading) {
converter->setOnComplete([=]() {
converter->write();
onDone();
});
return converter;
}
auto pool = std::make_shared<util::ThreadPool<convert_task, int>>(
"converter-pool",
[=]() { return std::make_shared<ConverterWorker>(converter); },
[=](int&) {}
);
auto& converterTasks = converter->tasks;
while (!converterTasks.empty()) {
const convert_task& task = converterTasks.front();
auto ptr = std::make_shared<convert_task>(task);
pool->enqueueJob(ptr);
converterTasks.pop();
}
pool->setOnComplete([=]() {
converter->write();
onDone();
});
return pool;
}
void WorldConverter::convertRegion(const 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(const 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(const 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 = std::move(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;
}