synchronized regfiles
This commit is contained in:
+91
-67
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user