X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmap.h;h=cd85e1827ee99bf3830d6fd797ab35045ce23356;hb=d6050bee5187d0d456e9d95406fc74ba163ea3aa;hp=9ab77b25f5300435a1f9ac6c3044387cc4c61e70;hpb=f501cfd799fb156a32df8791fa3b3ad590d3c0d8;p=minetest.git diff --git a/src/map.h b/src/map.h index 9ab77b25f..cd85e1827 100644 --- a/src/map.h +++ b/src/map.h @@ -1,434 +1,341 @@ /* -Minetest-c55 -Copyright (C) 2010 celeron55, Perttu Ahola +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. +GNU Lesser General Public License for more details. -You should have received a copy of the GNU General Public License along +You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MAP_HEADER -#define MAP_HEADER +#pragma once -#include -#include #include -#include - -#ifdef _WIN32 - #include - #define sleep_s(x) Sleep((x*1000)) -#else - #include - #define sleep_s(x) sleep(x) -#endif +#include +#include +#include +#include -#include "common_irrlicht.h" -#include "heightmap.h" -#include "loadstatus.h" +#include "irrlichttypes_bloated.h" #include "mapnode.h" -#include "mapblock.h" -#include "mapsector.h" #include "constants.h" #include "voxel.h" - -class Map; +#include "modifiedstate.h" +#include "util/container.h" +#include "nodetimer.h" +#include "map_settings_manager.h" +#include "debug.h" + +class Settings; +class MapDatabase; +class ClientMap; +class MapSector; +class ServerMapSector; +class MapBlock; +class NodeMetadata; +class IGameDef; +class IRollbackManager; +class EmergeManager; +class ServerEnvironment; +struct BlockMakeData; /* - A cache for short-term fast access to map data - - NOTE: This doesn't really make anything more efficient - NOTE: Use VoxelManipulator, if possible - TODO: Get rid of this? - NOTE: CONFIRMED: THIS CACHE DOESN'T MAKE ANYTHING ANY FASTER + MapEditEvent */ -class MapBlockPointerCache : public NodeContainer + +#define MAPTYPE_BASE 0 +#define MAPTYPE_SERVER 1 +#define MAPTYPE_CLIENT 2 + +enum MapEditEventType{ + // Node added (changed from air or something else to something) + MEET_ADDNODE, + // Node removed (changed to air) + MEET_REMOVENODE, + // Node swapped (changed without metadata change) + MEET_SWAPNODE, + // Node metadata of block changed (not knowing which node exactly) + // p stores block coordinate + MEET_BLOCK_NODE_METADATA_CHANGED, + // Anything else (modified_blocks are set unsent) + MEET_OTHER +}; + +struct MapEditEvent { -public: - MapBlockPointerCache(Map *map); - ~MapBlockPointerCache(); + MapEditEventType type = MEET_OTHER; + v3s16 p; + MapNode n = CONTENT_AIR; + std::set modified_blocks; + u16 already_known_by_peer = 0; + + MapEditEvent() = default; - virtual u16 nodeContainerId() const + MapEditEvent * clone() { - return NODECONTAINER_ID_MAPBLOCKCACHE; + MapEditEvent *event = new MapEditEvent(); + event->type = type; + event->p = p; + event->n = n; + event->modified_blocks = modified_blocks; + return event; } - MapBlock * getBlockNoCreate(v3s16 p); - - // virtual from NodeContainer - bool isValidPosition(v3s16 p) + VoxelArea getArea() { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock *blockref; - try{ - blockref = getBlockNoCreate(blockpos); + switch(type){ + case MEET_ADDNODE: + return VoxelArea(p); + case MEET_REMOVENODE: + return VoxelArea(p); + case MEET_SWAPNODE: + return VoxelArea(p); + case MEET_BLOCK_NODE_METADATA_CHANGED: + { + v3s16 np1 = p*MAP_BLOCKSIZE; + v3s16 np2 = np1 + v3s16(1,1,1)*MAP_BLOCKSIZE - v3s16(1,1,1); + return VoxelArea(np1, np2); } - catch(InvalidPositionException &e) + case MEET_OTHER: { - return false; + VoxelArea a; + for (v3s16 p : modified_blocks) { + v3s16 np1 = p*MAP_BLOCKSIZE; + v3s16 np2 = np1 + v3s16(1,1,1)*MAP_BLOCKSIZE - v3s16(1,1,1); + a.addPoint(np1); + a.addPoint(np2); + } + return a; } - return true; - } - - // virtual from NodeContainer - MapNode getNode(v3s16 p) - { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock * blockref = getBlockNoCreate(blockpos); - v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - - return blockref->getNodeNoCheck(relpos); - } - - // virtual from NodeContainer - void setNode(v3s16 p, MapNode & n) - { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock * block = getBlockNoCreate(blockpos); - v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - block->setNodeNoCheck(relpos, n); - m_modified_blocks[blockpos] = block; + } + return VoxelArea(); } - - core::map m_modified_blocks; - -private: - Map *m_map; - core::map m_blocks; - - u32 m_from_cache_count; - u32 m_from_map_count; }; -class CacheLock +class MapEventReceiver { public: - CacheLock() - { - m_count = 0; - m_count_mutex.Init(); - m_cache_mutex.Init(); - m_waitcache_mutex.Init(); - } - - void cacheCreated() - { - //dstream<<"cacheCreated() begin"< 0); - - m_count--; - - // If this is the last one, release the cache lock - if(m_count == 0) - m_cache_mutex.Unlock(); - - //dstream<<"cacheRemoved() end"< m_sectors; - JMutex m_sector_mutex; + /* Server overrides */ + virtual MapBlock * emergeBlock(v3s16 p, bool create_blank=true) + { return getBlockNoCreateNoEx(p); } - v3f m_camera_position; - v3f m_camera_direction; - JMutex m_camera_mutex; + inline INodeDefManager * getNodeDefManager() { return m_nodedef; } - // Be sure to set this to NULL when the cached sector is deleted - MapSector *m_sector_cache; - v2s16 m_sector_cache_p; + // Returns InvalidPositionException if not found + bool isNodeUnderground(v3s16 p); - WrapperHeightmap m_hwrapper; + bool isValidPosition(v3s16 p); -public: + // throws InvalidPositionException if not found + void setNode(v3s16 p, MapNode & n); + + // Returns a CONTENT_IGNORE node if not found + // If is_valid_position is not NULL then this will be set to true if the + // position is valid, otherwise false + MapNode getNodeNoEx(v3s16 p, bool *is_valid_position = NULL); - v3s16 drawoffset; // for drawbox() - /* - Used by MapBlockPointerCache. + These handle lighting but not faces. + */ + void addNodeAndUpdate(v3s16 p, MapNode n, + std::map &modified_blocks, + bool remove_metadata = true); + void removeNodeAndUpdate(v3s16 p, + std::map &modified_blocks); - waitCaches() can be called to remove all caches before continuing + /* + Wrappers for the latter ones. + These emit events. + Return true if succeeded, false if not. */ - CacheLock m_blockcachelock; + bool addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata = true); + bool removeNodeWithEvent(v3s16 p); - Map(std::ostream &dout); - virtual ~Map(); + // Call these before and after saving of many blocks + virtual void beginSave() {} + virtual void endSave() {} - virtual u16 nodeContainerId() const - { - return NODECONTAINER_ID_MAP; - } + virtual void save(ModifiedState save_level) { FATAL_ERROR("FIXME"); } - virtual s32 mapType() const - { - return MAPTYPE_BASE; - } + // Server implements these. + // Client leaves them as no-op. + virtual bool saveBlock(MapBlock *block) { return false; } + virtual bool deleteBlock(v3s16 blockpos) { return false; } - void updateCamera(v3f pos, v3f dir) - { - JMutexAutoLock lock(m_camera_mutex); - m_camera_position = pos; - m_camera_direction = dir; - } + /* + Updates usage timers and unloads unused blocks and sectors. + Saves modified blocks before unloading on MAPTYPE_SERVER. + */ + void timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks, + std::vector *unloaded_blocks=NULL); - /*void StartUpdater() - { - updater.Start(); - } + /* + Unloads all blocks with a zero refCount(). + Saves modified blocks before unloading on MAPTYPE_SERVER. + */ + void unloadUnreferencedBlocks(std::vector *unloaded_blocks=NULL); - void StopUpdater() - { - updater.setRun(false); - while(updater.IsRunning()) - sleep_s(1); - } + // Deletes sectors and their blocks from memory + // Takes cache into account + // If deleted sector is in sector cache, clears cache + void deleteSectors(std::vector &list); - bool UpdaterIsRunning() - { - return updater.IsRunning(); - }*/ + // For debug printing. Prints "Map: ", "ServerMap: " or "ClientMap: " + virtual void PrintInfo(std::ostream &out); - static core::aabbox3d getNodeBox(v3s16 p) - { - return core::aabbox3d( - (float)p.X * BS - 0.5*BS, - (float)p.Y * BS - 0.5*BS, - (float)p.Z * BS - 0.5*BS, - (float)p.X * BS + 0.5*BS, - (float)p.Y * BS + 0.5*BS, - (float)p.Z * BS + 0.5*BS - ); - } + void transformLiquids(std::map & modified_blocks, + ServerEnvironment *env); - //bool sectorExists(v2s16 p); - MapSector * getSectorNoGenerate(v2s16 p2d); /* - This is overloaded by ClientMap and ServerMap to allow - their differing fetch methods. + Node metadata + These are basically coordinate wrappers to MapBlock */ - virtual MapSector * emergeSector(v2s16 p) = 0; - - // Returns InvalidPositionException if not found - MapBlock * getBlockNoCreate(v3s16 p); - //virtual MapBlock * getBlock(v3s16 p, bool generate=true); - - // Returns InvalidPositionException if not found - f32 getGroundHeight(v2s16 p, bool generate=false); - void setGroundHeight(v2s16 p, f32 y, bool generate=false); - - // Returns InvalidPositionException if not found - bool isNodeUnderground(v3s16 p); - - // virtual from NodeContainer - bool isValidPosition(v3s16 p) - { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock *blockref; - try{ - blockref = getBlockNoCreate(blockpos); - } - catch(InvalidPositionException &e) - { - return false; - } - return true; - /*v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - bool is_valid = blockref->isValidPosition(relpos); - return is_valid;*/ - } - - // virtual from NodeContainer - MapNode getNode(v3s16 p) - { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock * blockref = getBlockNoCreate(blockpos); - v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - return blockref->getNodeNoCheck(relpos); - } - - // virtual from NodeContainer - void setNode(v3s16 p, MapNode & n) - { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock * blockref = getBlockNoCreate(blockpos); - v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - blockref->setNodeNoCheck(relpos, n); - } + std::vector findNodesWithMetadata(v3s16 p1, v3s16 p2); + NodeMetadata *getNodeMetadata(v3s16 p); + + /** + * Sets metadata for a node. + * This method sets the metadata for a given node. + * On success, it returns @c true and the object pointed to + * by @p meta is then managed by the system and should + * not be deleted by the caller. + * + * In case of failure, the method returns @c false and the + * caller is still responsible for deleting the object! + * + * @param p node coordinates + * @param meta pointer to @c NodeMetadata object + * @return @c true on success, false on failure + */ + bool setNodeMetadata(v3s16 p, NodeMetadata *meta); + void removeNodeMetadata(v3s16 p); - /*MapNode getNodeGenerate(v3s16 p) - { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock * blockref = getBlock(blockpos); - v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; + /* + Node Timers + These are basically coordinate wrappers to MapBlock + */ - return blockref->getNode(relpos); - }*/ + NodeTimer getNodeTimer(v3s16 p); + void setNodeTimer(const NodeTimer &t); + void removeNodeTimer(v3s16 p); - /*void setNodeGenerate(v3s16 p, MapNode & n) - { - v3s16 blockpos = getNodeBlockPos(p); - MapBlock * blockref = getBlock(blockpos); - v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; - blockref->setNode(relpos, n); - }*/ - - void unspreadLight(core::map & from_nodes, - core::map & light_sources, - core::map & modified_blocks); - - void unLightNeighbors(v3s16 pos, u8 lightwas, - core::map & light_sources, - core::map & modified_blocks); - - void spreadLight(core::map & from_nodes, - core::map & modified_blocks); - - void lightNeighbors(v3s16 pos, - core::map & modified_blocks); - - v3s16 getBrightestNeighbour(v3s16 p); - - s16 propagateSunlight(v3s16 start, - core::map & modified_blocks); - - void updateLighting(core::map & a_blocks, - core::map & modified_blocks); - /* - These handle lighting but not faces. + Misc. */ - void addNodeAndUpdate(v3s16 p, MapNode n, - core::map &modified_blocks); - void removeNodeAndUpdate(v3s16 p, - core::map &modified_blocks); - + std::map *getSectorsPtr(){return &m_sectors;} + /* - Updates the faces of the given block and blocks on the - leading edge. + Variables */ - void updateMeshes(v3s16 blockpos); - //core::aabbox3d getDisplayedBlockArea(); + void transforming_liquid_add(v3s16 p); + s32 transforming_liquid_size(); - //bool updateChangedVisibleArea(); - - virtual void save(bool only_changed){assert(0);}; + bool isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes); +protected: + friend class LuaVoxelManip; - /* - Updates usage timers - */ - void timerUpdate(float dtime); - - // Takes cache into account - // sector mutex should be locked when calling - void deleteSectors(core::list &list, bool only_blocks); - - // Returns count of deleted sectors - u32 deleteUnusedSectors(float timeout, bool only_blocks=false, - core::list *deleted_blocks=NULL); + std::ostream &m_dout; // A bit deprecated, could be removed - // For debug printing - virtual void PrintInfo(std::ostream &out); -}; + IGameDef *m_gamedef; -// Master heightmap parameters -struct HMParams -{ - HMParams() - { - blocksize = 64; - randmax = "constant 70.0"; - randfactor = "constant 0.6"; - base = "linear 0 80 0"; - } - s16 blocksize; - std::string randmax; - std::string randfactor; - std::string base; -}; + std::set m_event_receivers; -// Map parameters -struct MapParams -{ - MapParams() - { - plants_amount = 1.0; - ravines_amount = 1.0; - //max_objects_in_block = 30; - } - float plants_amount; - float ravines_amount; - //u16 max_objects_in_block; + std::map 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; + + // Queued transforming water nodes + UniqueQueue m_transforming_liquid; + + // This stores the properties of the nodes on the map. + INodeDefManager *m_nodedef; + + bool isOccluded(v3s16 p0, v3s16 p1, float step, float stepfac, + float start_off, float end_off, u32 needed_count); + +private: + f32 m_transforming_liquid_loop_count_multiplier = 1.0f; + u32 m_unprocessed_count = 0; + u64 m_inc_trending_up_start_time = 0; // milliseconds + bool m_queue_size_timer_started = false; }; +/* + ServerMap + + This is the only map class that is able to generate map. +*/ + class ServerMap : public Map { public: /* savedir: directory to which map data should be saved */ - ServerMap(std::string savedir, HMParams hmp, MapParams mp); + ServerMap(const std::string &savedir, IGameDef *gamedef, EmergeManager *emerge); ~ServerMap(); s32 mapType() const @@ -437,172 +344,166 @@ class ServerMap : public Map } /* - Forcefully get a sector from somewhere + Get a sector from somewhere. + - Check memory + - Check disk (doesn't load blocks) + - Create blank one + */ + MapSector *createSector(v2s16 p); + + bool saoPositionOverLimit(const v3f &p); + + /* + Blocks are generated by using these and makeBlock(). */ - MapSector * emergeSector(v2s16 p); + bool blockpos_over_mapgen_limit(v3s16 p); + bool initBlockMake(v3s16 blockpos, BlockMakeData *data); + void finishBlockMake(BlockMakeData *data, + std::map *changed_blocks); + + /* + Get a block from somewhere. + - Memory + - Create blank + */ + MapBlock *createBlock(v3s16 p); + /* Forcefully get a block from somewhere. + - Memory + - Load from disk + - Create blank filled with CONTENT_IGNORE - Exceptions: - - InvalidPositionException: possible if only_from_disk==true - - changed_blocks: - - All already existing blocks that were modified are added. - - If found on disk, nothing will be added. - - If generated, the new block will not be included. - - lighting_invalidated_blocks: - - All blocks that have heavy-to-calculate lighting changes - are added. - - updateLighting() should be called for these. - - - A block that is in changed_blocks may not be in - lighting_invalidated_blocks. */ - MapBlock * emergeBlock( - v3s16 p, - bool only_from_disk, - core::map &changed_blocks, - core::map &lighting_invalidated_blocks - ); - - void createDir(std::string path); - void createSaveDir(); - // returns something like "xxxxxxxx" - std::string getSectorSubDir(v2s16 pos); + MapBlock *emergeBlock(v3s16 p, bool create_blank=true); + + /* + Try to get a block. + If it does not exist in memory, add it to the emerge queue. + - Memory + - Emerge Queue (deferred disk or generate) + */ + MapBlock *getBlockOrEmerge(v3s16 p3d); + + // Helper for placing objects on ground level + s16 findGroundLevel(v2s16 p2d); + + /* + Misc. helper functions for fiddling with directory and file + names when saving + */ + void createDirs(std::string path); // returns something like "map/sectors/xxxxxxxx" - std::string getSectorDir(v2s16 pos); - std::string createSectorDir(v2s16 pos); + std::string getSectorDir(v2s16 pos, int layout = 2); // dirname: final directory name - v2s16 getSectorPos(std::string dirname); - v3s16 getBlockPos(std::string sectordir, std::string blockfile); - - void save(bool only_changed); - void loadAll(); - - void saveMasterHeightmap(); - void loadMasterHeightmap(); - - // The sector mutex should be locked when calling most of these - - // This only saves sector-specific data such as the heightmap - // (no MapBlocks) - void saveSectorMeta(ServerMapSector *sector); - MapSector* loadSectorMeta(std::string dirname); - - // Full load of a sector including all blocks. - // returns true on success, false on failure. - bool loadSectorFull(v2s16 p2d); - // If sector is not found in memory, try to load it from disk. - // Returns true if sector now resides in memory - //bool deFlushSector(v2s16 p2d); - - void saveBlock(MapBlock *block); - // This will generate a sector with getSector if not found. - void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector); + v2s16 getSectorPos(const std::string &dirname); + v3s16 getBlockPos(const std::string §ordir, const std::string &blockfile); + static std::string getBlockFilename(v3s16 p); - // Gets from master heightmap - void getSectorCorners(v2s16 p2d, s16 *corners); + /* + Database functions + */ + static MapDatabase *createDatabase(const std::string &name, const std::string &savedir, Settings &conf); - // For debug printing - virtual void PrintInfo(std::ostream &out); + // Returns true if the database file does not exist + bool loadFromFolders(); -private: - UnlimitedHeightmap *m_heightmap; - MapParams m_params; + // Call these before and after saving of blocks + void beginSave(); + void endSave(); - std::string m_savedir; - bool m_map_saving_enabled; -}; + void save(ModifiedState save_level); + void listAllLoadableBlocks(std::vector &dst); + void listAllLoadedBlocks(std::vector &dst); -class Client; + MapgenParams *getMapgenParams(); -class ClientMap : public Map, public scene::ISceneNode -{ -public: - ClientMap( - Client *client, - scene::ISceneNode* parent, - scene::ISceneManager* mgr, - s32 id - ); + bool saveBlock(MapBlock *block); + static bool saveBlock(MapBlock *block, MapDatabase *db); + // This will generate a sector with getSector if not found. + void loadBlock(const std::string §ordir, const std::string &blockfile, + MapSector *sector, bool save_after_load=false); + MapBlock* loadBlock(v3s16 p); + // Database version + void loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load=false); - ~ClientMap(); + bool deleteBlock(v3s16 blockpos); - s32 mapType() const - { - return MAPTYPE_CLIENT; - } + void updateVManip(v3s16 pos); - /* - Forcefully get a sector from somewhere - */ - MapSector * emergeSector(v2s16 p); + // For debug printing + virtual void PrintInfo(std::ostream &out); - void deSerializeSector(v2s16 p2d, std::istream &is); + bool isSavingEnabled(){ return m_map_saving_enabled; } - /* - ISceneNode methods - */ + u64 getSeed(); + s16 getWaterLevel(); - virtual void OnRegisterSceneNode(); + /*! + * Fixes lighting in one map block. + * May modify other blocks as well, as light can spread + * out of the specified block. + * Returns false if the block is not generated (so nothing + * changed), true otherwise. + */ + bool repairBlockLight(v3s16 blockpos, + std::map *modified_blocks); - virtual void render() - { - video::IVideoDriver* driver = SceneManager->getVideoDriver(); - driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); - renderMap(driver, SceneManager->getSceneNodeRenderPass()); - } - - virtual const core::aabbox3d& getBoundingBox() const - { - return m_box; - } + MapSettingsManager settings_mgr; - void renderMap(video::IVideoDriver* driver, s32 pass); +private: + // Emerge manager + EmergeManager *m_emerge; - // Update master heightmap mesh - void updateMesh(); + std::string m_savedir; + bool m_map_saving_enabled; - // For debug printing - virtual void PrintInfo(std::ostream &out); - -private: - Client *m_client; - - core::aabbox3d m_box; - - // This is the master heightmap mesh - scene::SMesh *mesh; - JMutex mesh_mutex; +#if 0 + // Chunk size in MapSectors + // If 0, chunks are disabled. + s16 m_chunksize; + // Chunks + core::map m_chunks; +#endif + + /* + Metadata is re-written on disk only if this is true. + This is reset to false when written on disk. + */ + bool m_map_metadata_changed = true; + MapDatabase *dbase = nullptr; }; -class MapVoxelManipulator : public VoxelManipulator + +#define VMANIP_BLOCK_DATA_INEXIST 1 +#define VMANIP_BLOCK_CONTAINS_CIGNORE 2 + +class MMVManip : public VoxelManipulator { public: - MapVoxelManipulator(Map *map); - virtual ~MapVoxelManipulator(); - + MMVManip(Map *map); + virtual ~MMVManip() = default; + virtual void clear() { VoxelManipulator::clear(); m_loaded_blocks.clear(); } - virtual void emerge(VoxelArea a, s32 caller_id=-1); + void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max, + bool load_if_inexistent = true); - void blitBack(core::map & modified_blocks); + // This is much faster with big chunks of generated data + void blitBackAll(std::map * modified_blocks, + bool overwrite_generated = true); -private: + bool m_is_dirty = false; + +protected: Map *m_map; /* - NOTE: This might be used or not - bool is dummy value - SUGG: How 'bout an another VoxelManipulator for storing the - information about which block is loaded? + key = blockpos + value = flags describing the block */ - core::map m_loaded_blocks; + std::map m_loaded_blocks; }; - -#endif -