synchronized regfiles

This commit is contained in:
MihailRis
2024-04-14 03:38:46 +03:00
parent 7c4c511826
commit b6900f5d4e
2 changed files with 100 additions and 71 deletions
+91 -67
View File
@@ -144,29 +144,13 @@ ubyte* WorldFiles::decompress(const ubyte* src, size_t srclen, size_t dstlen) {
return decompressed; return decompressed;
} }
int WorldFiles::getVoxelRegionVersion(int x, int z) { inline void calc_reg_coords(
regfile* rf = getRegFile(glm::ivec3(x, z, REGION_LAYER_VOXELS), getRegionsFolder()); int x, int z, int& regionX, int& regionZ, int& localX, int& localZ
if (rf == nullptr) { ) {
return 0; regionX = floordiv(x, REGION_SIZE);
} regionZ = floordiv(z, REGION_SIZE);
return rf->version; localX = x - (regionX * REGION_SIZE);
} localZ = z - (regionZ * REGION_SIZE);
int WorldFiles::getVoxelRegionsVersion() {
fs::path regionsFolder = getRegionsFolder();
if (!fs::is_directory(regionsFolder)) {
return REGION_FORMAT_VERSION;
}
for (auto file : fs::directory_iterator(regionsFolder)) {
int x;
int z;
if (!parseRegionFilename(file.path().stem().string(), x, z)) {
continue;
}
regfile* rf = getRegFile(glm::ivec3(x, z, REGION_LAYER_VOXELS), regionsFolder);
return rf->version;
}
return REGION_FORMAT_VERSION;
} }
@@ -174,10 +158,8 @@ int WorldFiles::getVoxelRegionsVersion() {
/// @param x chunk.x /// @param x chunk.x
/// @param z chunk.z /// @param z chunk.z
void WorldFiles::put(int x, int z, const ubyte* voxelData) { void WorldFiles::put(int x, int z, const ubyte* voxelData) {
int regionX = floordiv(x, REGION_SIZE); int regionX, regionZ, localX, localZ;
int regionZ = floordiv(z, REGION_SIZE); calc_reg_coords(x, z, regionX, regionZ, localX, localZ);
int localX = x - (regionX * REGION_SIZE);
int localZ = z - (regionZ * REGION_SIZE);
/* Writing Voxels */ { /* Writing Voxels */ {
WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ); WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ);
@@ -192,10 +174,8 @@ void WorldFiles::put(int x, int z, const ubyte* voxelData) {
void WorldFiles::put(Chunk* chunk){ void WorldFiles::put(Chunk* chunk){
assert(chunk != nullptr); assert(chunk != nullptr);
int regionX = floordiv(chunk->x, REGION_SIZE); int regionX, regionZ, localX, localZ;
int regionZ = floordiv(chunk->z, REGION_SIZE); calc_reg_coords(chunk->x, chunk->z, regionX, regionZ, localX, localZ);
int localX = chunk->x - (regionX * REGION_SIZE);
int localZ = chunk->z - (regionZ * REGION_SIZE);
/* Writing voxels */ { /* Writing voxels */ {
size_t compressedSize; size_t compressedSize;
@@ -325,13 +305,14 @@ chunk_inventories_map WorldFiles::fetchInventories(int x, int z) {
return inventories; return inventories;
} }
ubyte* WorldFiles::getData(regionsmap& regions, const fs::path& folder, ubyte* WorldFiles::getData(
int x, int z, int layer, bool compression) { regionsmap& regions,
int regionX = floordiv(x, REGION_SIZE); const fs::path& folder,
int regionZ = floordiv(z, REGION_SIZE); int x, int z, int layer,
bool compression
int localX = x - (regionX * REGION_SIZE); ) {
int localZ = z - (regionZ * REGION_SIZE); int regionX, regionZ, localX, localZ;
calc_reg_coords(x, z, regionX, regionZ, localX, localZ);
WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ); WorldRegion* region = getOrCreateRegion(regions, regionX, regionZ);
ubyte* data = region->getChunkData(localX, localZ); ubyte* data = region->getChunkData(localX, localZ);
@@ -352,41 +333,82 @@ ubyte* WorldFiles::getData(regionsmap& regions, const fs::path& folder,
return nullptr; return nullptr;
} }
std::shared_ptr<regfile> WorldFiles::useRegFile(glm::ivec3 coord) {
regfile* WorldFiles::getRegFile(glm::ivec3 coord, const fs::path& folder) { return std::shared_ptr<regfile>(openRegFiles[coord].get(), [this](regfile* ptr) {
const auto found = openRegFiles.find(coord); ptr->inUse = false;
if (found != openRegFiles.end()) { regFilesCv.notify_one();
return found->second.get(); });
}
if (openRegFiles.size() == MAX_OPEN_REGION_FILES) {
// [todo] replace with closing the most unused region
auto iter = std::next(openRegFiles.begin(), rand() % openRegFiles.size());
openRegFiles.erase(iter);
}
fs::path filename = folder / getRegionFilename(coord[0], coord[1]);
if (!fs::is_regular_file(filename)) {
return nullptr;
}
openRegFiles[coord] = std::make_unique<regfile>(filename);
return openRegFiles[coord].get();
} }
ubyte* WorldFiles::readChunkData(int x, void WorldFiles::closeRegFile(glm::ivec3 coord) {
int z, openRegFiles.erase(coord);
uint32_t& length, regFilesCv.notify_one();
fs::path folder, }
int layer){
// Marks regfile as used and unmarks when shared_ptr dies
std::shared_ptr<regfile> WorldFiles::getRegFile(glm::ivec3 coord, const fs::path& folder) {
{
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");
}
found->second->inUse = true;
return useRegFile(found->first);
}
}
return createRegFile(coord, folder);
}
std::shared_ptr<regfile> WorldFiles::createRegFile(glm::ivec3 coord, const fs::path& folder) {
fs::path file = 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<regfile>(file);
return useRegFile(coord);
} else {
std::lock_guard lock(regFilesMutex);
openRegFiles[coord] = std::make_unique<regfile>(file);
return useRegFile(coord);
}
}
ubyte* WorldFiles::readChunkData(
int x,
int z,
uint32_t& length,
fs::path folder,
int layer
){
if (generatorTestMode) if (generatorTestMode)
return nullptr; return nullptr;
int regionX = floordiv(x, REGION_SIZE); int regionX, regionZ, localX, localZ;
int regionZ = floordiv(z, REGION_SIZE); calc_reg_coords(x, z, regionX, regionZ, localX, localZ);
int localX = x - (regionX * REGION_SIZE);
int localZ = z - (regionZ * REGION_SIZE);
int chunkIndex = localZ * REGION_SIZE + localX; int chunkIndex = localZ * REGION_SIZE + localX;
glm::ivec3 coord(regionX, regionZ, layer); glm::ivec3 coord(regionX, regionZ, layer);
regfile* rfile = WorldFiles::getRegFile(coord, folder); auto rfile = getRegFile(coord, folder);
if (rfile == nullptr) { if (rfile == nullptr) {
return nullptr; return nullptr;
} }
@@ -399,7 +421,6 @@ ubyte* WorldFiles::readChunkData(int x,
file.seekg(table_offset + chunkIndex * 4); file.seekg(table_offset + chunkIndex * 4);
file.read((char*)(&offset), 4); file.read((char*)(&offset), 4);
offset = dataio::read_int32_big((const ubyte*)(&offset), 0); offset = dataio::read_int32_big((const ubyte*)(&offset), 0);
if (offset == 0){ if (offset == 0){
return nullptr; return nullptr;
} }
@@ -439,7 +460,10 @@ void WorldFiles::writeRegion(int x, int z, WorldRegion* entry, fs::path folder,
glm::ivec3 regcoord(x, z, layer); glm::ivec3 regcoord(x, z, layer);
if (getRegFile(regcoord, folder)) { if (getRegFile(regcoord, folder)) {
fetchChunks(entry, x, z, folder, layer); fetchChunks(entry, x, z, folder, layer);
openRegFiles.erase(regcoord); {
std::lock_guard lock(regFilesMutex);
closeRegFile(regcoord);
}
} }
char header[REGION_HEADER_SIZE] = REGION_FORMAT_MAGIC; char header[REGION_HEADER_SIZE] = REGION_FORMAT_MAGIC;
+9 -4
View File
@@ -8,11 +8,13 @@
#include "../voxels/Chunk.h" #include "../voxels/Chunk.h"
#include <map> #include <map>
#include <mutex>
#include <vector> #include <vector>
#include <string> #include <string>
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
#include <filesystem> #include <filesystem>
#include <condition_variable>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#define GLM_ENABLE_EXPERIMENTAL #define GLM_ENABLE_EXPERIMENTAL
@@ -66,6 +68,7 @@ public:
struct regfile { struct regfile {
files::rafile file; files::rafile file;
int version; int version;
bool inUse = false;
regfile(fs::path filename); regfile(fs::path filename);
}; };
@@ -74,6 +77,8 @@ typedef std::unordered_map<glm::ivec2, std::unique_ptr<WorldRegion>> regionsmap;
class WorldFiles { class WorldFiles {
std::unordered_map<glm::ivec3, std::unique_ptr<regfile>> openRegFiles; std::unordered_map<glm::ivec3, std::unique_ptr<regfile>> openRegFiles;
std::mutex regFilesMutex;
std::condition_variable regFilesCv;
void writeWorldInfo(const World* world); void writeWorldInfo(const World* world);
fs::path getRegionFilename(int x, int y) const; fs::path getRegionFilename(int x, int y) const;
@@ -106,7 +111,10 @@ class WorldFiles {
ubyte* getData(regionsmap& regions, const fs::path& folder, int x, int z, int layer, bool compression); ubyte* getData(regionsmap& regions, const fs::path& folder, int x, int z, int layer, bool compression);
regfile* getRegFile(glm::ivec3 coord, const fs::path& folder); std::shared_ptr<regfile> getRegFile(glm::ivec3 coord, const fs::path& folder);
void closeRegFile(glm::ivec3 coord);
std::shared_ptr<regfile> useRegFile(glm::ivec3 coord);
std::shared_ptr<regfile> createRegFile(glm::ivec3 coord, const fs::path& folder);
fs::path getLightsFolder() const; fs::path getLightsFolder() const;
fs::path getInventoriesFolder() const; fs::path getInventoriesFolder() const;
@@ -131,9 +139,6 @@ public:
void put(Chunk* chunk); void put(Chunk* chunk);
void put(int x, int z, const ubyte* voxelData); void put(int x, int z, const ubyte* voxelData);
int getVoxelRegionVersion(int x, int z);
int getVoxelRegionsVersion();
ubyte* getChunk(int x, int z); ubyte* getChunk(int x, int z);
light_t* getLights(int x, int z); light_t* getLights(int x, int z);
chunk_inventories_map fetchInventories(int x, int z); chunk_inventories_map fetchInventories(int x, int z);