#include <jmutex.h>
#include <jthread.h>
#include <iostream>
-#include <malloc.h>
#ifdef _WIN32
#include <windows.h>
#endif
#include "common_irrlicht.h"
-#include "heightmap.h"
#include "mapnode.h"
#include "mapblock.h"
#include "mapsector.h"
#include "constants.h"
#include "voxel.h"
+#include "mapchunk.h"
+#include "nodemetadata.h"
-class Map;
+#define MAPTYPE_BASE 0
+#define MAPTYPE_SERVER 1
+#define MAPTYPE_CLIENT 2
-#if 0
-/*
- A cache for short-term fast access to map data
+enum MapEditEventType{
+ MEET_ADDNODE,
+ MEET_REMOVENODE,
+ MEET_OTHER
+};
- 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
-*/
-class MapBlockPointerCache : public NodeContainer
+struct MapEditEvent
{
-public:
- MapBlockPointerCache(Map *map);
- ~MapBlockPointerCache();
-
- virtual u16 nodeContainerId() const
+ MapEditEventType type;
+ v3s16 p;
+ MapNode n;
+ core::map<v3s16, bool> modified_blocks;
+ u16 already_known_by_peer;
+
+ MapEditEvent():
+ type(MEET_OTHER),
+ already_known_by_peer(0)
{
- return NODECONTAINER_ID_MAPBLOCKCACHE;
}
-
- MapBlock * getBlockNoCreate(v3s16 p);
-
- // virtual from NodeContainer
- bool isValidPosition(v3s16 p)
+
+ MapEditEvent * clone()
{
- v3s16 blockpos = getNodeBlockPos(p);
- MapBlock *blockref;
- try{
- blockref = getBlockNoCreate(blockpos);
- }
- catch(InvalidPositionException &e)
+ MapEditEvent *event = new MapEditEvent();
+ event->type = type;
+ event->p = p;
+ event->n = n;
+ for(core::map<v3s16, bool>::Iterator
+ i = modified_blocks.getIterator();
+ i.atEnd()==false; i++)
{
- return false;
+ v3s16 p = i.getNode()->getKey();
+ bool v = i.getNode()->getValue();
+ event->modified_blocks.insert(p, v);
}
- 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 event;
}
-
- core::map<v3s16, MapBlock*> m_modified_blocks;
-
-private:
- Map *m_map;
- core::map<v3s16, MapBlock*> m_blocks;
-
- u32 m_from_cache_count;
- u32 m_from_map_count;
};
-#endif
-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"<<std::endl;
- JMutexAutoLock waitcachelock(m_waitcache_mutex);
- JMutexAutoLock countlock(m_count_mutex);
-
- // If this is the first cache, grab the cache lock
- if(m_count == 0)
- m_cache_mutex.Lock();
-
- m_count++;
-
- //dstream<<"cacheCreated() end"<<std::endl;
- }
-
- void cacheRemoved()
- {
- //dstream<<"cacheRemoved() begin"<<std::endl;
- JMutexAutoLock countlock(m_count_mutex);
-
- assert(m_count > 0);
-
- m_count--;
-
- // If this is the last one, release the cache lock
- if(m_count == 0)
- m_cache_mutex.Unlock();
-
- //dstream<<"cacheRemoved() end"<<std::endl;
- }
-
- /*
- This lock should be taken when removing stuff that can be
- pointed by the cache.
-
- You'll want to grab this in a SharedPtr.
- */
- JMutexAutoLock * waitCaches()
- {
- //dstream<<"waitCaches() begin"<<std::endl;
- JMutexAutoLock waitcachelock(m_waitcache_mutex);
- JMutexAutoLock *lock = new JMutexAutoLock(m_cache_mutex);
- //dstream<<"waitCaches() end"<<std::endl;
- return lock;
- }
-
-private:
- // Count of existing caches
- u32 m_count;
- JMutex m_count_mutex;
- // This is locked always when there are some caches
- JMutex m_cache_mutex;
- // Locked so that when waitCaches() is called, no more caches are created
- JMutex m_waitcache_mutex;
+ // event shall be deleted by caller after the call.
+ virtual void onMapEditEvent(MapEditEvent *event) = 0;
};
-#define MAPTYPE_BASE 0
-#define MAPTYPE_SERVER 1
-#define MAPTYPE_CLIENT 2
-
-class Map : public NodeContainer, public Heightmappish
+class Map : public NodeContainer
{
-protected:
-
- std::ostream &m_dout;
-
- core::map<v2s16, MapSector*> m_sectors;
- JMutex m_sector_mutex;
-
- v3f m_camera_position;
- v3f m_camera_direction;
- JMutex m_camera_mutex;
-
- // Be sure to set this to NULL when the cached sector is deleted
- MapSector *m_sector_cache;
- v2s16 m_sector_cache_p;
-
- WrapperHeightmap m_hwrapper;
-
public:
- v3s16 drawoffset; // for drawbox()
-
- /*
- Used by MapBlockPointerCache.
-
- waitCaches() can be called to remove all caches before continuing
- */
- CacheLock m_blockcachelock;
-
Map(std::ostream &dout);
virtual ~Map();
{
return MAPTYPE_BASE;
}
-
+
+ /*
+ Drop (client) or delete (server) the map.
+ */
virtual void drop()
{
delete this;
}
- void updateCamera(v3f pos, v3f dir)
- {
- JMutexAutoLock lock(m_camera_mutex);
- m_camera_position = pos;
- m_camera_direction = dir;
- }
-
- /*void StartUpdater()
- {
- updater.Start();
- }
-
- void StopUpdater()
- {
- updater.setRun(false);
- while(updater.IsRunning())
- sleep_s(1);
- }
-
- bool UpdaterIsRunning()
- {
- return updater.IsRunning();
- }*/
+ void addEventReceiver(MapEventReceiver *event_receiver);
+ void removeEventReceiver(MapEventReceiver *event_receiver);
+ // event shall be deleted by caller after the call.
+ void dispatchEvent(MapEditEvent *event);
- static core::aabbox3d<f32> getNodeBox(v3s16 p)
- {
- return core::aabbox3d<f32>(
- (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
- );
- }
-
- //bool sectorExists(v2s16 p);
+ // On failure returns NULL
+ MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d);
+ // On failure returns NULL
+ MapSector * getSectorNoGenerateNoEx(v2s16 p2d);
+ // On failure throws InvalidPositionException
MapSector * getSectorNoGenerate(v2s16 p2d);
+ // Gets an existing sector or creates an empty one
+ //MapSector * getSectorCreate(v2s16 p2d);
+
/*
This is overloaded by ClientMap and ServerMap to allow
their differing fetch methods.
*/
- virtual MapSector * emergeSector(v2s16 p) = 0;
-
+ virtual MapSector * emergeSector(v2s16 p){ return NULL; }
+ virtual MapSector * emergeSector(v2s16 p,
+ core::map<v3s16, MapBlock*> &changed_blocks){ return NULL; }
+
// Returns InvalidPositionException if not found
MapBlock * getBlockNoCreate(v3s16 p);
// Returns NULL if not found
MapBlock * getBlockNoCreateNoEx(v3s16 p);
+ // Gets an existing block or creates an empty one
+ //MapBlock * getBlockCreate(v3s16 p);
- // 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);
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
blockref->setNodeNoCheck(relpos, n);
}
-
- /*MapNode getNodeGenerate(v3s16 p)
+
+ // Returns a CONTENT_IGNORE node if not found
+ MapNode getNodeNoEx(v3s16 p)
{
- v3s16 blockpos = getNodeBlockPos(p);
- MapBlock * blockref = getBlock(blockpos);
- v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
-
- return blockref->getNode(relpos);
- }*/
+ try{
+ v3s16 blockpos = getNodeBlockPos(p);
+ MapBlock * blockref = getBlockNoCreate(blockpos);
+ v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
- /*void setNodeGenerate(v3s16 p, MapNode & n)
- {
- v3s16 blockpos = getNodeBlockPos(p);
- MapBlock * blockref = getBlock(blockpos);
- v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
- blockref->setNode(relpos, n);
- }*/
+ return blockref->getNodeNoCheck(relpos);
+ }
+ catch(InvalidPositionException &e)
+ {
+ return MapNode(CONTENT_IGNORE);
+ }
+ }
void unspreadLight(enum LightBank bank,
core::map<v3s16, u8> & from_nodes,
core::map<v3s16, MapBlock*> &modified_blocks);
void removeNodeAndUpdate(v3s16 p,
core::map<v3s16, MapBlock*> &modified_blocks);
-
-#ifndef SERVER
- void expireMeshes(bool only_daynight_diffed);
-
+
/*
- Updates the faces of the given block and blocks on the
- leading edge.
+ Wrappers for the latter ones.
+ These emit events.
+ Return true if succeeded, false if not.
*/
- void updateMeshes(v3s16 blockpos, u32 daynight_ratio);
-#endif
-
+ bool addNodeWithEvent(v3s16 p, MapNode n);
+ bool removeNodeWithEvent(v3s16 p);
+
/*
Takes the blocks at the edges into account
*/
// For debug printing
virtual void PrintInfo(std::ostream &out);
-};
+
+ void transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks);
-// 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;
-};
+ /*
+ Node metadata
+ These are basically coordinate wrappers to MapBlock
+ */
+
+ NodeMetadata* getNodeMetadata(v3s16 p);
+ void setNodeMetadata(v3s16 p, NodeMetadata *meta);
+ void removeNodeMetadata(v3s16 p);
+ void nodeMetadataStep(float dtime,
+ core::map<v3s16, MapBlock*> &changed_blocks);
+
+ /*
+ Misc.
+ */
+ core::map<v2s16, MapSector*> *getSectorsPtr(){return &m_sectors;}
-// 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;
+ /*
+ Variables
+ */
+
+protected:
+
+ std::ostream &m_dout;
+
+ core::map<MapEventReceiver*, bool> m_event_receivers;
+
+ core::map<v2s16, MapSector*> m_sectors;
+ //JMutex m_sector_mutex;
+
+ // Be sure to set this to NULL when the cached sector is deleted
+ MapSector *m_sector_cache;
+ v2s16 m_sector_cache_p;
+
+ // Queued transforming water nodes
+ UniqueQueue<v3s16> m_transforming_liquid;
};
+/*
+ ServerMap
+
+ This is the only map class that is able to generate map.
+*/
+
+struct ChunkMakeData;
+
class ServerMap : public Map
{
public:
/*
savedir: directory to which map data should be saved
*/
- ServerMap(std::string savedir, HMParams hmp, MapParams mp);
+ ServerMap(std::string savedir);
~ServerMap();
s32 mapType() const
}
/*
- Forcefully get a sector from somewhere
+ Map generation
*/
- MapSector * emergeSector(v2s16 p);
+
+ // Returns the position of the chunk where the sector is in
+ v2s16 sector_to_chunk(v2s16 sectorpos)
+ {
+ if(m_chunksize == 0)
+ return v2s16(0,0);
+ sectorpos.X += m_chunksize / 2;
+ sectorpos.Y += m_chunksize / 2;
+ v2s16 chunkpos = getContainerPos(sectorpos, m_chunksize);
+ return chunkpos;
+ }
+
+ // Returns the position of the (0,0) sector of the chunk
+ v2s16 chunk_to_sector(v2s16 chunkpos)
+ {
+ if(m_chunksize == 0)
+ return v2s16(0,0);
+ v2s16 sectorpos(
+ chunkpos.X * m_chunksize,
+ chunkpos.Y * m_chunksize
+ );
+ sectorpos.X -= m_chunksize / 2;
+ sectorpos.Y -= m_chunksize / 2;
+ return sectorpos;
+ }
+
+ /*
+ Get a chunk.
+ */
+ MapChunk *getChunk(v2s16 chunkpos)
+ {
+ core::map<v2s16, MapChunk*>::Node *n;
+ n = m_chunks.find(chunkpos);
+ if(n == NULL)
+ return NULL;
+ return n->getValue();
+ }
+
+ /*
+ True if the chunk and its neighbors are fully generated.
+ It means the chunk will not be touched in the future by the
+ generator. If false, generateChunk will make it true.
+ */
+ bool chunkNonVolatile(v2s16 chunkpos)
+ {
+ if(m_chunksize == 0)
+ return true;
+
+ /*for(s16 x=-1; x<=1; x++)
+ for(s16 y=-1; y<=1; y++)*/
+ s16 x=0;
+ s16 y=0;
+ {
+ v2s16 chunkpos0 = chunkpos + v2s16(x,y);
+ MapChunk *chunk = getChunk(chunkpos);
+ if(chunk == NULL)
+ return false;
+ if(chunk->getGenLevel() != GENERATED_FULLY)
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ Returns true if any chunk is marked as modified
+ */
+ bool anyChunkModified()
+ {
+ for(core::map<v2s16, MapChunk*>::Iterator
+ i = m_chunks.getIterator();
+ i.atEnd()==false; i++)
+ {
+ v2s16 p = i.getNode()->getKey();
+ MapChunk *chunk = i.getNode()->getValue();
+ if(chunk->isModified())
+ return true;
+ }
+ return false;
+ }
+
+ void setChunksNonModified()
+ {
+ for(core::map<v2s16, MapChunk*>::Iterator
+ i = m_chunks.getIterator();
+ i.atEnd()==false; i++)
+ {
+ v2s16 p = i.getNode()->getKey();
+ MapChunk *chunk = i.getNode()->getValue();
+ chunk->setModified(false);
+ }
+ }
+
+ /*
+ Chunks are generated by using these and makeChunk().
+ */
+ void initChunkMake(ChunkMakeData &data, v2s16 chunkpos);
+ MapChunk* finishChunkMake(ChunkMakeData &data,
+ core::map<v3s16, MapBlock*> &changed_blocks);
+
+ /*
+ Generate a chunk.
+
+ All chunks touching this one can be altered also.
+ */
+ /*MapChunk* generateChunkRaw(v2s16 chunkpos,
+ core::map<v3s16, MapBlock*> &changed_blocks,
+ bool force=false);*/
+
+ /*
+ Generate a chunk and its neighbors so that it won't be touched
+ anymore.
+ */
+ /*MapChunk* generateChunk(v2s16 chunkpos,
+ core::map<v3s16, MapBlock*> &changed_blocks);*/
+
+ /*
+ Generate a sector.
+
+ This is mainly called by generateChunkRaw.
+ */
+ //ServerMapSector * generateSector(v2s16 p);
+
+ /*
+ Get a sector from somewhere.
+ - Check memory
+ - Check disk (loads blocks also)
+ - Create blank one
+ */
+ ServerMapSector * createSector(v2s16 p);
+
+ /*
+ Get a sector from somewhere.
+ - Check memory
+ - Check disk (loads blocks also)
+ - Generate chunk
+ */
+ /*MapSector * emergeSector(v2s16 p,
+ core::map<v3s16, MapBlock*> &changed_blocks);*/
+
+ /*MapSector * emergeSector(v2s16 p)
+ {
+ core::map<v3s16, MapBlock*> changed_blocks;
+ return emergeSector(p, changed_blocks);
+ }*/
+
+ MapBlock * generateBlock(
+ v3s16 p,
+ MapBlock *original_dummy,
+ ServerMapSector *sector,
+ core::map<v3s16, MapBlock*> &changed_blocks,
+ core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
+ );
+
+ /*
+ Get a block from somewhere.
+ - Memory
+ - Create blank
+ */
+ MapBlock * createBlock(v3s16 p);
+
+ /*
+ only_from_disk, changed_blocks and lighting_invalidated_blocks
+ are not properly used by the new map generator.
+ */
+ MapBlock * emergeBlock(
+ v3s16 p,
+ bool only_from_disk,
+ core::map<v3s16, MapBlock*> &changed_blocks,
+ core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
+ );
+
+#if 0
/*
Forcefully get a block from somewhere.
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
);
+#endif
+
+ // Helper for placing objects on ground level
+ s16 findGroundLevel(v2s16 p2d);
+ /*
+ Misc. helper functions for fiddling with directory and file
+ names when saving
+ */
void createDir(std::string path);
void createSaveDir();
// returns something like "xxxxxxxx"
v3s16 getBlockPos(std::string sectordir, std::string blockfile);
void save(bool only_changed);
- void loadAll();
-
- void saveMasterHeightmap();
- void loadMasterHeightmap();
-
+ //void loadAll();
+
+ // Saves map seed and possibly other stuff
+ void saveMapMeta();
+ void loadMapMeta();
+
+ void saveChunkMeta();
+ void loadChunkMeta();
+
// The sector mutex should be locked when calling most of these
// This only saves sector-specific data such as the heightmap
// (no MapBlocks)
+ // DEPRECATED? Sectors have no metadata anymore.
void saveSectorMeta(ServerMapSector *sector);
MapSector* loadSectorMeta(std::string dirname);
// This will generate a sector with getSector if not found.
void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector);
- // Gets from master heightmap
- void getSectorCorners(v2s16 p2d, s16 *corners);
-
// For debug printing
virtual void PrintInfo(std::ostream &out);
+ bool isSavingEnabled(){ return m_map_saving_enabled; }
+
private:
- UnlimitedHeightmap *m_heightmap;
- MapParams m_params;
+ // Seed used for all kinds of randomness
+ u64 m_seed;
std::string m_savedir;
bool m_map_saving_enabled;
+
+ // Chunk size in MapSectors
+ // If 0, chunks are disabled.
+ s16 m_chunksize;
+ // Chunks
+ core::map<v2s16, MapChunk*> m_chunks;
+
+ /*
+ 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;
};
+/*
+ ClientMap stuff
+*/
+
#ifndef SERVER
+struct MapDrawControl
+{
+ MapDrawControl():
+ range_all(false),
+ wanted_range(50),
+ wanted_max_blocks(0),
+ wanted_min_range(0),
+ blocks_drawn(0),
+ blocks_would_have_drawn(0)
+ {
+ }
+ // Overrides limits by drawing everything
+ bool range_all;
+ // Wanted drawing range
+ float wanted_range;
+ // Maximum number of blocks to draw
+ u32 wanted_max_blocks;
+ // Blocks in this range are drawn regardless of number of blocks drawn
+ float wanted_min_range;
+ // Number of blocks rendered is written here by the renderer
+ u32 blocks_drawn;
+ // Number of blocks that would have been drawn in wanted_range
+ u32 blocks_would_have_drawn;
+};
+
class Client;
+/*
+ ClientMap
+
+ This is the only map class that is able to render itself on screen.
+*/
+
class ClientMap : public Map, public scene::ISceneNode
{
public:
ClientMap(
Client *client,
- JMutex &range_mutex,
- float &viewing_range_nodes,
- bool &viewing_range_all,
+ MapDrawControl &control,
scene::ISceneNode* parent,
scene::ISceneManager* mgr,
s32 id
ISceneNode::drop();
}
+ void updateCamera(v3f pos, v3f dir)
+ {
+ JMutexAutoLock lock(m_camera_mutex);
+ m_camera_position = pos;
+ m_camera_direction = dir;
+ }
+
/*
Forcefully get a sector from somewhere
*/
/*
Methods for setting temporary modifications to nodes for
drawing.
- Return value is position of changed block.
+
+ Returns true if something changed.
+
+ All blocks whose mesh could have been changed are inserted
+ to affected_blocks.
*/
- v3s16 setTempMod(v3s16 p, NodeMod mod);
- v3s16 clearTempMod(v3s16 p);
+ bool setTempMod(v3s16 p, NodeMod mod,
+ core::map<v3s16, MapBlock*> *affected_blocks=NULL);
+ bool clearTempMod(v3s16 p,
+ core::map<v3s16, MapBlock*> *affected_blocks=NULL);
// Efficient implementation needs a cache of TempMods
//void clearTempMods();
+ void expireMeshes(bool only_daynight_diffed);
+
+ /*
+ Update the faces of the given block and blocks on the
+ leading edge.
+ */
+ void updateMeshes(v3s16 blockpos, u32 daynight_ratio);
+
+ // Update meshes that touch the node
+ //void updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio);
+
// For debug printing
virtual void PrintInfo(std::ostream &out);
core::aabbox3d<f32> m_box;
// This is the master heightmap mesh
- scene::SMesh *mesh;
- JMutex mesh_mutex;
+ //scene::SMesh *mesh;
+ //JMutex mesh_mutex;
+
+ MapDrawControl &m_control;
+
+ v3f m_camera_position;
+ v3f m_camera_direction;
+ JMutex m_camera_mutex;
- JMutex &m_range_mutex;
- float &m_viewing_range_nodes;
- bool &m_viewing_range_all;
};
#endif
void blitBack(core::map<v3s16, MapBlock*> & modified_blocks);
-private:
+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 = block existed when loaded
*/
core::map<v3s16, bool> m_loaded_blocks;
};
+class ManualMapVoxelManipulator : public MapVoxelManipulator
+{
+public:
+ ManualMapVoxelManipulator(Map *map);
+ virtual ~ManualMapVoxelManipulator();
+
+ void setMap(Map *map)
+ {m_map = map;}
+
+ virtual void emerge(VoxelArea a, s32 caller_id=-1);
+
+ void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max);
+
+ // This is much faster with big chunks of generated data
+ void blitBackAll(core::map<v3s16, MapBlock*> * modified_blocks);
+
+protected:
+ bool m_create_area;
+};
+
+struct ChunkMakeData
+{
+ bool no_op;
+ ManualMapVoxelManipulator vmanip;
+ u64 seed;
+ v2s16 chunkpos;
+ s16 y_blocks_min;
+ s16 y_blocks_max;
+ v2s16 sectorpos_base;
+ s16 sectorpos_base_size;
+ v2s16 sectorpos_bigbase;
+ s16 sectorpos_bigbase_size;
+ s16 max_spread_amount;
+ UniqueQueue<v3s16> transforming_liquid;
+
+ ChunkMakeData():
+ no_op(false),
+ vmanip(NULL),
+ seed(0)
+ {}
+};
+
+void makeChunk(ChunkMakeData *data);
+
#endif