update WorldConverter (WIP)

This commit is contained in:
MihailRis 2024-09-02 23:24:59 +03:00
parent 3f826a88d3
commit 728795f0f3
12 changed files with 299 additions and 97 deletions

View File

@ -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();

View File

@ -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;
union {
ContentType contentType; 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;

View File

@ -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;
return;
} }
logger.info() << "converting region " << name;
wfile->getRegions().processRegionVoxels(x, z, [=](ubyte* data) { void WorldConverter::upgradeVoxels(const fs::path& file, int x, int z) const {
if (report) { throw std::runtime_error("unsupported region format");
Chunk::convert(data, report.get());
} }
return true;
void WorldConverter::convertVoxels(const fs::path& file, int x, int z) const {
logger.info() << "converting voxels region " << x << "_" << z;
wfile->getRegions().processRegion(x, z, REGION_LAYER_VOXELS, CHUNK_DATA_LEN,
[=](std::unique_ptr<ubyte[]> data, uint32_t*) {
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() {

View File

@ -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
); );
}; };

View File

@ -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;
} }
if (content) {
writeIndices(content->getIndices()); writeIndices(content->getIndices());
regions.write(); }
regions.writeAll();
} }
void WorldFiles::writePacks(const std::vector<ContentPack>& packs) { void WorldFiles::writePacks(const std::vector<ContentPack>& packs) {

View File

@ -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();
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());
meta[index] = inv;
} }
return meta;
void WorldRegions::processInventories(
int x, int z, const inventoryproc& func
) {
processRegion(x, z, REGION_LAYER_INVENTORIES, 0,
[=](std::unique_ptr<ubyte[]> data, uint32_t* size) {
auto inventories = load_inventories(data.get(), *size);
for (const auto& [_, inventory] : inventories) {
func(inventory.get());
}
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;
} }
uint32_t totalLength = dataLen;
if (layer.compression != compression::Method::NONE) {
if (dataLen == 0) {
throw std::invalid_argument("invalid data length");
}
data = compression::decompress( data = compression::decompress(
data.get(), length, CHUNK_DATA_LEN, layer.compression data.get(), length, dataLen, layer.compression
); );
if (func(data.get())) { } else {
put(gx, totalLength = length;
gz, }
REGION_LAYER_VOXELS, if (auto writeData = func(std::move(data), &totalLength)) {
std::move(data), put(gx, gz, layerid, std::move(writeData), totalLength);
CHUNK_DATA_LEN);
} }
} }
} }
} }
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();

View File

@ -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

View 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
};

View File

@ -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++) {

View File

@ -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) {

View File

@ -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 {

View File

@ -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
); );
} }