VoxelEngine/src/voxels/GlobalChunks.cpp

207 lines
5.9 KiB
C++

#include "GlobalChunks.hpp"
#include <algorithm>
#include "content/Content.hpp"
#include "coders/json.hpp"
#include "debug/Logger.hpp"
#include "files/WorldFiles.hpp"
#include "items/Inventories.hpp"
#include "lighting/Lightmap.hpp"
#include "maths/voxmaths.hpp"
#include "objects/Entities.hpp"
#include "voxels/blocks_agent.hpp"
#include "typedefs.hpp"
#include "world/Level.hpp"
#include "world/World.hpp"
#include "Block.hpp"
#include "Chunk.hpp"
static debug::Logger logger("chunks-storage");
GlobalChunks::GlobalChunks(Level* level)
: level(level), indices(level ? level->content->getIndices() : nullptr) {
chunksMap.max_load_factor(CHUNKS_MAP_MAX_LOAD_FACTOR);
}
void GlobalChunks::setOnUnload(consumer<Chunk&> onUnload) {
this->onUnload = std::move(onUnload);
}
std::shared_ptr<Chunk> GlobalChunks::fetch(int x, int z) {
const auto& found = chunksMap.find(keyfrom(x, z));
if (found == chunksMap.end()) {
return nullptr;
}
return found->second;
}
static void check_voxels(const ContentIndices& indices, Chunk& chunk) {
bool corrupted = false;
blockid_t defsCount = indices.blocks.count();
for (size_t i = 0; i < CHUNK_VOL; i++) {
blockid_t id = chunk.voxels[i].id;
if (id >= defsCount) {
if (!corrupted) {
#ifdef NDEBUG
// release
auto logline = logger.error();
logline << "corruped blocks detected at " << i << " of chunk ";
logline << chunk.x << "x" << chunk.z;
logline << " -> " << id;
corrupted = true;
#else
// debug
abort();
#endif
}
chunk.voxels[i].id = BLOCK_AIR;
}
}
}
void GlobalChunks::erase(int x, int z) {
chunksMap.erase(keyfrom(x, z));
}
static inline auto load_inventories(
WorldRegions& regions,
const Chunk& chunk,
const ContentUnitIndices<Block>& defs
) {
auto invs = regions.fetchInventories(chunk.x, chunk.z);
auto iterator = invs.begin();
while (iterator != invs.end()) {
uint index = iterator->first;
const auto& def = defs.require(chunk.voxels[index].id);
if (def.inventorySize == 0) {
iterator = invs.erase(iterator);
continue;
}
auto& inventory = iterator->second;
if (def.inventorySize != inventory->size()) {
inventory->resize(def.inventorySize);
}
++iterator;
}
return invs;
}
std::shared_ptr<Chunk> GlobalChunks::create(int x, int z) {
const auto& found = chunksMap.find(keyfrom(x, z));
if (found != chunksMap.end()) {
return found->second;
}
auto chunk = std::make_shared<Chunk>(x, z);
chunksMap[keyfrom(x, z)] = chunk;
World* world = level->getWorld();
auto& regions = world->wfile.get()->getRegions();
if (auto data = regions.getVoxels(chunk->x, chunk->z)) {
const auto& indices = *level->content->getIndices();
chunk->decode(data.get());
check_voxels(indices, *chunk);
chunk->setBlockInventories(
load_inventories(regions, *chunk, indices.blocks)
);
auto entitiesData = regions.fetchEntities(chunk->x, chunk->z);
if (entitiesData.getType() == dv::value_type::object) {
level->entities->loadEntities(std::move(entitiesData));
chunk->flags.entities = true;
}
chunk->flags.loaded = true;
for (auto& entry : chunk->inventories) {
level->inventories->store(entry.second);
}
}
if (auto lights = regions.getLights(chunk->x, chunk->z)) {
chunk->lightmap.set(lights.get());
chunk->flags.loadedLights = true;
}
chunk->blocksMetadata = regions.getBlocksData(chunk->x, chunk->z);
return chunk;
}
void GlobalChunks::pinChunk(std::shared_ptr<Chunk> chunk) {
pinnedChunks[{chunk->x, chunk->z}] = std::move(chunk);
}
void GlobalChunks::unpinChunk(int x, int z) {
pinnedChunks.erase({x, z});
}
size_t GlobalChunks::size() const {
return chunksMap.size();
}
void GlobalChunks::incref(Chunk* chunk) {
auto key = reinterpret_cast<ptrdiff_t>(chunk);
const auto& found = refCounters.find(key);
if (found == refCounters.end()) {
refCounters[key] = 1;
return;
}
found->second++;
}
void GlobalChunks::decref(Chunk* chunk) {
auto key = reinterpret_cast<ptrdiff_t>(chunk);
const auto& found = refCounters.find(key);
if (found == refCounters.end()) {
abort();
}
if (--found->second == 0) {
union {
int pos[2];
long long key;
} ekey;
ekey.pos[0] = chunk->x;
ekey.pos[1] = chunk->z;
if (onUnload) {
onUnload(*chunk);
}
save(chunk);
chunksMap.erase(ekey.key);
refCounters.erase(found);
}
}
void GlobalChunks::save(Chunk* chunk) {
if (chunk == nullptr) {
return;
}
AABB aabb = chunk->getAABB();
auto entities = level->entities->getAllInside(aabb);
auto root = dv::object();
root["data"] = level->entities->serialize(entities);
if (!entities.empty()) {
chunk->flags.entities = true;
}
level->getWorld()->wfile->getRegions().put(
chunk,
chunk->flags.entities ? json::to_binary(root, true)
: std::vector<ubyte>()
);
}
void GlobalChunks::saveAll() {
for (const auto& [_, chunk] : chunksMap) {
save(chunk.get());
}
}
void GlobalChunks::putChunk(std::shared_ptr<Chunk> chunk) {
chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk);
}
const AABB* GlobalChunks::isObstacleAt(float x, float y, float z) const {
return blocks_agent::is_obstacle_at(*this, x, y, z);
}