169 lines
5.3 KiB
C++
169 lines
5.3 KiB
C++
#include "File.hpp"
|
|
|
|
#include <filesystem>
|
|
|
|
using namespace world::server;
|
|
|
|
#define REMOVE_CORRUPTED 1
|
|
|
|
FileRegion::FileRegion(const std::string &folderPath, const area_<region_pos> &pos) {
|
|
path = folderPath + '/' + std::to_string(pos.first.index) + '.' + std::to_string(pos.second.x) + '.' +
|
|
std::to_string(pos.second.y) + '.' + std::to_string(pos.second.z) + ".map";
|
|
|
|
load();
|
|
}
|
|
FileRegion::~FileRegion() {
|
|
file.close();
|
|
}
|
|
|
|
void FileRegion::load() {
|
|
std::unique_lock lock(mutex);
|
|
|
|
file.open(path, std::ios::in | std::ios::binary);
|
|
if(!file.good()) {
|
|
return;
|
|
}
|
|
// Read header
|
|
uint16_t chunkCount; //NOTE: pretty useless
|
|
file.read(reinterpret_cast<char *>(&chunkCount), sizeof(chunkCount));
|
|
|
|
while (!file.eof()) {
|
|
// Read pos
|
|
region_chunk_pos pos;
|
|
file.read(reinterpret_cast<char *>(&pos.x), sizeof(region_chunk_pos::value_type));
|
|
file.read(reinterpret_cast<char *>(&pos.y), sizeof(region_chunk_pos::value_type));
|
|
file.read(reinterpret_cast<char *>(&pos.z), sizeof(region_chunk_pos::value_type));
|
|
|
|
//NOTE: align uchar pos
|
|
if constexpr (sizeof(region_chunk_pos) % sizeof(uint16_t) != 0) {
|
|
file.ignore(1);
|
|
}
|
|
|
|
// Read size
|
|
uint16_t size = 0;
|
|
file.read(reinterpret_cast<char *>(&size), sizeof(size));
|
|
|
|
// Ignore content
|
|
if(!index.insert({pos, std::make_pair(size, file.tellg())}).second) {
|
|
LOG_E("Duplicated chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z);
|
|
}
|
|
file.ignore(size);
|
|
file.peek();
|
|
}
|
|
|
|
if(file.bad()) {
|
|
LOG_E("region corrupted read " << path);
|
|
}
|
|
|
|
assert(index.size() == chunkCount);
|
|
}
|
|
bool FileRegion::read(const region_chunk_pos& pos, const zstd::read_ctx& ctx, data& out) {
|
|
std::unique_lock lock(mutex);
|
|
|
|
const auto it = index.find(pos);
|
|
if (it == index.end())
|
|
return false;
|
|
|
|
data in;
|
|
in.resize(it->second.first);
|
|
file.seekg(it->second.second);
|
|
file.read(in.data(), in.size());
|
|
|
|
if (auto err = ctx.decompress(in, out)) {
|
|
LOG_E("Corrupted region chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " "
|
|
<< err.value());
|
|
#ifdef REMOVE_CORRUPTED
|
|
LOG_W("Removing");
|
|
index.erase(it);
|
|
lock.unlock();
|
|
save(std::nullopt);
|
|
#endif
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
void FileRegion::write(const region_chunk_pos& pos, const zstd::write_ctx& ctx, const std::string_view& in) {
|
|
auto buffer = std::make_unique<FileRegion::data>();
|
|
if (auto err = ctx.compress(in, *buffer)) {
|
|
LOG_E("Corrupted chunk save: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " "
|
|
<< err.value());
|
|
return;
|
|
}
|
|
save({{pos, std::move(buffer)}});
|
|
}
|
|
|
|
void FileRegion::save(std::optional<std::pair<region_chunk_pos, std::unique_ptr<FileRegion::data>>> added) {
|
|
std::unique_lock lock(mutex);
|
|
|
|
const auto tmpPath = path + ".tmp";
|
|
|
|
std::ofstream tmpFile(tmpPath, std::ios::out | std::ios::binary);
|
|
if (!tmpFile.good()) {
|
|
LOG_E("Corrupted region path: " << tmpPath);
|
|
return;
|
|
}
|
|
|
|
{ // Write header
|
|
uint16_t size = index.size() + (added.has_value() ? 1 : 0);
|
|
tmpFile.write(reinterpret_cast<char *>(&size), sizeof(size));
|
|
}
|
|
|
|
data tmp;
|
|
for(const auto& chunk: index) {
|
|
{ // Write pos
|
|
region_chunk_pos pos = chunk.first;
|
|
tmpFile.write(reinterpret_cast<char *>(&pos.x), sizeof(region_chunk_pos::value_type));
|
|
tmpFile.write(reinterpret_cast<char *>(&pos.y), sizeof(region_chunk_pos::value_type));
|
|
tmpFile.write(reinterpret_cast<char *>(&pos.z), sizeof(region_chunk_pos::value_type));
|
|
}
|
|
|
|
//NOTE: align uchar pos
|
|
if constexpr (sizeof(region_chunk_pos) % sizeof(uint16_t) != 0) {
|
|
tmpFile.put(0);
|
|
//MAYBE: store usefull uchar flags
|
|
}
|
|
|
|
// Write size
|
|
auto size = chunk.second.first;
|
|
tmpFile.write(reinterpret_cast<char *>(&size), sizeof(size));
|
|
|
|
// Write content
|
|
tmp.resize(size);
|
|
file.seekg(chunk.second.second);
|
|
file.read(tmp.data(), size);
|
|
tmpFile.write(tmp.data(), size);
|
|
}
|
|
if(added.has_value()) {
|
|
{ // Write pos
|
|
region_chunk_pos pos = added.value().first;
|
|
tmpFile.write(reinterpret_cast<char *>(&pos.x), sizeof(region_chunk_pos::value_type));
|
|
tmpFile.write(reinterpret_cast<char *>(&pos.y), sizeof(region_chunk_pos::value_type));
|
|
tmpFile.write(reinterpret_cast<char *>(&pos.z), sizeof(region_chunk_pos::value_type));
|
|
}
|
|
|
|
//NOTE: align uchar pos
|
|
if constexpr (sizeof(region_chunk_pos) % sizeof(uint16_t) != 0) {
|
|
tmpFile.put(0);
|
|
//MAYBE: store usefull uchar flags
|
|
}
|
|
|
|
// Write size
|
|
auto size = added.value().second->size();
|
|
tmpFile.write(reinterpret_cast<char *>(&size), sizeof(size));
|
|
|
|
// Write content
|
|
tmpFile.write(added.value().second->data(), size);
|
|
}
|
|
|
|
if (!tmpFile.good()) {
|
|
LOG_E("Region corrupted write " << tmpPath);
|
|
tmpFile.close();
|
|
return;
|
|
}
|
|
tmpFile.close();
|
|
|
|
index.clear();
|
|
file.close();
|
|
std::filesystem::rename(tmpPath, path);
|
|
load();
|
|
} |