#include "GlobalChunks.hpp" #include #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 onUnload) { this->onUnload = std::move(onUnload); } std::shared_ptr 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& 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 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(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) { 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(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(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() ); } void GlobalChunks::saveAll() { for (const auto& [_, chunk] : chunksMap) { save(chunk.get()); } } void GlobalChunks::putChunk(std::shared_ptr 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); }