fix world converter

This commit is contained in:
MihailRis 2024-10-03 02:38:57 +03:00
parent 8f1eae4330
commit 0ad588fd33
8 changed files with 109 additions and 53 deletions

View File

@ -46,10 +46,14 @@ static void process_blocks_data(
report.dataLoss.push_back(name + ": discard data");
continue;
}
auto incapatibility = layout.checkCompatibility(*def->dataStruct);
if (!incapatibility.empty()) {
if (layout != *def->dataStruct) {
ContentIssue issue {ContentIssueType::BLOCK_DATA_LAYOUTS_UPDATE};
report.issues.push_back(issue);
report.dataLayoutsUpdated = true;
}
auto incapatibility = layout.checkCompatibility(*def->dataStruct);
if (!incapatibility.empty()) {
for (const auto& error : incapatibility) {
report.dataLoss.push_back(
"[" + name + "] field " + error.name + " - " +
@ -57,9 +61,6 @@ static void process_blocks_data(
);
}
}
if (layout != *def->dataStruct) {
report.dataLayoutsUpdated = true;
}
report.blocksDataLayouts[name] = std::move(layout);
}
}

View File

@ -100,37 +100,45 @@ void WorldConverter::createConvertTasks() {
tasks.push(ConvertTask {ConvertTaskType::PLAYER, wfile->getPlayerFile()});
}
void WorldConverter::createBlockFieldsConvertTasks() {
// blocks data conversion requires correct block indices
// so it must be done AFTER voxels conversion
const auto& regions = wfile->getRegions();
for (auto& issue : report->getIssues()) {
switch (issue.issueType) {
case ContentIssueType::BLOCK_DATA_LAYOUTS_UPDATE:
addRegionsTasks(
REGION_LAYER_BLOCKS_DATA,
ConvertTaskType::CONVERT_BLOCKS_DATA
);
break;
default:
break;
}
}
}
WorldConverter::WorldConverter(
const std::shared_ptr<WorldFiles>& worldFiles,
const Content* content,
std::shared_ptr<ContentReport> reportPtr,
bool upgradeMode
ConvertMode mode
)
: wfile(worldFiles),
report(std::move(reportPtr)),
content(content),
upgradeMode(upgradeMode)
mode(mode)
{
if (upgradeMode) {
createUpgradeTasks();
} else if (report->hasContentReorder()) {
createConvertTasks();
} else {
// blocks data conversion requires correct block indices
// so it must be done AFTER voxels conversion
const auto& regions = wfile->getRegions();
for (auto& issue : report->getIssues()) {
switch (issue.issueType) {
case ContentIssueType::BLOCK_DATA_LAYOUTS_UPDATE:
addRegionsTasks(
REGION_LAYER_BLOCKS_DATA,
ConvertTaskType::CONVERT_BLOCKS_DATA
);
break;
default:
break;
}
}
switch (mode) {
case ConvertMode::UPGRADE:
createUpgradeTasks();
break;
case ConvertMode::REINDEX:
createConvertTasks();
break;
case ConvertMode::BLOCK_FIELDS:
createBlockFieldsConvertTasks();
break;
}
}
@ -142,11 +150,11 @@ std::shared_ptr<Task> WorldConverter::startTask(
const Content* content,
const std::shared_ptr<ContentReport>& report,
const runnable& onDone,
bool upgradeMode,
ConvertMode mode,
bool multithreading
) {
auto converter = std::make_shared<WorldConverter>(
worldFiles, content, report, upgradeMode);
worldFiles, content, report, mode);
if (!multithreading) {
converter->setOnComplete([=]() {
converter->write();
@ -208,14 +216,14 @@ void WorldConverter::convertPlayer(const fs::path& file) const {
void WorldConverter::convertBlocksData(int x, int z, const ContentReport& report) const {
logger.info() << "converting blocks data";
wfile->getRegions().processBlocksData(x, z,
[=](BlocksMetadata& heap, std::unique_ptr<ubyte[]> voxelsData) {
[=](BlocksMetadata* heap, std::unique_ptr<ubyte[]> voxelsData) {
Chunk chunk(0, 0);
chunk.decode(voxelsData.get());
const auto& indices = content->getIndices()->blocks;
BlocksMetadata newHeap;
for (const auto& entry : heap) {
for (const auto& entry : *heap) {
size_t index = entry.index;
const auto& def = indices.require(chunk.voxels[index].id);
const auto& newStruct = *def.dataStruct;
@ -229,7 +237,7 @@ void WorldConverter::convertBlocksData(int x, int z, const ContentReport& report
uint8_t* dst = newHeap.allocate(index, newStruct.size());
newStruct.convert(prevStruct, entry.data(), dst, true);
}
heap = std::move(newHeap);
*heap = std::move(newHeap);
});
}
@ -286,13 +294,22 @@ bool WorldConverter::isActive() const {
}
void WorldConverter::write() {
if (upgradeMode) {
logger.info() << "refreshing version";
wfile->patchIndicesVersion("region-version", REGION_FORMAT_VERSION);
} else {
logger.info() << "writing world";
wfile->write(nullptr, content);
logger.info() << "applying changes";
auto patch = dv::object();
switch (mode) {
case ConvertMode::UPGRADE:
patch["region-version"] = REGION_FORMAT_VERSION;
break;
case ConvertMode::REINDEX:
WorldFiles::createContentIndicesCache(content->getIndices(), patch);
break;
case ConvertMode::BLOCK_FIELDS:
WorldFiles::createBlockFieldsIndices(content->getIndices(), patch);
break;
}
wfile->patchIndicesFile(patch);
wfile->write(nullptr, nullptr);
}
void WorldConverter::waitForEnd() {

View File

@ -37,6 +37,12 @@ struct ConvertTask {
RegionLayerIndex layer;
};
enum class ConvertMode {
UPGRADE,
REINDEX,
BLOCK_FIELDS,
};
class WorldConverter : public Task {
std::shared_ptr<WorldFiles> wfile;
std::shared_ptr<ContentReport> const report;
@ -44,7 +50,7 @@ class WorldConverter : public Task {
std::queue<ConvertTask> tasks;
runnable onComplete;
uint tasksDone = 0;
bool upgradeMode;
ConvertMode mode;
void upgradeRegion(
const fs::path& file, int x, int z, RegionLayerIndex layer) const;
@ -60,12 +66,13 @@ class WorldConverter : public Task {
void createUpgradeTasks();
void createConvertTasks();
void createBlockFieldsConvertTasks();
public:
WorldConverter(
const std::shared_ptr<WorldFiles>& worldFiles,
const Content* content,
std::shared_ptr<ContentReport> report,
bool upgradeMode
ConvertMode mode
);
~WorldConverter();
@ -86,7 +93,7 @@ public:
const Content* content,
const std::shared_ptr<ContentReport>& report,
const runnable& onDone,
bool upgradeMode,
ConvertMode mode,
bool multithreading
);
};

View File

@ -25,6 +25,7 @@
#include "settings.hpp"
#include "typedefs.hpp"
#include "util/data_io.hpp"
#include "util/stringutil.hpp"
#include "voxels/Block.hpp"
#include "voxels/Chunk.hpp"
#include "voxels/voxel.hpp"
@ -111,13 +112,17 @@ static void write_indices(
}
}
void WorldFiles::writeIndices(const ContentIndices* indices) {
dv::value root = dv::object();
root["region-version"] = REGION_FORMAT_VERSION;
void WorldFiles::createContentIndicesCache(
const ContentIndices* indices, dv::value& root
) {
write_indices(indices->blocks, root.list("blocks"));
write_indices(indices->items, root.list("items"));
write_indices(indices->entities, root.list("entities"));
}
void WorldFiles::createBlockFieldsIndices(
const ContentIndices* indices, dv::value& root
) {
auto& structsMap = root.object("blocks-data");
for (const auto* def : indices->blocks.getIterable()) {
if (def->dataStruct == nullptr) {
@ -125,6 +130,14 @@ void WorldFiles::writeIndices(const ContentIndices* indices) {
}
structsMap[def->name] = def->dataStruct->serialize();
}
}
void WorldFiles::writeIndices(const ContentIndices* indices) {
dv::value root = dv::object();
root["region-version"] = REGION_FORMAT_VERSION;
createContentIndicesCache(indices, root);
createBlockFieldsIndices(indices, root);
files::write_json(getIndicesFile(), root);
}
@ -178,14 +191,17 @@ bool WorldFiles::readResourcesData(const Content* content) {
return true;
}
void WorldFiles::patchIndicesVersion(const std::string& field, uint version) {
void WorldFiles::patchIndicesFile(const dv::value& map) {
fs::path file = getIndicesFile();
if (!fs::is_regular_file(file)) {
logger.error() << file.filename().u8string() << " does not exists";
return;
}
auto root = files::read_json(file);
root[field] = version;
for (const auto& [key, value] : map.asObject()) {
logger.info() << "patching indices.json: update " << util::quote(key);
root[key] = value;
}
files::write_json(file, root, true);
}

View File

@ -51,7 +51,14 @@ public:
std::optional<WorldInfo> readWorldInfo();
bool readResourcesData(const Content* content);
void patchIndicesVersion(const std::string& field, uint version);
static void createContentIndicesCache(
const ContentIndices* indices, dv::value& root
);
static void createBlockFieldsIndices(
const ContentIndices* indices, dv::value& root
);
void patchIndicesFile(const dv::value& map);
/// @brief Write all unsaved data to world files
/// @param world target world

View File

@ -318,7 +318,7 @@ void WorldRegions::processBlocksData(int x, int z, const BlockDataProc& func) {
BlocksMetadata blocksData;
blocksData.deserialize(datData.get(), datLength);
try {
func(blocksData, std::move(voxData));
func(&blocksData, std::move(voxData));
} catch (const std::exception& err) {
logger.error() << "an error ocurred while processing blocks "
"data in chunk (" << gx << ", " << gz << "): " << err.what();

View File

@ -67,7 +67,7 @@ struct regfile {
using RegionsMap = std::unordered_map<glm::ivec2, std::unique_ptr<WorldRegion>>;
using RegionProc = std::function<std::unique_ptr<ubyte[]>(std::unique_ptr<ubyte[]>,uint32_t*)>;
using InventoryProc = std::function<void(Inventory*)>;
using BlockDataProc = std::function<void(BlocksMetadata&, std::unique_ptr<ubyte[]>)>;
using BlockDataProc = std::function<void(BlocksMetadata*, std::unique_ptr<ubyte[]>)>;
/// @brief Region file pointer keeping inUse flag on until destroyed
class regfile_ptr {

View File

@ -49,6 +49,14 @@ std::shared_ptr<Task> create_converter(
const std::shared_ptr<ContentReport>& report,
const runnable& postRunnable
) {
ConvertMode mode;
if (report->isUpgradeRequired()) {
mode = ConvertMode::UPGRADE;
} else if (report->hasContentReorder()) {
mode = ConvertMode::REINDEX;
} else {
mode = ConvertMode::BLOCK_FIELDS;
}
return WorldConverter::startTask(
worldFiles,
content,
@ -59,7 +67,7 @@ std::shared_ptr<Task> create_converter(
menu->setPage("main", false);
engine->getGUI()->postRunnable([=]() { postRunnable(); });
},
report->isUpgradeRequired(),
mode,
true
);
}
@ -79,9 +87,9 @@ void show_convert_request(
);
};
std::wstring message = L"world.convert-request";
if (report->hasUpdatedLayouts()) {
message = L"world.convert-block-layouts";
std::wstring message = L"world.convert-block-layouts";
if (report->hasContentReorder()) {
message = L"world.convert-request";
}
if (report->isUpgradeRequired()) {
message = L"world.upgrade-request";