mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-04 18:41:51 +00:00
219 lines
6.4 KiB
C++
219 lines
6.4 KiB
C++
#include "GlobalChunks.hpp"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "Block.hpp"
|
|
#include "Chunk.hpp"
|
|
#include "coders/json.hpp"
|
|
#include "content/Content.hpp"
|
|
#include "debug/Logger.hpp"
|
|
#include "items/Inventories.hpp"
|
|
#include "lighting/Lightmap.hpp"
|
|
#include "maths/voxmaths.hpp"
|
|
#include "objects/Entities.hpp"
|
|
#include "objects/Entity.hpp"
|
|
#include "typedefs.hpp"
|
|
#include "util/ObjectsPool.hpp"
|
|
#include "voxels/blocks_agent.hpp"
|
|
#include "world/files/WorldFiles.hpp"
|
|
#include "world/Level.hpp"
|
|
#include "world/LevelEvents.hpp"
|
|
#include "world/World.hpp"
|
|
|
|
static debug::Logger logger("chunks-storage");
|
|
|
|
GlobalChunks::GlobalChunks(Level& level)
|
|
: level(level), indices(*level.content.getIndices()) {
|
|
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] = {};
|
|
}
|
|
}
|
|
}
|
|
|
|
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, blockid_t>& 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;
|
|
}
|
|
|
|
static util::ObjectsPool<Chunk> chunks_pool(1'024);
|
|
static util::ObjectsPool<Lightmap> lightmaps_pool;
|
|
|
|
std::shared_ptr<Chunk> GlobalChunks::create(int x, int z, bool lighting) {
|
|
const auto& found = chunksMap.find(keyfrom(x, z));
|
|
if (found != chunksMap.end()) {
|
|
return found->second;
|
|
}
|
|
static std::unique_ptr<ubyte[]> voxelDataBuffer = nullptr;
|
|
if (voxelDataBuffer == nullptr) {
|
|
voxelDataBuffer = std::make_unique<ubyte[]>(CHUNK_DATA_LEN);
|
|
}
|
|
|
|
auto chunk =
|
|
chunks_pool.create(x, z, lighting ? lightmaps_pool.create() : nullptr);
|
|
chunksMap[keyfrom(x, z)] = chunk;
|
|
|
|
World& world = level.getWorld();
|
|
auto& regions = world.wfile.get()->getRegions();
|
|
|
|
if (regions.getVoxels(chunk->x, chunk->z, voxelDataBuffer.get())) {
|
|
const auto& indices = *level.content.getIndices();
|
|
|
|
chunk->decode(voxelDataBuffer.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 (chunk->lightmap) {
|
|
if (regions.getLights(chunk->x, chunk->z, voxelDataBuffer.get())) {
|
|
chunk->lightmap->decode(voxelDataBuffer.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;
|
|
|
|
save(chunk);
|
|
if (onUnload) {
|
|
onUnload(*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);
|
|
}
|
|
|
|
std::optional<AABB> GlobalChunks::isObstacleAt(float x, float y, float z, const AABB& aabb) const {
|
|
return blocks_agent::is_obstacle_at(*this, x, y, z, aabb);
|
|
}
|