update WorldConverter (WIP)
This commit is contained in:
parent
3f826a88d3
commit
728795f0f3
@ -14,10 +14,12 @@
|
|||||||
ContentReport::ContentReport(
|
ContentReport::ContentReport(
|
||||||
const ContentIndices* indices,
|
const ContentIndices* indices,
|
||||||
size_t blocksCount,
|
size_t blocksCount,
|
||||||
size_t itemsCount
|
size_t itemsCount,
|
||||||
|
uint regionsVersion
|
||||||
)
|
)
|
||||||
: blocks(blocksCount, indices->blocks, BLOCK_VOID, ContentType::BLOCK),
|
: blocks(blocksCount, indices->blocks, BLOCK_VOID, ContentType::BLOCK),
|
||||||
items(itemsCount, indices->items, ITEM_VOID, ContentType::ITEM)
|
items(itemsCount, indices->items, ITEM_VOID, ContentType::ITEM),
|
||||||
|
regionsVersion(regionsVersion)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@ -38,6 +40,8 @@ std::shared_ptr<ContentReport> ContentReport::create(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto root = files::read_json(filename);
|
auto root = files::read_json(filename);
|
||||||
|
// TODO: remove default value 2 in 0.24
|
||||||
|
uint regionsVersion = root->get("region-version", 2U);
|
||||||
auto blocklist = root->list("blocks");
|
auto blocklist = root->list("blocks");
|
||||||
auto itemlist = root->list("items");
|
auto itemlist = root->list("items");
|
||||||
|
|
||||||
@ -45,7 +49,8 @@ std::shared_ptr<ContentReport> ContentReport::create(
|
|||||||
size_t blocks_c = get_entries_count(indices->blocks, blocklist);
|
size_t blocks_c = get_entries_count(indices->blocks, blocklist);
|
||||||
size_t items_c = get_entries_count(indices->items, itemlist);
|
size_t items_c = get_entries_count(indices->items, itemlist);
|
||||||
|
|
||||||
auto report = std::make_shared<ContentReport>(indices, blocks_c, items_c);
|
auto report = std::make_shared<ContentReport>(
|
||||||
|
indices, blocks_c, items_c, regionsVersion);
|
||||||
report->blocks.setup(blocklist.get(), content->blocks);
|
report->blocks.setup(blocklist.get(), content->blocks);
|
||||||
report->items.setup(itemlist.get(), content->items);
|
report->items.setup(itemlist.get(), content->items);
|
||||||
report->buildIssues();
|
report->buildIssues();
|
||||||
|
|||||||
@ -9,17 +9,22 @@
|
|||||||
#include "data/dynamic.hpp"
|
#include "data/dynamic.hpp"
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "Content.hpp"
|
#include "Content.hpp"
|
||||||
|
#include "files/world_regions_fwd.hpp"
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
enum class ContentIssueType {
|
enum class ContentIssueType {
|
||||||
REORDER,
|
REORDER,
|
||||||
MISSING,
|
MISSING,
|
||||||
|
REGION_FORMAT_UPDATE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ContentIssue {
|
struct ContentIssue {
|
||||||
ContentIssueType issueType;
|
ContentIssueType issueType;
|
||||||
ContentType contentType;
|
union {
|
||||||
|
ContentType contentType;
|
||||||
|
RegionLayerIndex regionLayer;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ContentEntry {
|
struct ContentEntry {
|
||||||
@ -29,12 +34,16 @@ struct ContentEntry {
|
|||||||
|
|
||||||
class WorldFiles;
|
class WorldFiles;
|
||||||
|
|
||||||
|
/// @brief Content unit lookup table
|
||||||
|
/// @tparam T index type
|
||||||
|
/// @tparam U unit class
|
||||||
template <typename T, class U>
|
template <typename T, class U>
|
||||||
class ContentUnitLUT {
|
class ContentUnitLUT {
|
||||||
std::vector<T> indices;
|
std::vector<T> indices;
|
||||||
std::vector<std::string> names;
|
std::vector<std::string> names;
|
||||||
bool missingContent = false;
|
bool missingContent = false;
|
||||||
bool reorderContent = false;
|
bool reorderContent = false;
|
||||||
|
/// @brief index that will be used to mark missing unit
|
||||||
T missingValue;
|
T missingValue;
|
||||||
ContentType type;
|
ContentType type;
|
||||||
public:
|
public:
|
||||||
@ -110,13 +119,15 @@ class ContentReport {
|
|||||||
public:
|
public:
|
||||||
ContentUnitLUT<blockid_t, Block> blocks;
|
ContentUnitLUT<blockid_t, Block> blocks;
|
||||||
ContentUnitLUT<itemid_t, ItemDef> items;
|
ContentUnitLUT<itemid_t, ItemDef> items;
|
||||||
|
uint regionsVersion;
|
||||||
|
|
||||||
std::vector<ContentIssue> issues;
|
std::vector<ContentIssue> issues;
|
||||||
|
|
||||||
ContentReport(
|
ContentReport(
|
||||||
const ContentIndices* indices,
|
const ContentIndices* indices,
|
||||||
size_t blocks,
|
size_t blocks,
|
||||||
size_t items
|
size_t items,
|
||||||
|
uint regionsVersion
|
||||||
);
|
);
|
||||||
|
|
||||||
static std::shared_ptr<ContentReport> create(
|
static std::shared_ptr<ContentReport> create(
|
||||||
@ -131,6 +142,9 @@ public:
|
|||||||
inline bool hasMissingContent() const {
|
inline bool hasMissingContent() const {
|
||||||
return blocks.hasMissingContent() || items.hasMissingContent();
|
return blocks.hasMissingContent() || items.hasMissingContent();
|
||||||
}
|
}
|
||||||
|
inline bool isUpgradeRequired() const {
|
||||||
|
return regionsVersion < REGION_FORMAT_VERSION;
|
||||||
|
}
|
||||||
void buildIssues();
|
void buildIssues();
|
||||||
|
|
||||||
const std::vector<ContentIssue>& getIssues() const;
|
const std::vector<ContentIssue>& getIssues() const;
|
||||||
|
|||||||
@ -12,43 +12,111 @@
|
|||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "util/ThreadPool.hpp"
|
#include "util/ThreadPool.hpp"
|
||||||
#include "voxels/Chunk.hpp"
|
#include "voxels/Chunk.hpp"
|
||||||
|
#include "items/Inventory.hpp"
|
||||||
#include "WorldFiles.hpp"
|
#include "WorldFiles.hpp"
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
static debug::Logger logger("world-converter");
|
static debug::Logger logger("world-converter");
|
||||||
|
|
||||||
class ConverterWorker : public util::Worker<convert_task, int> {
|
class ConverterWorker : public util::Worker<ConvertTask, int> {
|
||||||
std::shared_ptr<WorldConverter> converter;
|
std::shared_ptr<WorldConverter> converter;
|
||||||
public:
|
public:
|
||||||
ConverterWorker(std::shared_ptr<WorldConverter> converter)
|
ConverterWorker(std::shared_ptr<WorldConverter> converter)
|
||||||
: converter(std::move(converter)) {
|
: converter(std::move(converter)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int operator()(const std::shared_ptr<convert_task>& task) override {
|
int operator()(const std::shared_ptr<ConvertTask>& task) override {
|
||||||
converter->convert(*task);
|
converter->convert(*task);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void WorldConverter::addRegionsTasks(
|
||||||
|
RegionLayerIndex layerid,
|
||||||
|
ConvertTaskType taskType
|
||||||
|
) {
|
||||||
|
const auto& regions = wfile->getRegions();
|
||||||
|
auto regionsFolder = regions.getRegionsFolder(layerid);
|
||||||
|
if (!fs::is_directory(regionsFolder)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const auto& file : fs::directory_iterator(regionsFolder)) {
|
||||||
|
int x, z;
|
||||||
|
std::string name = file.path().stem().string();
|
||||||
|
if (!WorldRegions::parseRegionFilename(name, x, z)) {
|
||||||
|
logger.error() << "could not parse region name " << name;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tasks.push(ConvertTask {taskType, file.path(), x, z});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldConverter::createUpgradeTasks() {
|
||||||
|
const auto& regions = wfile->getRegions();
|
||||||
|
for (auto& issue : report->getIssues()) {
|
||||||
|
if (issue.issueType != ContentIssueType::REGION_FORMAT_UPDATE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (issue.regionLayer == REGION_LAYER_VOXELS) {
|
||||||
|
addRegionsTasks(issue.regionLayer, ConvertTaskType::UPGRADE_VOXELS);
|
||||||
|
} else {
|
||||||
|
addRegionsTasks(issue.regionLayer, ConvertTaskType::UPGRADE_SIMPLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldConverter::createConvertTasks() {
|
||||||
|
auto handleReorder = [=](ContentType contentType) {
|
||||||
|
switch (contentType) {
|
||||||
|
case ContentType::BLOCK:
|
||||||
|
addRegionsTasks(
|
||||||
|
REGION_LAYER_VOXELS,
|
||||||
|
ConvertTaskType::VOXELS
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case ContentType::ITEM:
|
||||||
|
addRegionsTasks(
|
||||||
|
REGION_LAYER_INVENTORIES,
|
||||||
|
ConvertTaskType::INVENTORIES
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto& regions = wfile->getRegions();
|
||||||
|
for (auto& issue : report->getIssues()) {
|
||||||
|
switch (issue.issueType) {
|
||||||
|
case ContentIssueType::REGION_FORMAT_UPDATE:
|
||||||
|
break;
|
||||||
|
case ContentIssueType::MISSING:
|
||||||
|
throw std::runtime_error("issue can't be resolved");
|
||||||
|
case ContentIssueType::REORDER:
|
||||||
|
handleReorder(issue.contentType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.push(ConvertTask {ConvertTaskType::PLAYER, wfile->getPlayerFile()});
|
||||||
|
}
|
||||||
|
|
||||||
WorldConverter::WorldConverter(
|
WorldConverter::WorldConverter(
|
||||||
const std::shared_ptr<WorldFiles>& worldFiles,
|
const std::shared_ptr<WorldFiles>& worldFiles,
|
||||||
const Content* content,
|
const Content* content,
|
||||||
std::shared_ptr<ContentReport> report
|
std::shared_ptr<ContentReport> reportPtr,
|
||||||
|
bool upgradeMode
|
||||||
)
|
)
|
||||||
: wfile(worldFiles),
|
: wfile(worldFiles),
|
||||||
report(std::move(report)),
|
report(std::move(reportPtr)),
|
||||||
content(content) {
|
content(content),
|
||||||
fs::path regionsFolder =
|
upgradeMode(upgradeMode)
|
||||||
wfile->getRegions().getRegionsFolder(REGION_LAYER_VOXELS);
|
{
|
||||||
if (!fs::is_directory(regionsFolder)) {
|
if (upgradeMode) {
|
||||||
logger.error() << "nothing to convert";
|
createUpgradeTasks();
|
||||||
return;
|
} else {
|
||||||
}
|
createConvertTasks();
|
||||||
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()});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,9 +128,11 @@ std::shared_ptr<Task> WorldConverter::startTask(
|
|||||||
const Content* content,
|
const Content* content,
|
||||||
const std::shared_ptr<ContentReport>& report,
|
const std::shared_ptr<ContentReport>& report,
|
||||||
const runnable& onDone,
|
const runnable& onDone,
|
||||||
|
bool upgradeMode,
|
||||||
bool multithreading
|
bool multithreading
|
||||||
) {
|
) {
|
||||||
auto converter = std::make_shared<WorldConverter>(worldFiles, content, report);
|
auto converter = std::make_shared<WorldConverter>(
|
||||||
|
worldFiles, content, report, upgradeMode);
|
||||||
if (!multithreading) {
|
if (!multithreading) {
|
||||||
converter->setOnComplete([=]() {
|
converter->setOnComplete([=]() {
|
||||||
converter->write();
|
converter->write();
|
||||||
@ -70,15 +140,15 @@ std::shared_ptr<Task> WorldConverter::startTask(
|
|||||||
});
|
});
|
||||||
return converter;
|
return converter;
|
||||||
}
|
}
|
||||||
auto pool = std::make_shared<util::ThreadPool<convert_task, int>>(
|
auto pool = std::make_shared<util::ThreadPool<ConvertTask, int>>(
|
||||||
"converter-pool",
|
"converter-pool",
|
||||||
[=]() { return std::make_shared<ConverterWorker>(converter); },
|
[=]() { return std::make_shared<ConverterWorker>(converter); },
|
||||||
[=](int&) {}
|
[=](int&) {}
|
||||||
);
|
);
|
||||||
auto& converterTasks = converter->tasks;
|
auto& converterTasks = converter->tasks;
|
||||||
while (!converterTasks.empty()) {
|
while (!converterTasks.empty()) {
|
||||||
const convert_task& task = converterTasks.front();
|
const ConvertTask& task = converterTasks.front();
|
||||||
auto ptr = std::make_shared<convert_task>(task);
|
auto ptr = std::make_shared<ConvertTask>(task);
|
||||||
pool->enqueueJob(ptr);
|
pool->enqueueJob(ptr);
|
||||||
converterTasks.pop();
|
converterTasks.pop();
|
||||||
}
|
}
|
||||||
@ -89,19 +159,27 @@ std::shared_ptr<Task> WorldConverter::startTask(
|
|||||||
return pool;
|
return pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldConverter::convertRegion(const fs::path& file) const {
|
void WorldConverter::upgradeSimple(const fs::path& file, int x, int z) const {
|
||||||
int x, z;
|
throw std::runtime_error("unsupported region format");
|
||||||
std::string name = file.stem().string();
|
}
|
||||||
if (!WorldRegions::parseRegionFilename(name, x, z)) {
|
|
||||||
logger.error() << "could not parse name " << name;
|
void WorldConverter::upgradeVoxels(const fs::path& file, int x, int z) const {
|
||||||
return;
|
throw std::runtime_error("unsupported region format");
|
||||||
}
|
}
|
||||||
logger.info() << "converting region " << name;
|
|
||||||
wfile->getRegions().processRegionVoxels(x, z, [=](ubyte* data) {
|
void WorldConverter::convertVoxels(const fs::path& file, int x, int z) const {
|
||||||
if (report) {
|
logger.info() << "converting voxels region " << x << "_" << z;
|
||||||
Chunk::convert(data, report.get());
|
wfile->getRegions().processRegion(x, z, REGION_LAYER_VOXELS, CHUNK_DATA_LEN,
|
||||||
}
|
[=](std::unique_ptr<ubyte[]> data, uint32_t*) {
|
||||||
return true;
|
Chunk::convert(data.get(), report.get());
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorldConverter::convertInventories(const fs::path& file, int x, int z) const {
|
||||||
|
logger.info() << "converting inventories region " << x << "_" << z;
|
||||||
|
wfile->getRegions().processInventories(x, z, [=](Inventory* inventory) {
|
||||||
|
inventory->convert(report.get());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,14 +190,23 @@ void WorldConverter::convertPlayer(const fs::path& file) const {
|
|||||||
files::write_json(file, map.get());
|
files::write_json(file, map.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldConverter::convert(const convert_task& task) const {
|
void WorldConverter::convert(const ConvertTask& task) const {
|
||||||
if (!fs::is_regular_file(task.file)) return;
|
if (!fs::is_regular_file(task.file)) return;
|
||||||
|
|
||||||
switch (task.type) {
|
switch (task.type) {
|
||||||
case convert_task_type::region:
|
case ConvertTaskType::UPGRADE_SIMPLE:
|
||||||
convertRegion(task.file);
|
upgradeSimple(task.file, task.x, task.z);
|
||||||
break;
|
break;
|
||||||
case convert_task_type::player:
|
case ConvertTaskType::UPGRADE_VOXELS:
|
||||||
|
upgradeVoxels(task.file, task.x, task.z);
|
||||||
|
break;
|
||||||
|
case ConvertTaskType::VOXELS:
|
||||||
|
convertVoxels(task.file, task.x, task.z);
|
||||||
|
break;
|
||||||
|
case ConvertTaskType::INVENTORIES:
|
||||||
|
convertInventories(task.file, task.x, task.z);
|
||||||
|
break;
|
||||||
|
case ConvertTaskType::PLAYER:
|
||||||
convertPlayer(task.file);
|
convertPlayer(task.file);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -129,7 +216,7 @@ void WorldConverter::convertNext() {
|
|||||||
if (tasks.empty()) {
|
if (tasks.empty()) {
|
||||||
throw std::runtime_error("no more regions to convert");
|
throw std::runtime_error("no more regions to convert");
|
||||||
}
|
}
|
||||||
convert_task task = tasks.front();
|
ConvertTask task = tasks.front();
|
||||||
tasks.pop();
|
tasks.pop();
|
||||||
tasksDone++;
|
tasksDone++;
|
||||||
|
|
||||||
@ -157,7 +244,7 @@ bool WorldConverter::isActive() const {
|
|||||||
|
|
||||||
void WorldConverter::write() {
|
void WorldConverter::write() {
|
||||||
logger.info() << "writing world";
|
logger.info() << "writing world";
|
||||||
wfile->write(nullptr, content);
|
wfile->write(nullptr, upgradeMode ? nullptr : content);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldConverter::waitForEnd() {
|
void WorldConverter::waitForEnd() {
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "delegates.hpp"
|
#include "delegates.hpp"
|
||||||
#include "interfaces/Task.hpp"
|
#include "interfaces/Task.hpp"
|
||||||
|
#include "files/world_regions_fwd.hpp"
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
@ -14,32 +15,59 @@ class Content;
|
|||||||
class ContentReport;
|
class ContentReport;
|
||||||
class WorldFiles;
|
class WorldFiles;
|
||||||
|
|
||||||
enum class convert_task_type { region, player };
|
enum class ConvertTaskType {
|
||||||
|
/// @brief rewrite voxels region indices
|
||||||
|
VOXELS,
|
||||||
|
/// @brief rewrite inventories region indices
|
||||||
|
INVENTORIES,
|
||||||
|
/// @brief rewrite player
|
||||||
|
PLAYER,
|
||||||
|
/// @brief refresh region file version
|
||||||
|
UPGRADE_SIMPLE,
|
||||||
|
/// @brief rewrite voxels region file to new format
|
||||||
|
UPGRADE_VOXELS,
|
||||||
|
};
|
||||||
|
|
||||||
struct convert_task {
|
struct ConvertTask {
|
||||||
convert_task_type type;
|
ConvertTaskType type;
|
||||||
fs::path file;
|
fs::path file;
|
||||||
|
|
||||||
|
/// @brief region coords
|
||||||
|
int x, z;
|
||||||
};
|
};
|
||||||
|
|
||||||
class WorldConverter : public Task {
|
class WorldConverter : public Task {
|
||||||
std::shared_ptr<WorldFiles> wfile;
|
std::shared_ptr<WorldFiles> wfile;
|
||||||
std::shared_ptr<ContentReport> const report;
|
std::shared_ptr<ContentReport> const report;
|
||||||
const Content* const content;
|
const Content* const content;
|
||||||
std::queue<convert_task> tasks;
|
std::queue<ConvertTask> tasks;
|
||||||
runnable onComplete;
|
runnable onComplete;
|
||||||
uint tasksDone = 0;
|
uint tasksDone = 0;
|
||||||
|
bool upgradeMode;
|
||||||
|
|
||||||
|
void upgradeSimple(const fs::path& file, int x, int z) const;
|
||||||
|
void upgradeVoxels(const fs::path& file, int x, int z) const;
|
||||||
void convertPlayer(const fs::path& file) const;
|
void convertPlayer(const fs::path& file) const;
|
||||||
void convertRegion(const fs::path& file) const;
|
void convertVoxels(const fs::path& file, int x, int z) const;
|
||||||
|
void convertInventories(const fs::path& file, int x, int z) const;
|
||||||
|
|
||||||
|
void addRegionsTasks(
|
||||||
|
RegionLayerIndex layerid,
|
||||||
|
ConvertTaskType taskType
|
||||||
|
);
|
||||||
|
|
||||||
|
void createUpgradeTasks();
|
||||||
|
void createConvertTasks();
|
||||||
public:
|
public:
|
||||||
WorldConverter(
|
WorldConverter(
|
||||||
const std::shared_ptr<WorldFiles>& worldFiles,
|
const std::shared_ptr<WorldFiles>& worldFiles,
|
||||||
const Content* content,
|
const Content* content,
|
||||||
std::shared_ptr<ContentReport> report
|
std::shared_ptr<ContentReport> report,
|
||||||
|
bool upgradeMode
|
||||||
);
|
);
|
||||||
~WorldConverter();
|
~WorldConverter();
|
||||||
|
|
||||||
void convert(const convert_task& task) const;
|
void convert(const ConvertTask& task) const;
|
||||||
void convertNext();
|
void convertNext();
|
||||||
void setOnComplete(runnable callback);
|
void setOnComplete(runnable callback);
|
||||||
void write();
|
void write();
|
||||||
@ -56,6 +84,7 @@ public:
|
|||||||
const Content* content,
|
const Content* content,
|
||||||
const std::shared_ptr<ContentReport>& report,
|
const std::shared_ptr<ContentReport>& report,
|
||||||
const runnable& onDone,
|
const runnable& onDone,
|
||||||
|
bool upgradeMode,
|
||||||
bool multithreading
|
bool multithreading
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -74,7 +74,9 @@ fs::path WorldFiles::getPacksFile() const {
|
|||||||
return directory / fs::path("packs.list");
|
return directory / fs::path("packs.list");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldFiles::write(const World* world, const Content* content) {
|
void WorldFiles::write(
|
||||||
|
const World* world, const Content* content
|
||||||
|
) {
|
||||||
if (world) {
|
if (world) {
|
||||||
writeWorldInfo(world->getInfo());
|
writeWorldInfo(world->getInfo());
|
||||||
if (!fs::exists(getPacksFile())) {
|
if (!fs::exists(getPacksFile())) {
|
||||||
@ -84,8 +86,10 @@ void WorldFiles::write(const World* world, const Content* content) {
|
|||||||
if (generatorTestMode) {
|
if (generatorTestMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
writeIndices(content->getIndices());
|
if (content) {
|
||||||
regions.write();
|
writeIndices(content->getIndices());
|
||||||
|
}
|
||||||
|
regions.writeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldFiles::writePacks(const std::vector<ContentPack>& packs) {
|
void WorldFiles::writePacks(const std::vector<ContentPack>& packs) {
|
||||||
|
|||||||
@ -101,9 +101,8 @@ void WorldRegions::put(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr<ubyte[]> write_inventories(
|
static std::unique_ptr<ubyte[]> write_inventories(
|
||||||
Chunk* chunk, uint& datasize
|
const chunk_inventories_map& inventories, uint32_t& datasize
|
||||||
) {
|
) {
|
||||||
auto& inventories = chunk->inventories;
|
|
||||||
ByteBuilder builder;
|
ByteBuilder builder;
|
||||||
builder.putInt32(inventories.size());
|
builder.putInt32(inventories.size());
|
||||||
for (auto& entry : inventories) {
|
for (auto& entry : inventories) {
|
||||||
@ -120,6 +119,22 @@ static std::unique_ptr<ubyte[]> write_inventories(
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static chunk_inventories_map load_inventories(const ubyte* src, uint32_t size) {
|
||||||
|
chunk_inventories_map inventories;
|
||||||
|
ByteReader reader(src, size);
|
||||||
|
auto count = reader.getInt32();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
uint index = reader.getInt32();
|
||||||
|
uint size = reader.getInt32();
|
||||||
|
auto map = json::from_binary(reader.pointer(), size);
|
||||||
|
reader.skip(size);
|
||||||
|
auto inv = std::make_shared<Inventory>(0, 0);
|
||||||
|
inv->deserialize(map.get());
|
||||||
|
inventories[index] = inv;
|
||||||
|
}
|
||||||
|
return inventories;
|
||||||
|
}
|
||||||
|
|
||||||
void WorldRegions::put(Chunk* chunk, std::vector<ubyte> entitiesData) {
|
void WorldRegions::put(Chunk* chunk, std::vector<ubyte> entitiesData) {
|
||||||
assert(chunk != nullptr);
|
assert(chunk != nullptr);
|
||||||
if (!chunk->flags.lighted) {
|
if (!chunk->flags.lighted) {
|
||||||
@ -150,7 +165,7 @@ void WorldRegions::put(Chunk* chunk, std::vector<ubyte> entitiesData) {
|
|||||||
// Writing block inventories
|
// Writing block inventories
|
||||||
if (!chunk->inventories.empty()) {
|
if (!chunk->inventories.empty()) {
|
||||||
uint datasize;
|
uint datasize;
|
||||||
auto data = write_inventories(chunk, datasize);
|
auto data = write_inventories(chunk->inventories, datasize);
|
||||||
put(chunk->x,
|
put(chunk->x,
|
||||||
chunk->z,
|
chunk->z,
|
||||||
REGION_LAYER_INVENTORIES,
|
REGION_LAYER_INVENTORIES,
|
||||||
@ -195,24 +210,25 @@ std::unique_ptr<light_t[]> WorldRegions::getLights(int x, int z) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
chunk_inventories_map WorldRegions::fetchInventories(int x, int z) {
|
chunk_inventories_map WorldRegions::fetchInventories(int x, int z) {
|
||||||
chunk_inventories_map meta;
|
|
||||||
uint32_t bytesSize;
|
uint32_t bytesSize;
|
||||||
auto bytes = layers[REGION_LAYER_INVENTORIES].getData(x, z, bytesSize);
|
auto bytes = layers[REGION_LAYER_INVENTORIES].getData(x, z, bytesSize);
|
||||||
if (bytes == nullptr) {
|
if (bytes == nullptr) {
|
||||||
return meta;
|
return {};
|
||||||
}
|
}
|
||||||
ByteReader reader(bytes, bytesSize);
|
return load_inventories(bytes, bytesSize);
|
||||||
auto count = reader.getInt32();
|
}
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
uint index = reader.getInt32();
|
void WorldRegions::processInventories(
|
||||||
uint size = reader.getInt32();
|
int x, int z, const inventoryproc& func
|
||||||
auto map = json::from_binary(reader.pointer(), size);
|
) {
|
||||||
reader.skip(size);
|
processRegion(x, z, REGION_LAYER_INVENTORIES, 0,
|
||||||
auto inv = std::make_shared<Inventory>(0, 0);
|
[=](std::unique_ptr<ubyte[]> data, uint32_t* size) {
|
||||||
inv->deserialize(map.get());
|
auto inventories = load_inventories(data.get(), *size);
|
||||||
meta[index] = inv;
|
for (const auto& [_, inventory] : inventories) {
|
||||||
}
|
func(inventory.get());
|
||||||
return meta;
|
}
|
||||||
|
return write_inventories(inventories, *size);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic::Map_sptr WorldRegions::fetchEntities(int x, int z) {
|
dynamic::Map_sptr WorldRegions::fetchEntities(int x, int z) {
|
||||||
@ -231,8 +247,10 @@ dynamic::Map_sptr WorldRegions::fetchEntities(int x, int z) {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRegions::processRegionVoxels(int x, int z, const regionproc& func) {
|
void WorldRegions::processRegion(
|
||||||
auto& layer = layers[REGION_LAYER_VOXELS];
|
int x, int z, RegionLayerIndex layerid, uint32_t dataLen, const regionproc& func
|
||||||
|
) {
|
||||||
|
auto& layer = layers[layerid];
|
||||||
if (layer.getRegion(x, z)) {
|
if (layer.getRegion(x, z)) {
|
||||||
throw std::runtime_error("not implemented for in-memory regions");
|
throw std::runtime_error("not implemented for in-memory regions");
|
||||||
}
|
}
|
||||||
@ -250,25 +268,29 @@ void WorldRegions::processRegionVoxels(int x, int z, const regionproc& func) {
|
|||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
data = compression::decompress(
|
uint32_t totalLength = dataLen;
|
||||||
data.get(), length, CHUNK_DATA_LEN, layer.compression
|
if (layer.compression != compression::Method::NONE) {
|
||||||
);
|
if (dataLen == 0) {
|
||||||
if (func(data.get())) {
|
throw std::invalid_argument("invalid data length");
|
||||||
put(gx,
|
}
|
||||||
gz,
|
data = compression::decompress(
|
||||||
REGION_LAYER_VOXELS,
|
data.get(), length, dataLen, layer.compression
|
||||||
std::move(data),
|
);
|
||||||
CHUNK_DATA_LEN);
|
} else {
|
||||||
|
totalLength = length;
|
||||||
|
}
|
||||||
|
if (auto writeData = func(std::move(data), &totalLength)) {
|
||||||
|
put(gx, gz, layerid, std::move(writeData), totalLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path WorldRegions::getRegionsFolder(int layer) const {
|
const fs::path& WorldRegions::getRegionsFolder(RegionLayerIndex layerid) const {
|
||||||
return layers[layer].folder;
|
return layers[layerid].folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRegions::write() {
|
void WorldRegions::writeAll() {
|
||||||
for (auto& layer : layers) {
|
for (auto& layer : layers) {
|
||||||
fs::create_directories(layer.folder);
|
fs::create_directories(layer.folder);
|
||||||
layer.writeAll();
|
layer.writeAll();
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
#include "maths/voxmaths.hpp"
|
#include "maths/voxmaths.hpp"
|
||||||
#include "coders/compression.hpp"
|
#include "coders/compression.hpp"
|
||||||
#include "files.hpp"
|
#include "files.hpp"
|
||||||
|
#include "world_regions_fwd.hpp"
|
||||||
|
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
#include <glm/gtx/hash.hpp>
|
#include <glm/gtx/hash.hpp>
|
||||||
@ -24,15 +25,6 @@ namespace fs = std::filesystem;
|
|||||||
|
|
||||||
inline constexpr uint REGION_HEADER_SIZE = 10;
|
inline constexpr uint REGION_HEADER_SIZE = 10;
|
||||||
|
|
||||||
enum RegionLayerIndex : uint {
|
|
||||||
REGION_LAYER_VOXELS = 0,
|
|
||||||
REGION_LAYER_LIGHTS,
|
|
||||||
REGION_LAYER_INVENTORIES,
|
|
||||||
REGION_LAYER_ENTITIES,
|
|
||||||
|
|
||||||
REGION_LAYERS_COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr uint REGION_SIZE_BIT = 5;
|
inline constexpr uint REGION_SIZE_BIT = 5;
|
||||||
inline constexpr uint REGION_SIZE = (1 << (REGION_SIZE_BIT));
|
inline constexpr uint REGION_SIZE = (1 << (REGION_SIZE_BIT));
|
||||||
inline constexpr uint REGION_CHUNKS_COUNT = ((REGION_SIZE) * (REGION_SIZE));
|
inline constexpr uint REGION_CHUNKS_COUNT = ((REGION_SIZE) * (REGION_SIZE));
|
||||||
@ -75,8 +67,10 @@ struct regfile {
|
|||||||
};
|
};
|
||||||
|
|
||||||
using regionsmap = std::unordered_map<glm::ivec2, std::unique_ptr<WorldRegion>>;
|
using regionsmap = std::unordered_map<glm::ivec2, std::unique_ptr<WorldRegion>>;
|
||||||
using regionproc = std::function<bool(ubyte*)>;
|
using regionproc = std::function<std::unique_ptr<ubyte[]>(std::unique_ptr<ubyte[]>,uint32_t*)>;
|
||||||
|
using inventoryproc = std::function<void(Inventory*)>;
|
||||||
|
|
||||||
|
/// @brief Region file pointer keeping inUse flag on until destroyed
|
||||||
class regfile_ptr {
|
class regfile_ptr {
|
||||||
regfile* file;
|
regfile* file;
|
||||||
std::condition_variable* cv;
|
std::condition_variable* cv;
|
||||||
@ -209,6 +203,10 @@ public:
|
|||||||
size_t size
|
size_t size
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// @brief Get chunk voxels data
|
||||||
|
/// @param x chunk.x
|
||||||
|
/// @param z chunk.z
|
||||||
|
/// @return voxels data buffer or nullptr
|
||||||
std::unique_ptr<ubyte[]> getVoxels(int x, int z);
|
std::unique_ptr<ubyte[]> getVoxels(int x, int z);
|
||||||
|
|
||||||
/// @brief Get cached lights for chunk at x,z
|
/// @brief Get cached lights for chunk at x,z
|
||||||
@ -217,13 +215,30 @@ public:
|
|||||||
|
|
||||||
chunk_inventories_map fetchInventories(int x, int z);
|
chunk_inventories_map fetchInventories(int x, int z);
|
||||||
|
|
||||||
|
/// @brief Load saved entities data for chunk
|
||||||
|
/// @param x chunk.x
|
||||||
|
/// @param z chunk.z
|
||||||
|
/// @return map with entities list as "data"
|
||||||
dynamic::Map_sptr fetchEntities(int x, int z);
|
dynamic::Map_sptr fetchEntities(int x, int z);
|
||||||
|
|
||||||
void processRegionVoxels(int x, int z, const regionproc& func);
|
/// @brief Load, process and save processed region chunks data
|
||||||
|
/// @param x region X
|
||||||
|
/// @param z region Z
|
||||||
|
/// @param layerid regions layer index
|
||||||
|
/// @param func processing callback
|
||||||
|
void processRegion(
|
||||||
|
int x, int z, RegionLayerIndex layerid, uint32_t dataLen, const regionproc& func);
|
||||||
|
|
||||||
fs::path getRegionsFolder(int layer) const;
|
void processInventories(
|
||||||
|
int x, int z, const inventoryproc& func);
|
||||||
|
|
||||||
void write();
|
/// @brief Get regions directory by layer index
|
||||||
|
/// @param layerid layer index
|
||||||
|
/// @return directory path
|
||||||
|
const fs::path& getRegionsFolder(RegionLayerIndex layerid) const;
|
||||||
|
|
||||||
|
/// @brief Write all region layers
|
||||||
|
void writeAll();
|
||||||
|
|
||||||
/// @brief Extract X and Z from 'X_Z.bin' region file name.
|
/// @brief Extract X and Z from 'X_Z.bin' region file name.
|
||||||
/// @param name source region file name
|
/// @param name source region file name
|
||||||
|
|||||||
12
src/files/world_regions_fwd.hpp
Normal file
12
src/files/world_regions_fwd.hpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "typedefs.hpp"
|
||||||
|
|
||||||
|
enum RegionLayerIndex : uint {
|
||||||
|
REGION_LAYER_VOXELS = 0,
|
||||||
|
REGION_LAYER_LIGHTS,
|
||||||
|
REGION_LAYER_INVENTORIES,
|
||||||
|
REGION_LAYER_ENTITIES,
|
||||||
|
|
||||||
|
REGION_LAYERS_COUNT
|
||||||
|
};
|
||||||
@ -81,6 +81,15 @@ std::unique_ptr<dynamic::Map> Inventory::serialize() const {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Inventory::convert(const ContentReport* report) {
|
||||||
|
for (auto& slot : slots) {
|
||||||
|
itemid_t id = slot.getItemId();
|
||||||
|
itemid_t replacement = report->items.getId(id);
|
||||||
|
slot.set(ItemStack(replacement, slot.getCount()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove
|
||||||
void Inventory::convert(dynamic::Map* data, const ContentReport* report) {
|
void Inventory::convert(dynamic::Map* data, const ContentReport* report) {
|
||||||
auto slotsarr = data->list("slots");
|
auto slotsarr = data->list("slots");
|
||||||
for (size_t i = 0; i < slotsarr->size(); i++) {
|
for (size_t i = 0; i < slotsarr->size(); i++) {
|
||||||
|
|||||||
@ -42,6 +42,7 @@ public:
|
|||||||
/* serializing inventory */
|
/* serializing inventory */
|
||||||
std::unique_ptr<dynamic::Map> serialize() const override;
|
std::unique_ptr<dynamic::Map> serialize() const override;
|
||||||
|
|
||||||
|
void convert(const ContentReport* report);
|
||||||
static void convert(dynamic::Map* data, const ContentReport* report);
|
static void convert(dynamic::Map* data, const ContentReport* report);
|
||||||
|
|
||||||
inline void setId(int64_t id) {
|
inline void setId(int64_t id) {
|
||||||
|
|||||||
@ -16,6 +16,9 @@ void ItemStack::set(const ItemStack& item) {
|
|||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
this->item = 0;
|
this->item = 0;
|
||||||
}
|
}
|
||||||
|
if (this->item == 0) {
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ItemStack::accepts(const ItemStack& other) const {
|
bool ItemStack::accepts(const ItemStack& other) const {
|
||||||
|
|||||||
@ -59,6 +59,7 @@ std::shared_ptr<Task> create_converter(
|
|||||||
menu->setPage("main", false);
|
menu->setPage("main", false);
|
||||||
engine->getGUI()->postRunnable([=]() { postRunnable(); });
|
engine->getGUI()->postRunnable([=]() { postRunnable(); });
|
||||||
},
|
},
|
||||||
|
report->isUpgradeRequired(),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user