1
0
Fork 0
Univerxel/src/server/world/region/Memory.cpp

168 lines
4.8 KiB
C++

#include "Memory.hpp"
using namespace world::server;
#define REMOVE_CORRUPTED 1
#define LAZYNESS 8
MemoryRegion::MemoryRegion(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();
}
MemoryRegion::~MemoryRegion() {
if(!content.empty())
save(changed);
std::unique_lock lock(mutex);
auto it = content.begin();
while(it != content.end()) {
delete it->second;
it = content.erase(it);
}
}
void MemoryRegion::load() {
std::unique_lock lock(mutex);
std::ifstream file;
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));
// Read content
const auto data = new MemoryRegion::data();
data->resize(size);
file.read(data->data(), data->size());
if(!content.insert({pos, data}).second) {
LOG_E("Duplicated chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z);
}
file.peek();
}
if(file.bad()) {
LOG_E("Region corrupted read " << path);
}
assert(content.size() == chunkCount);
file.close();
}
bool MemoryRegion::read(const region_chunk_pos& pos, const zstd::read_ctx& ctx, data& out) {
std::shared_lock lock(mutex);
const auto it = content.find(pos);
if (it == content.end())
return false;
if(auto err = ctx.decompress(*it->second, out)) {
LOG_E("Corrupted region chunk: " << path << ":" << (int)pos.x << "." << (int)pos.y << "." << (int)pos.z << " "
<< err.value());
#ifdef REMOVE_CORRUPTED
LOG_W("Removing");
lock.unlock();
{
std::unique_lock ulock(mutex);
content.erase(it);
}
save(true);
#endif
return false;
}
return true;
}
void MemoryRegion::write(const region_chunk_pos& pos, const zstd::write_ctx& ctx, const std::string_view& in) {
const auto buffer = new MemoryRegion::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;
}
{
std::unique_lock lock(mutex);
// Save buffer
const auto it = content.find(pos);
if (it != content.end())
{
delete it->second;
content.erase(it);
}
content.insert({pos, buffer});
changed = true;
}
save(false);
}
void MemoryRegion::save(bool force) {
if(!force && rand() % LAZYNESS == 0)
return;
std::unique_lock lock(mutex);
std::ofstream file(path, std::ios::out | std::ios::binary);
if (!file.good()) {
LOG_E("Corrupted region path: " << path);
return;
}
{ // Write header
uint16_t size = (uint16_t)content.size();
file.write(reinterpret_cast<char *>(&size), sizeof(size));
}
for(const auto& chunk: content) {
assert(chunk.second->size() < USHRT_MAX);
auto size = (uint16_t)chunk.second->size();
const auto out = chunk.second->data();
{ // Write pos
region_chunk_pos pos = chunk.first;
file.write(reinterpret_cast<char *>(&pos.x), sizeof(region_chunk_pos::value_type));
file.write(reinterpret_cast<char *>(&pos.y), sizeof(region_chunk_pos::value_type));
file.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) {
file.put(0);
//MAYBE: store usefull uchar flags
}
// Write size
file.write(reinterpret_cast<char *>(&size), sizeof(size));
// Write content
file.write(out, size);
}
if (!file.good()) {
LOG_E("Region corrupted write " << path);
file.close();
return;
}
file.close();
changed = false;
}