diff --git a/src/data/StructLayout.cpp b/src/data/StructLayout.cpp index 01a89d2d..9124b68a 100644 --- a/src/data/StructLayout.cpp +++ b/src/data/StructLayout.cpp @@ -313,7 +313,6 @@ number_t StructLayout::getNumber( default: throw std::runtime_error("type error"); } - throw std::runtime_error("type error"); } std::string_view StructLayout::getChars( diff --git a/src/files/RegionsLayer.cpp b/src/files/RegionsLayer.cpp new file mode 100644 index 00000000..2fc405de --- /dev/null +++ b/src/files/RegionsLayer.cpp @@ -0,0 +1,229 @@ +#include "WorldRegions.hpp" + +#include + +#include "util/data_io.hpp" + +#define REGION_FORMAT_MAGIC ".VOXREG" + +static fs::path get_region_filename(int x, int z) { + return fs::path(std::to_string(x) + "_" + std::to_string(z) + ".bin"); +} + +/// @brief Read missing chunks data (null pointers) from region file +static void fetchChunks(WorldRegion* region, int x, int z, regfile* file) { + auto* chunks = region->getChunks(); + uint32_t* sizes = region->getSizes(); + + for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { + int chunk_x = (i % REGION_SIZE) + x * REGION_SIZE; + int chunk_z = (i / REGION_SIZE) + z * REGION_SIZE; + if (chunks[i] == nullptr) { + chunks[i] = + RegionsLayer::readChunkData(chunk_x, chunk_z, sizes[i], file); + } + } +} + +regfile::regfile(fs::path filename) : file(std::move(filename)) { + if (file.length() < REGION_HEADER_SIZE) + throw std::runtime_error("incomplete region file header"); + char header[REGION_HEADER_SIZE]; + file.read(header, REGION_HEADER_SIZE); + + // avoid of use strcmp_s + if (std::string(header, std::strlen(REGION_FORMAT_MAGIC)) != + REGION_FORMAT_MAGIC) { + throw std::runtime_error("invalid region file magic number"); + } + version = header[8]; + if (static_cast(version) > REGION_FORMAT_VERSION) { + throw illegal_region_format( + "region format " + std::to_string(version) + " is not supported" + ); + } +} + +std::unique_ptr regfile::read(int index, uint32_t& length) { + size_t file_size = file.length(); + size_t table_offset = file_size - REGION_CHUNKS_COUNT * 4; + + uint32_t offset; + file.seekg(table_offset + index * 4); + file.read(reinterpret_cast(&offset), 4); + offset = dataio::read_int32_big(reinterpret_cast(&offset), 0); + if (offset == 0) { + return nullptr; + } + + file.seekg(offset); + file.read(reinterpret_cast(&offset), 4); + length = dataio::read_int32_big(reinterpret_cast(&offset), 0); + auto data = std::make_unique(length); + file.read(reinterpret_cast(data.get()), length); + return data; +} + + +void RegionsLayer::closeRegFile(glm::ivec2 coord) { + openRegFiles.erase(coord); + regFilesCv.notify_one(); +} + +regfile_ptr RegionsLayer::useRegFile(glm::ivec2 coord) { + auto* file = openRegFiles[coord].get(); + file->inUse = true; + return regfile_ptr(file, ®FilesCv); +} + +// Marks regfile as used and unmarks when shared_ptr dies +regfile_ptr RegionsLayer::getRegFile(glm::ivec2 coord, bool create) { + { + std::lock_guard lock(regFilesMutex); + const auto found = openRegFiles.find(coord); + if (found != openRegFiles.end()) { + if (found->second->inUse) { + throw std::runtime_error("regfile is currently in use"); + } + return useRegFile(found->first); + } + } + if (create) { + return createRegFile(coord); + } + return nullptr; +} + +regfile_ptr RegionsLayer::createRegFile(glm::ivec2 coord) { + auto file = folder / get_region_filename(coord[0], coord[1]); + if (!fs::exists(file)) { + return nullptr; + } + if (openRegFiles.size() == MAX_OPEN_REGION_FILES) { + std::unique_lock lock(regFilesMutex); + while (true) { + bool closed = false; + // FIXME: bad choosing algorithm + for (auto& entry : openRegFiles) { + if (!entry.second->inUse) { + closeRegFile(entry.first); + closed = true; + break; + } + } + if (closed) { + break; + } + // notified when any regfile gets out of use or closed + regFilesCv.wait(lock); + } + openRegFiles[coord] = std::make_unique(file); + return useRegFile(coord); + } else { + std::lock_guard lock(regFilesMutex); + openRegFiles[coord] = std::make_unique(file); + return useRegFile(coord); + } +} + +WorldRegion* RegionsLayer::getRegion(int x, int z) { + std::lock_guard lock(mapMutex); + auto found = regions.find({x, z}); + if (found == regions.end()) { + return nullptr; + } + return found->second.get(); +} + +WorldRegion* RegionsLayer::getOrCreateRegion(int x, int z) { + if (auto region = getRegion(x, z)) { + return region; + } + std::lock_guard lock(mapMutex); + auto region_ptr = std::make_unique(); + auto region = region_ptr.get(); + regions[{x, z}] = std::move(region_ptr); + return region; +} + +ubyte* RegionsLayer::getData(int x, int z, uint32_t& size) { + int regionX, regionZ, localX, localZ; + calc_reg_coords(x, z, regionX, regionZ, localX, localZ); + + WorldRegion* region = getOrCreateRegion(regionX, regionZ); + ubyte* data = region->getChunkData(localX, localZ); + if (data == nullptr) { + auto regfile = getRegFile({regionX, regionZ}); + if (regfile != nullptr) { + auto dataptr = readChunkData(x, z, size, regfile.get()); + if (dataptr) { + data = dataptr.get(); + region->put(localX, localZ, std::move(dataptr), size); + } + } + } + if (data != nullptr) { + size = region->getChunkDataSize(localX, localZ); + return data; + } + return nullptr; +} + +void RegionsLayer::writeRegion(int x, int z, WorldRegion* entry) { + fs::path filename = folder / get_region_filename(x, z); + + glm::ivec2 regcoord(x, z); + if (auto regfile = getRegFile(regcoord, false)) { + fetchChunks(entry, x, z, regfile.get()); + + std::lock_guard lock(regFilesMutex); + regfile.reset(); + closeRegFile(regcoord); + } + + char header[REGION_HEADER_SIZE] = REGION_FORMAT_MAGIC; + header[8] = REGION_FORMAT_VERSION; + header[9] = 0; // flags + std::ofstream file(filename, std::ios::out | std::ios::binary); + file.write(header, REGION_HEADER_SIZE); + + size_t offset = REGION_HEADER_SIZE; + char intbuf[4] {}; + uint offsets[REGION_CHUNKS_COUNT] {}; + + auto* region = entry->getChunks(); + uint32_t* sizes = entry->getSizes(); + + for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { + ubyte* chunk = region[i].get(); + if (chunk == nullptr) { + offsets[i] = 0; + } else { + offsets[i] = offset; + + size_t compressedSize = sizes[i]; + dataio::write_int32_big( + compressedSize, reinterpret_cast(intbuf), 0 + ); + offset += 4 + compressedSize; + + file.write(intbuf, 4); + file.write(reinterpret_cast(chunk), compressedSize); + } + } + for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { + dataio::write_int32_big( + offsets[i], reinterpret_cast(intbuf), 0 + ); + file.write(intbuf, 4); + } +} + +std::unique_ptr RegionsLayer::readChunkData( + int x, int z, uint32_t& length, regfile* rfile +) { + int regionX, regionZ, localX, localZ; + calc_reg_coords(x, z, regionX, regionZ, localX, localZ); + int chunkIndex = localZ * REGION_SIZE + localX; + return rfile->read(chunkIndex, length); +} diff --git a/src/files/WorldFiles.cpp b/src/files/WorldFiles.cpp index 562c40c0..33ebaf37 100644 --- a/src/files/WorldFiles.cpp +++ b/src/files/WorldFiles.cpp @@ -84,7 +84,6 @@ void WorldFiles::write(const World* world, const Content* content) { if (generatorTestMode) { return; } - writeIndices(content->getIndices()); regions.write(); } diff --git a/src/files/WorldFiles.hpp b/src/files/WorldFiles.hpp index e0e78920..cb9c4b45 100644 --- a/src/files/WorldFiles.hpp +++ b/src/files/WorldFiles.hpp @@ -63,8 +63,6 @@ public: /// @return world folder fs::path getFolder() const; - static const inline std::string WORLD_FILE = "world.json"; - WorldRegions& getRegions() { return regions; } @@ -72,4 +70,6 @@ public: bool doesWriteLights() const { return doWriteLights; } + + static const inline std::string WORLD_FILE = "world.json"; }; diff --git a/src/files/WorldRegions.cpp b/src/files/WorldRegions.cpp index d47d64c9..9074c69e 100644 --- a/src/files/WorldRegions.cpp +++ b/src/files/WorldRegions.cpp @@ -11,47 +11,6 @@ #include "maths/voxmaths.hpp" #include "util/data_io.hpp" -#define REGION_FORMAT_MAGIC ".VOXREG" - -regfile::regfile(fs::path filename) : file(std::move(filename)) { - if (file.length() < REGION_HEADER_SIZE) - throw std::runtime_error("incomplete region file header"); - char header[REGION_HEADER_SIZE]; - file.read(header, REGION_HEADER_SIZE); - - // avoid of use strcmp_s - if (std::string(header, std::strlen(REGION_FORMAT_MAGIC)) != - REGION_FORMAT_MAGIC) { - throw std::runtime_error("invalid region file magic number"); - } - version = header[8]; - if (static_cast(version) > REGION_FORMAT_VERSION) { - throw illegal_region_format( - "region format " + std::to_string(version) + " is not supported" - ); - } -} - -std::unique_ptr regfile::read(int index, uint32_t& length) { - size_t file_size = file.length(); - size_t table_offset = file_size - REGION_CHUNKS_COUNT * 4; - - uint32_t offset; - file.seekg(table_offset + index * 4); - file.read(reinterpret_cast(&offset), 4); - offset = dataio::read_int32_big(reinterpret_cast(&offset), 0); - if (offset == 0) { - return nullptr; - } - - file.seekg(offset); - file.read(reinterpret_cast(&offset), 4); - length = dataio::read_int32_big(reinterpret_cast(&offset), 0); - auto data = std::make_unique(length); - file.read(reinterpret_cast(data.get()), length); - return data; -} - WorldRegion::WorldRegion() : chunksData( std::make_unique[]>(REGION_CHUNKS_COUNT) @@ -76,9 +35,11 @@ uint32_t* WorldRegion::getSizes() const { return sizes.get(); } -void WorldRegion::put(uint x, uint z, ubyte* data, uint32_t size) { +void WorldRegion::put( + uint x, uint z, std::unique_ptr data, uint32_t size +) { size_t chunk_index = z * REGION_SIZE + x; - chunksData[chunk_index].reset(data); + chunksData[chunk_index] = std::move(data); sizes[chunk_index] = size; } @@ -103,28 +64,6 @@ WorldRegions::WorldRegions(const fs::path& directory) : directory(directory) { WorldRegions::~WorldRegions() = default; -WorldRegion* WorldRegions::getRegion(int x, int z, int layer) { - RegionsLayer& regions = layers[layer]; - std::lock_guard lock(regions.mutex); - auto found = regions.regions.find(glm::ivec2(x, z)); - if (found == regions.regions.end()) { - return nullptr; - } - return found->second.get(); -} - -WorldRegion* WorldRegions::getOrCreateRegion(int x, int z, int layer) { - if (auto region = getRegion(x, z, layer)) { - return region; - } - RegionsLayer& regions = layers[layer]; - std::lock_guard lock(regions.mutex); - auto region_ptr = std::make_unique(); - auto region = region_ptr.get(); - regions.regions[{x, z}] = std::move(region_ptr); - return region; -} - std::unique_ptr WorldRegions::compress( const ubyte* src, size_t srclen, size_t& len ) { @@ -147,189 +86,14 @@ std::unique_ptr WorldRegions::decompress( return decompressed; } -inline void calc_reg_coords( - int x, int z, int& regionX, int& regionZ, int& localX, int& localZ -) { - regionX = floordiv(x, REGION_SIZE); - regionZ = floordiv(z, REGION_SIZE); - localX = x - (regionX * REGION_SIZE); - localZ = z - (regionZ * REGION_SIZE); -} - -std::unique_ptr WorldRegions::readChunkData( - int x, int z, uint32_t& length, regfile* rfile -) { - int regionX, regionZ, localX, localZ; - calc_reg_coords(x, z, regionX, regionZ, localX, localZ); - int chunkIndex = localZ * REGION_SIZE + localX; - return rfile->read(chunkIndex, length); -} - -/// @brief Read missing chunks data (null pointers) from region file -void WorldRegions::fetchChunks( - WorldRegion* region, int x, int z, regfile* file -) { - auto* chunks = region->getChunks(); - uint32_t* sizes = region->getSizes(); - - for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { - int chunk_x = (i % REGION_SIZE) + x * REGION_SIZE; - int chunk_z = (i / REGION_SIZE) + z * REGION_SIZE; - if (chunks[i] == nullptr) { - chunks[i] = readChunkData(chunk_x, chunk_z, sizes[i], file); - } - } -} - -ubyte* WorldRegions::getData(int x, int z, int layer, uint32_t& size) { - if (generatorTestMode) { - return nullptr; - } - int regionX, regionZ, localX, localZ; - calc_reg_coords(x, z, regionX, regionZ, localX, localZ); - - WorldRegion* region = getOrCreateRegion(regionX, regionZ, layer); - ubyte* data = region->getChunkData(localX, localZ); - if (data == nullptr) { - auto regfile = getRegFile(glm::ivec3(regionX, regionZ, layer)); - if (regfile != nullptr) { - data = readChunkData(x, z, size, regfile.get()).release(); - } - if (data != nullptr) { - region->put(localX, localZ, data, size); - } - } - if (data != nullptr) { - size = region->getChunkDataSize(localX, localZ); - return data; - } - return nullptr; -} - -regfile_ptr WorldRegions::useRegFile(glm::ivec3 coord) { - auto* file = openRegFiles[coord].get(); - file->inUse = true; - return regfile_ptr(file, ®FilesCv); -} - -void WorldRegions::closeRegFile(glm::ivec3 coord) { - openRegFiles.erase(coord); - regFilesCv.notify_one(); -} - -// Marks regfile as used and unmarks when shared_ptr dies -regfile_ptr WorldRegions::getRegFile(glm::ivec3 coord, bool create) { - { - std::lock_guard lock(regFilesMutex); - const auto found = openRegFiles.find(coord); - if (found != openRegFiles.end()) { - if (found->second->inUse) { - throw std::runtime_error("regfile is currently in use"); - } - return useRegFile(found->first); - } - } - if (create) { - return createRegFile(coord); - } - return nullptr; -} - -regfile_ptr WorldRegions::createRegFile(glm::ivec3 coord) { - fs::path file = - layers[coord[2]].folder / getRegionFilename(coord[0], coord[1]); - if (!fs::exists(file)) { - return nullptr; - } - if (openRegFiles.size() == MAX_OPEN_REGION_FILES) { - std::unique_lock lock(regFilesMutex); - while (true) { - bool closed = false; - // FIXME: bad choosing algorithm - for (auto& entry : openRegFiles) { - if (!entry.second->inUse) { - closeRegFile(entry.first); - closed = true; - break; - } - } - if (closed) { - break; - } - // notified when any regfile gets out of use or closed - regFilesCv.wait(lock); - } - openRegFiles[coord] = std::make_unique(file); - return useRegFile(coord); - } else { - std::lock_guard lock(regFilesMutex); - openRegFiles[coord] = std::make_unique(file); - return useRegFile(coord); - } -} - -fs::path WorldRegions::getRegionFilename(int x, int z) const { - return fs::path(std::to_string(x) + "_" + std::to_string(z) + ".bin"); -} - -void WorldRegions::writeRegion(int x, int z, int layer, WorldRegion* entry) { - fs::path filename = layers[layer].folder / getRegionFilename(x, z); - - glm::ivec3 regcoord(x, z, layer); - if (auto regfile = getRegFile(regcoord, false)) { - fetchChunks(entry, x, z, regfile.get()); - - std::lock_guard lock(regFilesMutex); - regfile.reset(); - closeRegFile(regcoord); - } - - char header[REGION_HEADER_SIZE] = REGION_FORMAT_MAGIC; - header[8] = REGION_FORMAT_VERSION; - header[9] = 0; // flags - std::ofstream file(filename, std::ios::out | std::ios::binary); - file.write(header, REGION_HEADER_SIZE); - - size_t offset = REGION_HEADER_SIZE; - char intbuf[4] {}; - uint offsets[REGION_CHUNKS_COUNT] {}; - - auto* region = entry->getChunks(); - uint32_t* sizes = entry->getSizes(); - - for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { - ubyte* chunk = region[i].get(); - if (chunk == nullptr) { - offsets[i] = 0; - } else { - offsets[i] = offset; - - size_t compressedSize = sizes[i]; - dataio::write_int32_big( - compressedSize, reinterpret_cast(intbuf), 0 - ); - offset += 4 + compressedSize; - - file.write(intbuf, 4); - file.write(reinterpret_cast(chunk), compressedSize); - } - } - for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { - dataio::write_int32_big( - offsets[i], reinterpret_cast(intbuf), 0 - ); - file.write(intbuf, 4); - } -} - -void WorldRegions::writeRegions(int layer) { - for (auto& it : layers[layer].regions) { +void RegionsLayer::writeAll() { + for (auto& it : regions) { WorldRegion* region = it.second.get(); if (region->getChunks() == nullptr || !region->isUnsaved()) { continue; } - glm::ivec2 key = it.first; - writeRegion(key[0], key[1], layer, region); + const auto& key = it.first; + writeRegion(key[0], key[1], region); } } @@ -350,9 +114,9 @@ void WorldRegions::put( int regionX, regionZ, localX, localZ; calc_reg_coords(x, z, regionX, regionZ, localX, localZ); - WorldRegion* region = getOrCreateRegion(regionX, regionZ, layer); + WorldRegion* region = layers[layer].getOrCreateRegion(regionX, regionZ); region->setUnsaved(true); - region->put(localX, localZ, data.release(), size); + region->put(localX, localZ, std::move(data), size); } static std::unique_ptr write_inventories( @@ -431,9 +195,9 @@ void WorldRegions::put(Chunk* chunk, std::vector entitiesData) { } } -std::unique_ptr WorldRegions::getChunk(int x, int z) { +std::unique_ptr WorldRegions::getVoxels(int x, int z) { uint32_t size; - auto* data = getData(x, z, REGION_LAYER_VOXELS, size); + auto* data = layers[REGION_LAYER_VOXELS].getData(x, z, size); if (data == nullptr) { return nullptr; } @@ -444,7 +208,7 @@ std::unique_ptr WorldRegions::getChunk(int x, int z) { /// @return lights data or nullptr std::unique_ptr WorldRegions::getLights(int x, int z) { uint32_t size; - auto* bytes = getData(x, z, REGION_LAYER_LIGHTS, size); + auto* bytes = layers[REGION_LAYER_LIGHTS].getData(x, z, size); if (bytes == nullptr) { return nullptr; } @@ -455,11 +219,11 @@ std::unique_ptr WorldRegions::getLights(int x, int z) { chunk_inventories_map WorldRegions::fetchInventories(int x, int z) { chunk_inventories_map meta; uint32_t bytesSize; - const ubyte* data = getData(x, z, REGION_LAYER_INVENTORIES, bytesSize); - if (data == nullptr) { + auto bytes = layers[REGION_LAYER_INVENTORIES].getData(x, z, bytesSize); + if (bytes == nullptr) { return meta; } - ByteReader reader(data, bytesSize); + ByteReader reader(bytes, bytesSize); auto count = reader.getInt32(); for (int i = 0; i < count; i++) { uint index = reader.getInt32(); @@ -474,8 +238,11 @@ chunk_inventories_map WorldRegions::fetchInventories(int x, int z) { } dynamic::Map_sptr WorldRegions::fetchEntities(int x, int z) { + if (generatorTestMode) { + return nullptr; + } uint32_t bytesSize; - const ubyte* data = getData(x, z, REGION_LAYER_ENTITIES, bytesSize); + const ubyte* data = layers[REGION_LAYER_ENTITIES].getData(x, z, bytesSize); if (data == nullptr) { return nullptr; } @@ -487,10 +254,10 @@ dynamic::Map_sptr WorldRegions::fetchEntities(int x, int z) { } void WorldRegions::processRegionVoxels(int x, int z, const regionproc& func) { - if (getRegion(x, z, REGION_LAYER_VOXELS)) { + if (layers[REGION_LAYER_VOXELS].getRegion(x, z)) { throw std::runtime_error("not implemented for in-memory regions"); } - auto regfile = getRegFile(glm::ivec3(x, z, REGION_LAYER_VOXELS)); + auto regfile = layers[REGION_LAYER_VOXELS].getRegFile({x, z}); if (regfile == nullptr) { throw std::runtime_error("could not open region file"); } @@ -499,7 +266,8 @@ void WorldRegions::processRegionVoxels(int x, int z, const regionproc& func) { int gx = cx + x * REGION_SIZE; int gz = cz + z * REGION_SIZE; uint32_t length; - auto data = readChunkData(gx, gz, length, regfile.get()); + auto data = + RegionsLayer::readChunkData(gx, gz, length, regfile.get()); if (data == nullptr) { continue; } @@ -523,7 +291,7 @@ fs::path WorldRegions::getRegionsFolder(int layer) const { void WorldRegions::write() { for (auto& layer : layers) { fs::create_directories(layer.folder); - writeRegions(layer.layer); + layer.writeAll(); } } diff --git a/src/files/WorldRegions.hpp b/src/files/WorldRegions.hpp index e346dddc..e467b798 100644 --- a/src/files/WorldRegions.hpp +++ b/src/files/WorldRegions.hpp @@ -12,6 +12,7 @@ #include "typedefs.hpp" #include "util/BufferPool.hpp" #include "voxels/Chunk.hpp" +#include "maths/voxmaths.hpp" #include "files.hpp" #define GLM_ENABLE_EXPERIMENTAL @@ -30,7 +31,7 @@ inline constexpr uint REGION_SIZE_BIT = 5; inline constexpr uint REGION_SIZE = (1 << (REGION_SIZE_BIT)); inline constexpr uint REGION_CHUNKS_COUNT = ((REGION_SIZE) * (REGION_SIZE)); inline constexpr uint REGION_FORMAT_VERSION = 2; -inline constexpr uint MAX_OPEN_REGION_FILES = 16; +inline constexpr uint MAX_OPEN_REGION_FILES = 32; class illegal_region_format : public std::runtime_error { public: @@ -47,7 +48,7 @@ public: WorldRegion(); ~WorldRegion(); - void put(uint x, uint z, ubyte* data, uint32_t size); + void put(uint x, uint z, std::unique_ptr data, uint32_t size); ubyte* getChunkData(uint x, uint z); uint getChunkDataSize(uint x, uint z); @@ -72,13 +73,6 @@ struct regfile { using regionsmap = std::unordered_map>; using regionproc = std::function; -struct RegionsLayer { - int layer; - fs::path folder; - regionsmap regions; - std::mutex mutex; -}; - class regfile_ptr { regfile* file; std::condition_variable* cv; @@ -116,18 +110,77 @@ public: } }; -class WorldRegions { - fs::path directory; - std::unordered_map> openRegFiles; +inline void calc_reg_coords( + int x, int z, int& regionX, int& regionZ, int& localX, int& localZ +) { + regionX = floordiv(x, REGION_SIZE); + regionZ = floordiv(z, REGION_SIZE); + localX = x - (regionX * REGION_SIZE); + localZ = z - (regionZ * REGION_SIZE); +} + +struct RegionsLayer { + /// @brief Layer index + int layer; + + /// @brief Regions layer folder + fs::path folder; + + /// @brief In-memory regions data + regionsmap regions; + + /// @brief In-memory regions map mutex + std::mutex mapMutex; + + /// @brief Open region files map + std::unordered_map> openRegFiles; + + /// @brief Open region files map mutex std::mutex regFilesMutex; std::condition_variable regFilesCv; + + regfile_ptr getRegFile(glm::ivec2 coord, bool create = true); + [[nodiscard]] regfile_ptr useRegFile(glm::ivec2 coord); + regfile_ptr createRegFile(glm::ivec2 coord); + void closeRegFile(glm::ivec2 coord); + + WorldRegion* getRegion(int x, int z); + WorldRegion* getOrCreateRegion(int x, int z); + + /// @brief Get chunk data. Read from file if not loaded yet. + /// @param x chunk x coord + /// @param z chunk z coord + /// @param size [out] chunk data length + /// @return nullptr if no saved chunk data found + [[nodiscard]] ubyte* getData(int x, int z, uint32_t& size); + + /// @brief Write or rewrite region file + /// @param x region X + /// @param z region Z + void writeRegion(int x, int y, WorldRegion* entry); + + /// @brief Write all unsaved regions to files + void writeAll(); + + /// @brief Read chunk data from region file + /// @param x chunk x coord + /// @param z chunk z coord + /// @param length [out] chunk data length + /// @param rfile region file + /// @return nullptr if chunk is not present in region file + [[nodiscard]] static std::unique_ptr readChunkData( + int x, int z, uint32_t& length, regfile* rfile + ); +}; + +class WorldRegions { + /// @brief World directory + fs::path directory; + RegionsLayer layers[4] {}; util::BufferPool bufferPool { std::max(CHUNK_DATA_LEN, LIGHTMAP_DATA_LEN) * 2}; - WorldRegion* getRegion(int x, int z, int layer); - WorldRegion* getOrCreateRegion(int x, int z, int layer); - /// @brief Compress buffer with extrle /// @param src source buffer /// @param srclen length of the source buffer @@ -145,29 +198,6 @@ class WorldRegions { std::unique_ptr decompress( const ubyte* src, size_t srclen, size_t dstlen ); - - std::unique_ptr readChunkData( - int x, int y, uint32_t& length, regfile* file - ); - - void fetchChunks(WorldRegion* region, int x, int y, regfile* file); - - ubyte* getData(int x, int z, int layer, uint32_t& size); - - regfile_ptr getRegFile(glm::ivec3 coord, bool create = true); - void closeRegFile(glm::ivec3 coord); - regfile_ptr useRegFile(glm::ivec3 coord); - regfile_ptr createRegFile(glm::ivec3 coord); - - fs::path getRegionFilename(int x, int y) const; - - void writeRegions(int layer); - - /// @brief Write or rewrite region file - /// @param x region X - /// @param z region Z - /// @param layer regions layer - void writeRegion(int x, int y, int layer, WorldRegion* entry); public: bool generatorTestMode = false; bool doWriteLights = true; @@ -195,7 +225,7 @@ public: bool rle ); - std::unique_ptr getChunk(int x, int z); + std::unique_ptr getVoxels(int x, int z); std::unique_ptr getLights(int x, int z); chunk_inventories_map fetchInventories(int x, int z); dynamic::Map_sptr fetchEntities(int x, int z); diff --git a/src/voxels/ChunksStorage.cpp b/src/voxels/ChunksStorage.cpp index 9bc0a4f6..23665b61 100644 --- a/src/voxels/ChunksStorage.cpp +++ b/src/voxels/ChunksStorage.cpp @@ -59,7 +59,7 @@ std::shared_ptr ChunksStorage::create(int x, int z) { auto chunk = std::make_shared(x, z); store(chunk); - auto data = regions.getChunk(chunk->x, chunk->z); + auto data = regions.getVoxels(chunk->x, chunk->z); if (data) { chunk->decode(data.get());