1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Remove MapSector, take 2

This commit is contained in:
Lars 2025-06-03 16:28:19 -07:00
parent 0ea89d4112
commit 1771b8c9a1
14 changed files with 236 additions and 649 deletions

View file

@ -463,7 +463,6 @@ set(common_SRCS
map.cpp
mapblock.cpp
mapnode.cpp
mapsector.cpp
nodedef.cpp
pathfinder.cpp
player.cpp

View file

@ -31,7 +31,6 @@
#include "filesys.h"
#include "mapblock_mesh.h"
#include "mapblock.h"
#include "mapsector.h"
#include "minimap.h"
#include "modchannels.h"
#include "content/mods.h"
@ -589,15 +588,14 @@ void Client::step(float dtime)
bool do_mapper_update = true;
ClientMap &map = m_env.getClientMap();
MapSector *sector = map.emergeSector(v2s16(r.p.X, r.p.Z));
MapBlock *block = sector->getBlockNoCreateNoEx(r.p.Y);
MapBlock *block = map.getBlockNoCreateNoEx(r.p);
// The block in question is not visible (perhaps it is culled at the server),
// create a blank block just to hold the chunk's mesh.
// If the block becomes visible later it will replace the blank block.
if (!block && r.mesh)
block = sector->createBlankBlock(r.p.Y);
block = map.createBlankBlock(r.p);
if (block) {
// Delete the old mesh

View file

@ -9,7 +9,6 @@
#include <IMaterialRenderer.h>
#include <IVideoDriver.h>
#include <matrix4.h>
#include "mapsector.h"
#include "mapblock.h"
#include "nodedef.h"
#include "profiler.h"
@ -239,20 +238,6 @@ void ClientMap::updateCamera(v3f pos, v3f dir, f32 fov, v3s16 offset, video::SCo
}
}
MapSector * ClientMap::emergeSector(v2s16 p2d)
{
// Check that it doesn't exist already
MapSector *sector = getSectorNoGenerate(p2d);
// Create it if it does not exist yet
if (!sector) {
sector = new MapSector(this, p2d, m_gamedef);
m_sectors[p2d] = sector;
}
return sector;
}
void ClientMap::OnRegisterSceneNode()
{
if(IsVisible)
@ -409,91 +394,75 @@ void ClientMap::updateDrawList()
frustum and display them.
*/
if (m_control.range_all || m_loops_occlusion_culler) {
// Number of blocks currently loaded by the client
u32 blocks_loaded = 0;
// Number of blocks with mesh in rendering range
u32 blocks_in_range_with_mesh = 0;
MapBlockVect sectorblocks;
// Loop through all blocks
for (const auto &entry : m_blocks) {
MapBlock *block = entry.second;
MapBlockMesh *mesh = block->mesh;
for (auto &sector_it : m_sectors) {
const MapSector *sector = sector_it.second;
v2s16 sp = sector->getPos();
// Calculate the coordinates for range and frustum culling
v3f mesh_sphere_center;
f32 mesh_sphere_radius;
blocks_loaded += sector->size();
if (!m_control.range_all) {
if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X ||
sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z)
continue;
v3s16 block_pos_nodes = block->getPosRelative();
if (mesh) {
mesh_sphere_center = intToFloat(block_pos_nodes, BS)
+ mesh->getBoundingSphereCenter();
mesh_sphere_radius = mesh->getBoundingRadius();
} else {
mesh_sphere_center = intToFloat(block_pos_nodes, BS)
+ v3f((MAP_BLOCKSIZE * 0.5f - 0.5f) * BS);
mesh_sphere_radius = 0.0f;
}
// Loop through blocks in sector
for (const auto &entry : sector->getBlocks()) {
MapBlock *block = entry.second.get();
MapBlockMesh *mesh = block->mesh;
// First, perform a simple distance check.
if (!m_control.range_all &&
mesh_sphere_center.getDistanceFrom(m_camera_position) >
m_control.wanted_range * BS + mesh_sphere_radius)
continue; // Out of range, skip.
// Calculate the coordinates for range and frustum culling
v3f mesh_sphere_center;
f32 mesh_sphere_radius;
// Keep the block alive as long as it is in range.
block->resetUsageTimer();
blocks_in_range_with_mesh++;
v3s16 block_pos_nodes = block->getPosRelative();
// Frustum culling
// Only do coarse culling here, to account for fast camera movement.
// This is needed because this function is not called every frame.
float frustum_cull_extra_radius = 300.0f;
if (is_frustum_culled(mesh_sphere_center,
mesh_sphere_radius + frustum_cull_extra_radius)) {
blocks_frustum_culled++;
continue;
}
if (mesh) {
mesh_sphere_center = intToFloat(block_pos_nodes, BS)
+ mesh->getBoundingSphereCenter();
mesh_sphere_radius = mesh->getBoundingRadius();
} else {
mesh_sphere_center = intToFloat(block_pos_nodes, BS)
+ v3f((MAP_BLOCKSIZE * 0.5f - 0.5f) * BS);
mesh_sphere_radius = 0.0f;
}
// Raytraced occlusion culling - send rays from the camera to the block's corners
if (!m_control.range_all && occlusion_culling_enabled && m_enable_raytraced_culling &&
mesh &&
isMeshOccluded(block, mesh_grid.cell_size, cam_pos_nodes)) {
blocks_occlusion_culled++;
continue;
}
// First, perform a simple distance check.
if (!m_control.range_all &&
mesh_sphere_center.getDistanceFrom(m_camera_position) >
m_control.wanted_range * BS + mesh_sphere_radius)
continue; // Out of range, skip.
// Keep the block alive as long as it is in range.
block->resetUsageTimer();
blocks_in_range_with_mesh++;
// Frustum culling
// Only do coarse culling here, to account for fast camera movement.
// This is needed because this function is not called every frame.
float frustum_cull_extra_radius = 300.0f;
if (is_frustum_culled(mesh_sphere_center,
mesh_sphere_radius + frustum_cull_extra_radius)) {
blocks_frustum_culled++;
continue;
}
// Raytraced occlusion culling - send rays from the camera to the block's corners
if (!m_control.range_all && occlusion_culling_enabled && m_enable_raytraced_culling &&
mesh &&
isMeshOccluded(block, mesh_grid.cell_size, cam_pos_nodes)) {
blocks_occlusion_culled++;
continue;
}
if (mesh_grid.cell_size > 1) {
// Block meshes are stored in the corner block of a chunk
// (where all coordinate are divisible by the chunk size)
// Add them to the de-dup set.
shortlist.emplace(mesh_grid.getMeshPos(block->getPos()));
// All other blocks we can grab and add to the keeplist right away.
m_keeplist.push_back(block);
block->refGrab();
} else if (mesh) {
// without mesh chunking we can add the block to the drawlist
block->refGrab();
m_drawlist.emplace(block->getPos(), block);
}
if (mesh_grid.cell_size > 1) {
// Block meshes are stored in the corner block of a chunk
// (where all coordinate are divisible by the chunk size)
// Add them to the de-dup set.
shortlist.emplace(mesh_grid.getMeshPos(block->getPos()));
// All other blocks we can grab and add to the keeplist right away.
m_keeplist.push_back(block);
block->refGrab();
} else if (mesh) {
// without mesh chunking we can add the block to the drawlist
block->refGrab();
m_drawlist.emplace(block->getPos(), block);
}
}
g_profiler->avg("MapBlock meshes in range [#]", blocks_in_range_with_mesh);
g_profiler->avg("MapBlocks loaded [#]", blocks_loaded);
g_profiler->avg("MapBlocks loaded [#]", m_blocks.size());
} else {
// Blocks visited by the algorithm
u32 blocks_visited = 0;
@ -529,10 +498,7 @@ void ClientMap::updateDrawList()
blocks_visited++;
// Get the sector, block and mesh
MapSector *sector = this->getSectorNoGenerate(v2s16(block_coord.X, block_coord.Z));
MapBlock *block = sector ? sector->getBlockNoCreateNoEx(block_coord.Y) : nullptr;
MapBlock *block = getBlockNoCreateNoEx(block_coord);
MapBlockMesh *mesh = block ? block->mesh : nullptr;
@ -729,60 +695,42 @@ void ClientMap::touchMapBlocks()
v3s16 p_blocks_max;
getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max);
// Number of blocks currently loaded by the client
u32 blocks_loaded = 0;
// Number of blocks with mesh in rendering range
u32 blocks_in_range_with_mesh = 0;
for (const auto &sector_it : m_sectors) {
const MapSector *sector = sector_it.second;
v2s16 sp = sector->getPos();
for (const auto &entry : m_blocks) {
MapBlock *block = entry.second;
MapBlockMesh *mesh = block->mesh;
blocks_loaded += sector->size();
if (!m_control.range_all) {
if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X ||
sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z)
continue;
// Calculate the coordinates for range and frustum culling
v3f mesh_sphere_center;
f32 mesh_sphere_radius;
v3s16 block_pos_nodes = block->getPosRelative();
if (mesh) {
mesh_sphere_center = intToFloat(block_pos_nodes, BS)
+ mesh->getBoundingSphereCenter();
mesh_sphere_radius = mesh->getBoundingRadius();
} else {
mesh_sphere_center = intToFloat(block_pos_nodes, BS)
+ v3f((MAP_BLOCKSIZE * 0.5f - 0.5f) * BS);
mesh_sphere_radius = 0.0f;
}
/*
Loop through blocks in sector
*/
// First, perform a simple distance check.
if (!m_control.range_all &&
mesh_sphere_center.getDistanceFrom(m_camera_position) >
m_control.wanted_range * BS + mesh_sphere_radius)
continue; // Out of range, skip.
for (const auto &entry : sector->getBlocks()) {
MapBlock *block = entry.second.get();
MapBlockMesh *mesh = block->mesh;
// Calculate the coordinates for range and frustum culling
v3f mesh_sphere_center;
f32 mesh_sphere_radius;
v3s16 block_pos_nodes = block->getPosRelative();
if (mesh) {
mesh_sphere_center = intToFloat(block_pos_nodes, BS)
+ mesh->getBoundingSphereCenter();
mesh_sphere_radius = mesh->getBoundingRadius();
} else {
mesh_sphere_center = intToFloat(block_pos_nodes, BS)
+ v3f((MAP_BLOCKSIZE * 0.5f - 0.5f) * BS);
mesh_sphere_radius = 0.0f;
}
// First, perform a simple distance check.
if (!m_control.range_all &&
mesh_sphere_center.getDistanceFrom(m_camera_position) >
m_control.wanted_range * BS + mesh_sphere_radius)
continue; // Out of range, skip.
// Keep the block alive as long as it is in range.
block->resetUsageTimer();
blocks_in_range_with_mesh++;
}
// Keep the block alive as long as it is in range.
block->resetUsageTimer();
blocks_in_range_with_mesh++;
}
g_profiler->avg("MapBlock meshes in range [#]", blocks_in_range_with_mesh);
g_profiler->avg("MapBlocks loaded [#]", blocks_loaded);
g_profiler->avg("MapBlocks loaded [#]", m_blocks.size());
}
void MeshBufListMaps::addFromBlock(v3s16 block_pos, MapBlockMesh *block_mesh,
@ -1532,48 +1480,36 @@ void ClientMap::updateDrawListShadow(v3f shadow_light_pos, v3f shadow_light_dir,
}
m_drawlist_shadow.clear();
// Number of blocks currently loaded by the client
u32 blocks_loaded = 0;
// Number of blocks with mesh in rendering range
u32 blocks_in_range_with_mesh = 0;
for (auto &sector_it : m_sectors) {
const MapSector *sector = sector_it.second;
if (!sector)
for (const auto &entry : m_blocks) {
MapBlock *block = entry.second;
MapBlockMesh *mesh = block->mesh;
if (!mesh) {
// Ignore if mesh doesn't exist
continue;
blocks_loaded += sector->size();
}
/*
Loop through blocks in sector
*/
for (const auto &entry : sector->getBlocks()) {
MapBlock *block = entry.second.get();
MapBlockMesh *mesh = block->mesh;
if (!mesh) {
// Ignore if mesh doesn't exist
continue;
}
v3f block_pos = intToFloat(block->getPosRelative(), BS) + mesh->getBoundingSphereCenter();
v3f projection = shadow_light_pos + shadow_light_dir * shadow_light_dir.dotProduct(block_pos - shadow_light_pos);
if (projection.getDistanceFrom(block_pos) > (radius + mesh->getBoundingRadius()))
continue;
v3f block_pos = intToFloat(block->getPosRelative(), BS) + mesh->getBoundingSphereCenter();
v3f projection = shadow_light_pos + shadow_light_dir * shadow_light_dir.dotProduct(block_pos - shadow_light_pos);
if (projection.getDistanceFrom(block_pos) > (radius + mesh->getBoundingRadius()))
continue;
blocks_in_range_with_mesh++;
blocks_in_range_with_mesh++;
// This block is in range. Reset usage timer.
block->resetUsageTimer();
// This block is in range. Reset usage timer.
block->resetUsageTimer();
// Add to set
if (m_drawlist_shadow.emplace(block->getPos(), block).second) {
block->refGrab();
}
// Add to set
if (m_drawlist_shadow.emplace(block->getPos(), block).second) {
block->refGrab();
}
}
g_profiler->avg("SHADOW MapBlock meshes in range [#]", blocks_in_range_with_mesh);
g_profiler->avg("SHADOW MapBlocks drawn [#]", m_drawlist_shadow.size());
g_profiler->avg("SHADOW MapBlocks loaded [#]", blocks_loaded);
g_profiler->avg("SHADOW MapBlocks loaded [#]", m_blocks.size());
}
void ClientMap::reportMetrics(u64 save_time_us, u32 saved_blocks, u32 all_blocks)

View file

@ -69,11 +69,6 @@ public:
void updateCamera(v3f pos, v3f dir, f32 fov, v3s16 offset, video::SColor light_color);
/*
Forcefully get a sector from somewhere
*/
MapSector * emergeSector(v2s16 p) override;
/*
ISceneNode methods
*/

View file

@ -239,7 +239,6 @@ void MapDatabaseSQLite3::createDatabase()
// Declaring a primary key automatically creates an index and the
// order largely dictates which range operations can be sped up.
// see also: <https://www.sqlite.org/optoverview.html#skipscan>
// Putting XZ before Y matches our MapSector abstraction.
"PRIMARY KEY (`x`, `z`, `y`)"
");\n"
;

View file

@ -5,7 +5,6 @@
#pragma once
#include "map.h"
#include "mapsector.h"
class DummyMap : public Map
{
@ -13,12 +12,9 @@ public:
DummyMap(IGameDef *gamedef, v3s16 bpmin, v3s16 bpmax): Map(gamedef)
{
for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
for (s16 x = bpmin.X; x <= bpmax.X; x++) {
v2s16 p2d(x, z);
MapSector *sector = new MapSector(this, p2d, gamedef);
m_sectors[p2d] = sector;
for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
sector->createBlankBlock(y);
for (s16 x = bpmin.X; x <= bpmax.X; x++)
for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
createBlankBlock(v3s16(x, y, z));
}
}

View file

@ -3,7 +3,6 @@
// Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
#include "map.h"
#include "mapsector.h"
#include "mapblock.h"
#include "voxel.h"
#include "voxelalgorithms.h"
@ -31,10 +30,10 @@ Map::Map(IGameDef *gamedef):
Map::~Map()
{
/*
Free all MapSectors
Free all MapBlocks
*/
for (auto &sector : m_sectors) {
delete sector.second;
for (auto &entry : m_blocks) {
delete entry.second;
}
}
@ -55,40 +54,61 @@ void Map::dispatchEvent(const MapEditEvent &event)
}
}
MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
MapBlock* Map::createBlankBlockNoInsert(v3s16 p)
{
if(m_sector_cache != NULL && p == m_sector_cache_p){
MapSector * sector = m_sector_cache;
return sector;
}
if (blockpos_over_max_limit(v3s16(p)))
throw InvalidPositionException("createBlankBlockNoInsert(): pos over max mapgen limit");
auto n = m_sectors.find(p);
if (n == m_sectors.end())
return NULL;
MapSector *sector = n->second;
// Cache the last result
m_sector_cache_p = p;
m_sector_cache = sector;
return sector;
return new MapBlock(p, m_gamedef);
}
MapSector *Map::getSectorNoGenerate(v2s16 p)
MapBlock *Map::createBlankBlock(v3s16 p)
{
return getSectorNoGenerateNoLock(p);
MapBlock *block = createBlankBlockNoInsert(p);
m_blocks[p] = block;
return block;
}
void Map::deleteBlockImmediate(MapBlock *block)
{
detachBlock(block);
// returned smart-ptr is dropped
}
std::unique_ptr<MapBlock> Map::detachBlock(MapBlock *block)
{
// Remove from container
auto it = m_blocks.find(block->getPos());
assert(it != m_blocks.end());
std::unique_ptr<MapBlock> ret(it->second);
assert(ret.get() == block);
m_blocks.erase(it);
// Mark as removed
block->makeOrphan();
return ret;
}
void Map::insertBlock(MapBlock *block)
{
v3s16 pos = block->getPos();
MapBlock *block2 = getBlockNoCreateNoEx(pos);
if (block2) {
throw AlreadyExistsException("Block already exists");
}
// Insert into container
m_blocks[pos] = block;
}
MapBlock *Map::getBlockNoCreateNoEx(v3s16 p3d)
{
v2s16 p2d(p3d.X, p3d.Z);
MapSector *sector = getSectorNoGenerate(p2d);
if (!sector)
return nullptr;
MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
return block;
auto it = m_blocks.find(p3d);
return it != m_blocks.end() ? it->second : nullptr;
}
MapBlock *Map::getBlockNoCreate(v3s16 p3d)
@ -256,11 +276,9 @@ bool Map::removeNodeWithEvent(v3s16 p)
}
struct TimeOrderedMapBlock {
MapSector *sect;
MapBlock *block;
TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
sect(sect),
TimeOrderedMapBlock(MapBlock *block) :
block(block)
{}
@ -281,7 +299,6 @@ void Map::timerUpdate(float dtime, float unload_timeout, s32 max_loaded_blocks,
// Profile modified reasons
Profiler modprofiler;
std::vector<v2s16> sector_deletion_queue;
u32 deleted_blocks_count = 0;
u32 saved_blocks_count = 0;
u32 block_count_all = 0;
@ -292,62 +309,44 @@ void Map::timerUpdate(float dtime, float unload_timeout, s32 max_loaded_blocks,
// If there is no practical limit, we spare creation of mapblock_queue
if (max_loaded_blocks < 0) {
MapBlockVect blocks;
for (auto &sector_it : m_sectors) {
MapSector *sector = sector_it.second;
for (auto it = m_blocks.begin(); it != m_blocks.end();) {
MapBlock *block = it->second;
block->incrementUsageTimer(dtime);
bool all_blocks_deleted = true;
if (block->refGet() == 0
&& block->getUsageTimer() > unload_timeout) {
v3s16 p = block->getPos();
blocks.clear();
sector->getBlocks(blocks);
for (MapBlock *block : blocks) {
block->incrementUsageTimer(dtime);
if (block->refGet() == 0
&& block->getUsageTimer() > unload_timeout) {
v3s16 p = block->getPos();
// Save if modified
if (block->getModified() != MOD_STATE_CLEAN
&& save_before_unloading) {
modprofiler.add(block->getModifiedReasonString(), 1);
if (!saveBlock(block))
continue;
saved_blocks_count++;
// Save if modified
if (block->getModified() != MOD_STATE_CLEAN
&& save_before_unloading) {
modprofiler.add(block->getModifiedReasonString(), 1);
if (!saveBlock(block)) {
it++;
continue;
}
// Delete from memory
sector->deleteBlock(block);
if (unloaded_blocks)
unloaded_blocks->push_back(p);
deleted_blocks_count++;
} else {
all_blocks_deleted = false;
block_count_all++;
saved_blocks_count++;
}
}
// Delete sector if we emptied it
if (all_blocks_deleted) {
sector_deletion_queue.push_back(sector_it.first);
// Delete directly from container
it = m_blocks.erase(it);
delete block;
if (unloaded_blocks)
unloaded_blocks->push_back(p);
deleted_blocks_count++;
} else {
block_count_all++;
it++;
}
}
} else {
std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
MapBlockVect blocks;
for (auto &sector_it : m_sectors) {
MapSector *sector = sector_it.second;
blocks.clear();
sector->getBlocks(blocks);
for (MapBlock *block : blocks) {
block->incrementUsageTimer(dtime);
mapblock_queue.push(TimeOrderedMapBlock(sector, block));
}
for (auto &entry : m_blocks) {
MapBlock *block = entry.second;
block->incrementUsageTimer(dtime);
mapblock_queue.push(TimeOrderedMapBlock(block));
}
block_count_all = mapblock_queue.size();
@ -375,7 +374,7 @@ void Map::timerUpdate(float dtime, float unload_timeout, s32 max_loaded_blocks,
}
// Delete from memory
b.sect->deleteBlock(block);
deleteBlockImmediate(block);
if (unloaded_blocks)
unloaded_blocks->push_back(p);
@ -383,13 +382,6 @@ void Map::timerUpdate(float dtime, float unload_timeout, s32 max_loaded_blocks,
deleted_blocks_count++;
block_count_all--;
}
// Delete empty sectors
for (auto &sector_it : m_sectors) {
if (sector_it.second->empty()) {
sector_deletion_queue.push_back(sector_it.first);
}
}
}
endSave();
@ -397,9 +389,6 @@ void Map::timerUpdate(float dtime, float unload_timeout, s32 max_loaded_blocks,
reportMetrics(end_time - start_time, saved_blocks_count, block_count_all);
// Finally delete the empty sectors
deleteSectors(sector_deletion_queue);
if(deleted_blocks_count != 0)
{
PrintInfo(infostream); // ServerMap/ClientMap:
@ -422,19 +411,6 @@ void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
timerUpdate(0, -1, 0, unloaded_blocks);
}
void Map::deleteSectors(const std::vector<v2s16> &sectorList)
{
for (v2s16 j : sectorList) {
MapSector *sector = m_sectors[j];
// If sector is in sector cache, remove it from there
if (m_sector_cache == sector)
m_sector_cache = nullptr;
// Remove from map and delete
m_sectors.erase(j);
delete sector;
}
}
void Map::PrintInfo(std::ostream &out)
{
out<<"Map: ";

View file

@ -7,6 +7,7 @@
#include <iostream>
#include <set>
#include <map>
#include <memory>
#include "irrlichttypes_bloated.h"
#include "mapblock.h"
@ -18,7 +19,6 @@
#include "nodetimer.h"
#include "debug.h"
class MapSector;
class NodeMetadata;
class IGameDef;
class IRollbackManager;
@ -113,22 +113,20 @@ public:
// event shall be deleted by caller after the call.
void dispatchEvent(const MapEditEvent &event);
// On failure returns NULL
MapSector * getSectorNoGenerateNoLock(v2s16 p2d);
// Same as the above (there exists no lock anymore)
MapSector * getSectorNoGenerate(v2s16 p2d);
/*
This is overloaded by ClientMap and ServerMap to allow
their differing fetch methods.
*/
virtual MapSector * emergeSector(v2s16 p){ return NULL; }
// Returns InvalidPositionException if not found
MapBlock * getBlockNoCreate(v3s16 p);
// Returns NULL if not found
MapBlock * getBlockNoCreateNoEx(v3s16 p);
MapBlock* createBlankBlockNoInsert(v3s16 p);
MapBlock *createBlankBlock(v3s16 p);
void insertBlock(MapBlock *block);
void deleteBlockImmediate(MapBlock *block);
// Remove a block from the map without deleting it
// Returns an owning ptr to block.
std::unique_ptr<MapBlock> detachBlock(MapBlock *block);
/* Server overrides */
virtual MapBlock * emergeBlock(v3s16 p, bool create_blank=true)
{ return getBlockNoCreateNoEx(p); }
@ -179,7 +177,7 @@ public:
virtual bool deleteBlock(v3s16 blockpos) { return false; }
/*
Updates usage timers and unloads unused blocks and sectors.
Updates usage timers and unloads unused blocks.
Saves modified blocks before unloading if possible.
*/
void timerUpdate(float dtime, float unload_timeout, s32 max_loaded_blocks,
@ -191,11 +189,6 @@ public:
*/
void unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks=NULL);
// Deletes sectors and their blocks from memory
// Takes cache into account
// If deleted sector is in sector cache, clears cache
void deleteSectors(const std::vector<v2s16> &list);
// For debug printing. Prints "Map: ", "ServerMap: " or "ClientMap: "
virtual void PrintInfo(std::ostream &out);
@ -248,7 +241,6 @@ public:
for (s16 bz = bpmin.Z; bz <= bpmax.Z; bz++)
for (s16 bx = bpmin.X; bx <= bpmax.X; bx++)
for (s16 by = bpmin.Y; by <= bpmax.Y; by++) {
// y is iterated innermost to make use of the sector cache.
v3s16 bp(bx, by, bz);
MapBlock *block = getBlockNoCreateNoEx(bp);
v3s16 basep = bp * MAP_BLOCKSIZE;
@ -282,11 +274,7 @@ protected:
std::set<MapEventReceiver*> m_event_receivers;
std::unordered_map<v2s16, MapSector*> m_sectors;
// Be sure to set this to NULL when the cached sector is deleted
MapSector *m_sector_cache = nullptr;
v2s16 m_sector_cache_p;
std::unordered_map<v3s16, MapBlock*> m_blocks;
// This stores the properties of the nodes on the map.
const NodeDefManager *m_nodedef;

View file

@ -1,125 +0,0 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
#include "mapsector.h"
#include "exceptions.h"
#include "mapblock.h"
#include "serialization.h"
MapSector::MapSector(Map *parent, v2s16 pos, IGameDef *gamedef):
m_parent(parent),
m_pos(pos),
m_gamedef(gamedef)
{
}
MapSector::~MapSector()
{
deleteBlocks();
}
void MapSector::deleteBlocks()
{
// Clear cache
m_block_cache = nullptr;
// Delete all blocks
m_blocks.clear();
}
MapBlock *MapSector::getBlockBuffered(s16 y)
{
MapBlock *block;
if (m_block_cache && y == m_block_cache_y) {
return m_block_cache;
}
// If block doesn't exist, return NULL
auto it = m_blocks.find(y);
block = it != m_blocks.end() ? it->second.get() : nullptr;
// Cache the last result
m_block_cache_y = y;
m_block_cache = block;
return block;
}
MapBlock *MapSector::getBlockNoCreateNoEx(s16 y)
{
return getBlockBuffered(y);
}
std::unique_ptr<MapBlock> MapSector::createBlankBlockNoInsert(s16 y)
{
assert(getBlockBuffered(y) == nullptr); // Pre-condition
if (blockpos_over_max_limit(v3s16(0, y, 0)))
throw InvalidPositionException("createBlankBlockNoInsert(): pos over max mapgen limit");
v3s16 blockpos_map(m_pos.X, y, m_pos.Y);
return std::make_unique<MapBlock>(blockpos_map, m_gamedef);
}
MapBlock *MapSector::createBlankBlock(s16 y)
{
std::unique_ptr<MapBlock> block_u = createBlankBlockNoInsert(y);
MapBlock *block = block_u.get();
m_blocks[y] = std::move(block_u);
return block;
}
void MapSector::insertBlock(std::unique_ptr<MapBlock> block)
{
s16 block_y = block->getPos().Y;
MapBlock *block2 = getBlockBuffered(block_y);
if (block2) {
throw AlreadyExistsException("Block already exists");
}
v2s16 p2d(block->getPos().X, block->getPos().Z);
assert(p2d == m_pos);
// Insert into container
m_blocks[block_y] = std::move(block);
}
void MapSector::deleteBlock(MapBlock *block)
{
detachBlock(block);
// returned smart-ptr is dropped
}
std::unique_ptr<MapBlock> MapSector::detachBlock(MapBlock *block)
{
s16 block_y = block->getPos().Y;
// Clear from cache
m_block_cache = nullptr;
// Remove from container
auto it = m_blocks.find(block_y);
assert(it != m_blocks.end());
std::unique_ptr<MapBlock> ret = std::move(it->second);
assert(ret.get() == block);
m_blocks.erase(it);
// Mark as removed
block->makeOrphan();
return ret;
}
void MapSector::getBlocks(MapBlockVect &dest)
{
dest.reserve(dest.size() + m_blocks.size());
for (auto &block : m_blocks) {
dest.push_back(block.second.get());
}
}

View file

@ -1,85 +0,0 @@
// Luanti
// SPDX-License-Identifier: LGPL-2.1-or-later
// Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
#pragma once
#include "irrlichttypes.h"
#include "irr_v2d.h"
#include "mapblock.h"
#include <ostream>
#include <memory>
#include <map>
#include <vector>
class Map;
class IGameDef;
/*
This is an Y-wise stack of MapBlocks.
*/
#define MAPSECTOR_SERVER 0
#define MAPSECTOR_CLIENT 1
class MapSector
{
public:
MapSector(Map *parent, v2s16 pos, IGameDef *gamedef);
virtual ~MapSector();
void deleteBlocks();
v2s16 getPos() const
{
return m_pos;
}
MapBlock *getBlockNoCreateNoEx(s16 y);
std::unique_ptr<MapBlock> createBlankBlockNoInsert(s16 y);
MapBlock *createBlankBlock(s16 y);
void insertBlock(std::unique_ptr<MapBlock> block);
void deleteBlock(MapBlock *block);
// Remove a block from the map and the sector without deleting it
// Returns an owning ptr to block.
std::unique_ptr<MapBlock> detachBlock(MapBlock *block);
// This makes a copy of the internal collection.
// Prefer getBlocks() if possible.
void getBlocks(MapBlockVect &dest);
// Get access to the internal collection
// This is explicitly only allowed on a const object since modifying anything while iterating is unsafe.
// The caller needs to make sure that this does not happen.
const auto &getBlocks() const { return m_blocks; }
const auto &getBlocks() = delete;
bool empty() const { return m_blocks.empty(); }
int size() const { return m_blocks.size(); }
protected:
// The pile of MapBlocks
std::unordered_map<s16, std::unique_ptr<MapBlock>> m_blocks;
Map *m_parent;
// Position on parent (in MapBlock widths)
v2s16 m_pos;
IGameDef *m_gamedef;
// Last-used block is cached here for quicker access.
// Be sure to set this to nullptr when the cached block is deleted
MapBlock *m_block_cache = nullptr;
s16 m_block_cache_y;
/*
Private methods
*/
MapBlock *getBlockBuffered(s16 y);
};

View file

@ -13,7 +13,6 @@
#include "client/clientmedia.h"
#include "log.h"
#include "servermap.h"
#include "mapsector.h"
#include "client/minimap.h"
#include "modchannels.h"
#include "nodedef.h"
@ -285,15 +284,7 @@ void Client::handleCommand_BlockData(NetworkPacket* pkt)
std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
std::istringstream istr(datastring, std::ios_base::binary);
MapSector *sector;
MapBlock *block;
v2s16 p2d(p.X, p.Z);
sector = m_env.getMap().emergeSector(p2d);
assert(sector->getPos() == p2d);
block = sector->getBlockNoCreateNoEx(p.Y);
MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(p);
if (block) {
/*
Update an existing block
@ -305,7 +296,7 @@ void Client::handleCommand_BlockData(NetworkPacket* pkt)
/*
Create a new block
*/
block = sector->createBlankBlock(p.Y);
block = m_env.getMap().createBlankBlock(p);
block->deSerialize(istr, m_server_ser_ver, false);
block->deSerializeNetworkSpecific(istr);
}

View file

@ -3,7 +3,6 @@
// Copyright (C) 2010-2024 celeron55, Perttu Ahola <celeron55@gmail.com>
#include "map.h"
#include "mapsector.h"
#include "filesys.h"
#include "voxel.h"
#include "voxelalgorithms.h"
@ -228,24 +227,18 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
Create the whole area of this and the neighboring blocks
*/
for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
v2s16 sectorpos(x, z);
// Sector metadata is loaded from disk if not already loaded.
MapSector *sector = createSector(sectorpos);
FATAL_ERROR_IF(sector == NULL, "createSector() failed");
for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
v3s16 p(x, y, z);
for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
v3s16 p(x, y, z);
MapBlock *block = emergeBlock(p, false);
if (block == NULL) {
block = createBlock(p);
MapBlock *block = emergeBlock(p, false);
if (block == NULL) {
block = createBlock(p);
// Block gets sunlight if this is true.
// Refer to the map generator heuristics.
bool ug = m_emerge->isBlockUnderground(p);
block->setIsUnderground(ug);
}
// Block gets sunlight if this is true.
// Refer to the map generator heuristics.
bool ug = m_emerge->isBlockUnderground(p);
block->setIsUnderground(ug);
}
}
@ -322,61 +315,15 @@ void ServerMap::finishBlockMake(BlockMakeData *data,
m_chunks_in_progress.erase(bpmin);
}
MapSector *ServerMap::createSector(v2s16 p2d)
{
/*
Check if it exists already in memory
*/
MapSector *sector = getSectorNoGenerate(p2d);
if (sector)
return sector;
/*
Do not create over max mapgen limit
*/
if (blockpos_over_max_limit(v3s16(p2d.X, 0, p2d.Y)))
throw InvalidPositionException("createSector(): pos over max mapgen limit");
/*
Generate blank sector
*/
sector = new MapSector(this, p2d, m_gamedef);
/*
Insert to container
*/
m_sectors[p2d] = sector;
return sector;
}
MapBlock * ServerMap::createBlock(v3s16 p)
{
v2s16 p2d(p.X, p.Z);
s16 block_y = p.Y;
/*
This will create or load a sector if not found in memory.
*/
MapSector *sector;
try {
sector = createSector(p2d);
} catch (InvalidPositionException &e) {
infostream<<"createBlock: createSector() failed"<<std::endl;
throw e;
}
/*
Try to get a block from the sector
*/
MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
MapBlock *block = getBlockNoCreateNoEx(p);
if (block)
return block;
// Create blank
try {
block = sector->createBlankBlock(block_y);
block = createBlankBlock(p);
} catch (InvalidPositionException &e) {
infostream << "createBlock: createBlankBlock() failed" << std::endl;
throw e;
@ -401,8 +348,7 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
if (create_blank) {
try {
MapSector *sector = createSector(v2s16(p.X, p.Z));
return sector->createBlankBlock(p.Y);
return createBlankBlock(p);
} catch (InvalidPositionException &e) {}
}
@ -502,27 +448,21 @@ void ServerMap::save(ModifiedState save_level)
// Don't do anything with sqlite unless something is really saved
bool save_started = false;
for (auto &sector_it : m_sectors) {
MapSector *sector = sector_it.second;
for (auto &entry : m_blocks) {
MapBlock *block = entry.second;
block_count_all++;
MapBlockVect blocks;
sector->getBlocks(blocks);
for (MapBlock *block : blocks) {
block_count_all++;
if(block->getModified() >= (u32)save_level) {
// Lazy beginSave()
if(!save_started) {
beginSave();
save_started = true;
}
modprofiler.add(block->getModifiedReasonString(), 1);
saveBlock(block);
block_count++;
if(block->getModified() >= (u32)save_level) {
// Lazy beginSave()
if(!save_started) {
beginSave();
save_started = true;
}
modprofiler.add(block->getModifiedReasonString(), 1);
saveBlock(block);
block_count++;
}
}
@ -557,16 +497,10 @@ void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
{
for (auto &sector_it : m_sectors) {
MapSector *sector = sector_it.second;
MapBlockVect blocks;
sector->getBlocks(blocks);
for (MapBlock *block : blocks) {
v3s16 p = block->getPos();
dst.push_back(p);
}
for (auto &entry : m_blocks) {
MapBlock *block = entry.second;
v3s16 p = block->getPos();
dst.push_back(p);
}
}
@ -666,14 +600,11 @@ MapBlock *ServerMap::loadBlock(const std::string &blob, v3s16 p3d, bool save_aft
bool created_new = false;
try {
v2s16 p2d(p3d.X, p3d.Z);
MapSector *sector = createSector(p2d);
std::unique_ptr<MapBlock> block_created_new;
block = sector->getBlockNoCreateNoEx(p3d.Y);
MapBlock* block_created_new = nullptr;
block = getBlockNoCreateNoEx(p3d);
if (!block) {
block_created_new = sector->createBlankBlockNoInsert(p3d.Y);
block = block_created_new.get();
block_created_new = createBlankBlockNoInsert(p3d);
block = block_created_new;
}
{
@ -683,7 +614,7 @@ MapBlock *ServerMap::loadBlock(const std::string &blob, v3s16 p3d, bool save_aft
// If it's a new block, insert it to the map
if (block_created_new) {
sector->insertBlock(std::move(block_created_new));
insertBlock(block_created_new);
created_new = true;
}
} catch (SerializationError &e) {
@ -751,13 +682,9 @@ bool ServerMap::deleteBlock(v3s16 blockpos)
MapBlock *block = getBlockNoCreateNoEx(blockpos);
if (block) {
v2s16 p2d(blockpos.X, blockpos.Z);
MapSector *sector = getSectorNoGenerate(p2d);
if (!sector)
return false;
// It may not be safe to delete the block from memory at the moment
// (pointers to it could still be in use)
m_detached_blocks.push_back(sector->detachBlock(block));
m_detached_blocks.push_back(detachBlock(block));
}
return true;

View file

@ -49,14 +49,6 @@ public:
ServerMap(const std::string &savedir, IGameDef *gamedef, EmergeManager *emerge, MetricsBackend *mb);
~ServerMap();
/*
Get a sector from somewhere.
- Check memory
- Check disk (doesn't load blocks)
- Create blank one
*/
MapSector *createSector(v2s16 p);
/*
Blocks are generated by using these and makeBlock().
*/

View file

@ -753,13 +753,13 @@ void update_block_border_lighting(Map *map, MapBlock *block,
/*!
* Resets the lighting of the given VoxelManipulator to
* complete darkness and full sunlight.
* Operates in one map sector.
* Operates in one map column.
*
* \param offset contains the least x and z node coordinates
* of the map sector.
* of the map column.
* \param light incoming sunlight, light[x][z] is true if there
* is sunlight above the voxel manipulator at the given x-z coordinates.
* The array's indices are relative node coordinates in the sector.
* The array's indices are relative node coordinates in the column.
* After the procedure returns, this contains outgoing light at
* the bottom of the voxel manipulator.
*/