New map directory structure that avoids map size being limited by filesystem
[oweals/minetest.git] / src / map.h
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifndef MAP_HEADER
21 #define MAP_HEADER
22
23 #include <jmutex.h>
24 #include <jthread.h>
25 #include <iostream>
26
27 #ifdef _WIN32
28         #include <windows.h>
29         #define sleep_s(x) Sleep((x*1000))
30 #else
31         #include <unistd.h>
32         #define sleep_s(x) sleep(x)
33 #endif
34
35 #include "common_irrlicht.h"
36 #include "mapnode.h"
37 #include "mapblock.h"
38 #include "mapsector.h"
39 #include "constants.h"
40 #include "voxel.h"
41 #include "mapchunk.h"
42 #include "nodemetadata.h"
43
44 #define MAPTYPE_BASE 0
45 #define MAPTYPE_SERVER 1
46 #define MAPTYPE_CLIENT 2
47
48 enum MapEditEventType{
49         MEET_ADDNODE,
50         MEET_REMOVENODE,
51         MEET_OTHER
52 };
53
54 struct MapEditEvent
55 {
56         MapEditEventType type;
57         v3s16 p;
58         MapNode n;
59         core::map<v3s16, bool> modified_blocks;
60         u16 already_known_by_peer;
61
62         MapEditEvent():
63                 type(MEET_OTHER),
64                 already_known_by_peer(0)
65         {
66         }
67         
68         MapEditEvent * clone()
69         {
70                 MapEditEvent *event = new MapEditEvent();
71                 event->type = type;
72                 event->p = p;
73                 event->n = n;
74                 for(core::map<v3s16, bool>::Iterator
75                                 i = modified_blocks.getIterator();
76                                 i.atEnd()==false; i++)
77                 {
78                         v3s16 p = i.getNode()->getKey();
79                         bool v = i.getNode()->getValue();
80                         event->modified_blocks.insert(p, v);
81                 }
82                 return event;
83         }
84 };
85
86 class MapEventReceiver
87 {
88 public:
89         // event shall be deleted by caller after the call.
90         virtual void onMapEditEvent(MapEditEvent *event) = 0;
91 };
92
93 class Map : public NodeContainer
94 {
95 public:
96
97         Map(std::ostream &dout);
98         virtual ~Map();
99
100         virtual u16 nodeContainerId() const
101         {
102                 return NODECONTAINER_ID_MAP;
103         }
104
105         virtual s32 mapType() const
106         {
107                 return MAPTYPE_BASE;
108         }
109         
110         /*
111                 Drop (client) or delete (server) the map.
112         */
113         virtual void drop()
114         {
115                 delete this;
116         }
117
118         void addEventReceiver(MapEventReceiver *event_receiver);
119         void removeEventReceiver(MapEventReceiver *event_receiver);
120         // event shall be deleted by caller after the call.
121         void dispatchEvent(MapEditEvent *event);
122
123         // On failure returns NULL
124         MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d);
125         // On failure returns NULL
126         MapSector * getSectorNoGenerateNoEx(v2s16 p2d);
127         // On failure throws InvalidPositionException
128         MapSector * getSectorNoGenerate(v2s16 p2d);
129         // Gets an existing sector or creates an empty one
130         //MapSector * getSectorCreate(v2s16 p2d);
131
132         /*
133                 This is overloaded by ClientMap and ServerMap to allow
134                 their differing fetch methods.
135         */
136         virtual MapSector * emergeSector(v2s16 p){ return NULL; }
137         virtual MapSector * emergeSector(v2s16 p,
138                         core::map<v3s16, MapBlock*> &changed_blocks){ return NULL; }
139
140         // Returns InvalidPositionException if not found
141         MapBlock * getBlockNoCreate(v3s16 p);
142         // Returns NULL if not found
143         MapBlock * getBlockNoCreateNoEx(v3s16 p);
144         // Gets an existing block or creates an empty one
145         //MapBlock * getBlockCreate(v3s16 p);
146         
147         // Returns InvalidPositionException if not found
148         bool isNodeUnderground(v3s16 p);
149         
150         // virtual from NodeContainer
151         bool isValidPosition(v3s16 p)
152         {
153                 v3s16 blockpos = getNodeBlockPos(p);
154                 MapBlock *blockref;
155                 try{
156                         blockref = getBlockNoCreate(blockpos);
157                 }
158                 catch(InvalidPositionException &e)
159                 {
160                         return false;
161                 }
162                 return true;
163                 /*v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
164                 bool is_valid = blockref->isValidPosition(relpos);
165                 return is_valid;*/
166         }
167         
168         // virtual from NodeContainer
169         // throws InvalidPositionException if not found
170         MapNode getNode(v3s16 p)
171         {
172                 v3s16 blockpos = getNodeBlockPos(p);
173                 MapBlock * blockref = getBlockNoCreate(blockpos);
174                 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
175
176                 return blockref->getNodeNoCheck(relpos);
177         }
178
179         // virtual from NodeContainer
180         // throws InvalidPositionException if not found
181         void setNode(v3s16 p, MapNode & n)
182         {
183                 v3s16 blockpos = getNodeBlockPos(p);
184                 MapBlock * blockref = getBlockNoCreate(blockpos);
185                 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
186                 blockref->setNodeNoCheck(relpos, n);
187         }
188         
189         // Returns a CONTENT_IGNORE node if not found
190         MapNode getNodeNoEx(v3s16 p)
191         {
192                 try{
193                         v3s16 blockpos = getNodeBlockPos(p);
194                         MapBlock * blockref = getBlockNoCreate(blockpos);
195                         v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
196
197                         return blockref->getNodeNoCheck(relpos);
198                 }
199                 catch(InvalidPositionException &e)
200                 {
201                         return MapNode(CONTENT_IGNORE);
202                 }
203         }
204
205         void unspreadLight(enum LightBank bank,
206                         core::map<v3s16, u8> & from_nodes,
207                         core::map<v3s16, bool> & light_sources,
208                         core::map<v3s16, MapBlock*> & modified_blocks);
209
210         void unLightNeighbors(enum LightBank bank,
211                         v3s16 pos, u8 lightwas,
212                         core::map<v3s16, bool> & light_sources,
213                         core::map<v3s16, MapBlock*> & modified_blocks);
214         
215         void spreadLight(enum LightBank bank,
216                         core::map<v3s16, bool> & from_nodes,
217                         core::map<v3s16, MapBlock*> & modified_blocks);
218         
219         void lightNeighbors(enum LightBank bank,
220                         v3s16 pos,
221                         core::map<v3s16, MapBlock*> & modified_blocks);
222
223         v3s16 getBrightestNeighbour(enum LightBank bank, v3s16 p);
224
225         s16 propagateSunlight(v3s16 start,
226                         core::map<v3s16, MapBlock*> & modified_blocks);
227         
228         void updateLighting(enum LightBank bank,
229                         core::map<v3s16, MapBlock*>  & a_blocks,
230                         core::map<v3s16, MapBlock*> & modified_blocks);
231                         
232         void updateLighting(core::map<v3s16, MapBlock*>  & a_blocks,
233                         core::map<v3s16, MapBlock*> & modified_blocks);
234                         
235         /*
236                 These handle lighting but not faces.
237         */
238         void addNodeAndUpdate(v3s16 p, MapNode n,
239                         core::map<v3s16, MapBlock*> &modified_blocks);
240         void removeNodeAndUpdate(v3s16 p,
241                         core::map<v3s16, MapBlock*> &modified_blocks);
242
243         /*
244                 Wrappers for the latter ones.
245                 These emit events.
246                 Return true if succeeded, false if not.
247         */
248         bool addNodeWithEvent(v3s16 p, MapNode n);
249         bool removeNodeWithEvent(v3s16 p);
250         
251         /*
252                 Takes the blocks at the edges into account
253         */
254         bool dayNightDiffed(v3s16 blockpos);
255
256         //core::aabbox3d<s16> getDisplayedBlockArea();
257
258         //bool updateChangedVisibleArea();
259         
260         virtual void save(bool only_changed){assert(0);};
261
262         /*
263                 Updates usage timers
264         */
265         void timerUpdate(float dtime);
266         
267         // Takes cache into account
268         // sector mutex should be locked when calling
269         void deleteSectors(core::list<v2s16> &list, bool only_blocks);
270         
271         // Returns count of deleted sectors
272         u32 deleteUnusedSectors(float timeout, bool only_blocks=false,
273                         core::list<v3s16> *deleted_blocks=NULL);
274
275         // For debug printing
276         virtual void PrintInfo(std::ostream &out);
277         
278         void transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks);
279
280         /*
281                 Node metadata
282                 These are basically coordinate wrappers to MapBlock
283         */
284         
285         NodeMetadata* getNodeMetadata(v3s16 p);
286         void setNodeMetadata(v3s16 p, NodeMetadata *meta);
287         void removeNodeMetadata(v3s16 p);
288         void nodeMetadataStep(float dtime,
289                         core::map<v3s16, MapBlock*> &changed_blocks);
290         
291         /*
292                 Misc.
293         */
294         core::map<v2s16, MapSector*> *getSectorsPtr(){return &m_sectors;}
295
296         /*
297                 Variables
298         */
299         
300 protected:
301
302         std::ostream &m_dout;
303
304         core::map<MapEventReceiver*, bool> m_event_receivers;
305         
306         core::map<v2s16, MapSector*> m_sectors;
307         //JMutex m_sector_mutex;
308
309         // Be sure to set this to NULL when the cached sector is deleted 
310         MapSector *m_sector_cache;
311         v2s16 m_sector_cache_p;
312
313         // Queued transforming water nodes
314         UniqueQueue<v3s16> m_transforming_liquid;
315 };
316
317 /*
318         ServerMap
319
320         This is the only map class that is able to generate map.
321 */
322
323 struct ChunkMakeData;
324
325 class ServerMap : public Map
326 {
327 public:
328         /*
329                 savedir: directory to which map data should be saved
330         */
331         ServerMap(std::string savedir);
332         ~ServerMap();
333
334         s32 mapType() const
335         {
336                 return MAPTYPE_SERVER;
337         }
338
339         /*
340                 Map generation
341         */
342         
343         // Returns the position of the chunk where the sector is in
344         v2s16 sector_to_chunk(v2s16 sectorpos)
345         {
346                 if(m_chunksize == 0)
347                         return v2s16(0,0);
348                 sectorpos.X += m_chunksize / 2;
349                 sectorpos.Y += m_chunksize / 2;
350                 v2s16 chunkpos = getContainerPos(sectorpos, m_chunksize);
351                 return chunkpos;
352         }
353         
354         // Returns the position of the (0,0) sector of the chunk
355         v2s16 chunk_to_sector(v2s16 chunkpos)
356         {
357                 if(m_chunksize == 0)
358                         return v2s16(0,0);
359                 v2s16 sectorpos(
360                         chunkpos.X * m_chunksize,
361                         chunkpos.Y * m_chunksize
362                 );
363                 sectorpos.X -= m_chunksize / 2;
364                 sectorpos.Y -= m_chunksize / 2;
365                 return sectorpos;
366         }
367
368         /*
369                 Get a chunk.
370         */
371         MapChunk *getChunk(v2s16 chunkpos)
372         {
373                 core::map<v2s16, MapChunk*>::Node *n;
374                 n = m_chunks.find(chunkpos);
375                 if(n == NULL)
376                         return NULL;
377                 return n->getValue();
378         }
379
380         /*
381                 True if the chunk and its neighbors are fully generated.
382                 It means the chunk will not be touched in the future by the
383                 generator. If false, generateChunk will make it true.
384         */
385         bool chunkNonVolatile(v2s16 chunkpos)
386         {
387                 if(m_chunksize == 0)
388                         return true;
389                 
390                 /*for(s16 x=-1; x<=1; x++)
391                 for(s16 y=-1; y<=1; y++)*/
392                 s16 x=0;
393                 s16 y=0;
394                 {
395                         v2s16 chunkpos0 = chunkpos + v2s16(x,y);
396                         MapChunk *chunk = getChunk(chunkpos);
397                         if(chunk == NULL)
398                                 return false;
399                         if(chunk->getGenLevel() != GENERATED_FULLY)
400                                 return false;
401                 }
402                 return true;
403         }
404         
405         /*
406                 Returns true if any chunk is marked as modified
407         */
408         bool anyChunkModified()
409         {
410                 for(core::map<v2s16, MapChunk*>::Iterator
411                                 i = m_chunks.getIterator();
412                                 i.atEnd()==false; i++)
413                 {
414                         v2s16 p = i.getNode()->getKey();
415                         MapChunk *chunk = i.getNode()->getValue();
416                         if(chunk->isModified())
417                                 return true;
418                 }
419                 return false;
420         }
421
422         void setChunksNonModified()
423         {
424                 for(core::map<v2s16, MapChunk*>::Iterator
425                                 i = m_chunks.getIterator();
426                                 i.atEnd()==false; i++)
427                 {
428                         v2s16 p = i.getNode()->getKey();
429                         MapChunk *chunk = i.getNode()->getValue();
430                         chunk->setModified(false);
431                 }
432         }
433
434         /*
435                 Chunks are generated by using these and makeChunk().
436         */
437         void initChunkMake(ChunkMakeData &data, v2s16 chunkpos);
438         MapChunk* finishChunkMake(ChunkMakeData &data,
439                         core::map<v3s16, MapBlock*> &changed_blocks);
440
441         /*
442                 Generate a chunk.
443
444                 All chunks touching this one can be altered also.
445         */
446         /*MapChunk* generateChunkRaw(v2s16 chunkpos,
447                         core::map<v3s16, MapBlock*> &changed_blocks,
448                         bool force=false);*/
449         
450         /*
451                 Generate a chunk and its neighbors so that it won't be touched
452                 anymore.
453         */
454         /*MapChunk* generateChunk(v2s16 chunkpos,
455                         core::map<v3s16, MapBlock*> &changed_blocks);*/
456         
457         /*
458                 Generate a sector.
459                 
460                 This is mainly called by generateChunkRaw.
461         */
462         //ServerMapSector * generateSector(v2s16 p);
463         
464         /*
465                 Get a sector from somewhere.
466                 - Check memory
467                 - Check disk (loads blocks also)
468                 - Create blank one
469         */
470         ServerMapSector * createSector(v2s16 p);
471
472         /*
473                 Get a sector from somewhere.
474                 - Check memory
475                 - Check disk (loads blocks also)
476                 - Generate chunk
477         */
478         /*MapSector * emergeSector(v2s16 p,
479                         core::map<v3s16, MapBlock*> &changed_blocks);*/
480         
481         /*MapSector * emergeSector(v2s16 p)
482         {
483                 core::map<v3s16, MapBlock*> changed_blocks;
484                 return emergeSector(p, changed_blocks);
485         }*/
486
487         MapBlock * generateBlock(
488                         v3s16 p,
489                         MapBlock *original_dummy,
490                         ServerMapSector *sector,
491                         core::map<v3s16, MapBlock*> &changed_blocks,
492                         core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
493         );
494         
495         /*
496                 Get a block from somewhere.
497                 - Memory
498                 - Create blank
499         */
500         MapBlock * createBlock(v3s16 p);
501         
502         /*
503                 only_from_disk, changed_blocks and lighting_invalidated_blocks
504                 are not properly used by the new map generator.
505         */
506         MapBlock * emergeBlock(
507                         v3s16 p,
508                         bool only_from_disk,
509                         core::map<v3s16, MapBlock*> &changed_blocks,
510                         core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
511         );
512
513 #if 0
514         /*
515                 Forcefully get a block from somewhere.
516
517                 Exceptions:
518                 - InvalidPositionException: possible if only_from_disk==true
519                 
520                 changed_blocks:
521                 - All already existing blocks that were modified are added.
522                         - If found on disk, nothing will be added.
523                         - If generated, the new block will not be included.
524
525                 lighting_invalidated_blocks:
526                 - All blocks that have heavy-to-calculate lighting changes
527                   are added.
528                         - updateLighting() should be called for these.
529                 
530                 - A block that is in changed_blocks may not be in
531                   lighting_invalidated_blocks.
532         */
533         MapBlock * emergeBlock(
534                         v3s16 p,
535                         bool only_from_disk,
536                         core::map<v3s16, MapBlock*> &changed_blocks,
537                         core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
538         );
539 #endif
540         
541         // Helper for placing objects on ground level
542         s16 findGroundLevel(v2s16 p2d);
543
544         /*
545                 Misc. helper functions for fiddling with directory and file
546                 names when saving
547         */
548         void createDirs(std::string path);
549         // returns something like "map/sectors/xxxxxxxx"
550         std::string getSectorDir(v2s16 pos, int layout = 2);
551         // dirname: final directory name
552         v2s16 getSectorPos(std::string dirname);
553         v3s16 getBlockPos(std::string sectordir, std::string blockfile);
554
555         void save(bool only_changed);
556         //void loadAll();
557         
558         // Saves map seed and possibly other stuff
559         void saveMapMeta();
560         void loadMapMeta();
561         
562         void saveChunkMeta();
563         void loadChunkMeta();
564         
565         // The sector mutex should be locked when calling most of these
566         
567         // This only saves sector-specific data such as the heightmap
568         // (no MapBlocks)
569         // DEPRECATED? Sectors have no metadata anymore.
570         void saveSectorMeta(ServerMapSector *sector);
571         MapSector* loadSectorMeta(std::string dirname, bool save_after_load);
572         
573         // Full load of a sector including all blocks.
574         // returns true on success, false on failure.
575         bool loadSectorFull(v2s16 p2d);
576         // If sector is not found in memory, try to load it from disk.
577         // Returns true if sector now resides in memory
578         //bool deFlushSector(v2s16 p2d);
579         
580         void saveBlock(MapBlock *block);
581         // This will generate a sector with getSector if not found.
582         void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load=false);
583
584         // For debug printing
585         virtual void PrintInfo(std::ostream &out);
586
587         bool isSavingEnabled(){ return m_map_saving_enabled; }
588
589 private:
590         // Seed used for all kinds of randomness
591         u64 m_seed;
592
593         std::string m_savedir;
594         bool m_map_saving_enabled;
595
596         // Chunk size in MapSectors
597         // If 0, chunks are disabled.
598         s16 m_chunksize;
599         // Chunks
600         core::map<v2s16, MapChunk*> m_chunks;
601
602         /*
603                 Metadata is re-written on disk only if this is true.
604                 This is reset to false when written on disk.
605         */
606         bool m_map_metadata_changed;
607 };
608
609 /*
610         ClientMap stuff
611 */
612
613 #ifndef SERVER
614
615 struct MapDrawControl
616 {
617         MapDrawControl():
618                 range_all(false),
619                 wanted_range(50),
620                 wanted_max_blocks(0),
621                 wanted_min_range(0),
622                 blocks_drawn(0),
623                 blocks_would_have_drawn(0)
624         {
625         }
626         // Overrides limits by drawing everything
627         bool range_all;
628         // Wanted drawing range
629         float wanted_range;
630         // Maximum number of blocks to draw
631         u32 wanted_max_blocks;
632         // Blocks in this range are drawn regardless of number of blocks drawn
633         float wanted_min_range;
634         // Number of blocks rendered is written here by the renderer
635         u32 blocks_drawn;
636         // Number of blocks that would have been drawn in wanted_range
637         u32 blocks_would_have_drawn;
638 };
639
640 class Client;
641
642 /*
643         ClientMap
644         
645         This is the only map class that is able to render itself on screen.
646 */
647
648 class ClientMap : public Map, public scene::ISceneNode
649 {
650 public:
651         ClientMap(
652                         Client *client,
653                         MapDrawControl &control,
654                         scene::ISceneNode* parent,
655                         scene::ISceneManager* mgr,
656                         s32 id
657         );
658
659         ~ClientMap();
660
661         s32 mapType() const
662         {
663                 return MAPTYPE_CLIENT;
664         }
665
666         void drop()
667         {
668                 ISceneNode::drop();
669         }
670
671         void updateCamera(v3f pos, v3f dir)
672         {
673                 JMutexAutoLock lock(m_camera_mutex);
674                 m_camera_position = pos;
675                 m_camera_direction = dir;
676         }
677
678         /*
679                 Forcefully get a sector from somewhere
680         */
681         MapSector * emergeSector(v2s16 p);
682
683         void deSerializeSector(v2s16 p2d, std::istream &is);
684
685         /*
686                 ISceneNode methods
687         */
688
689         virtual void OnRegisterSceneNode();
690
691         virtual void render()
692         {
693                 video::IVideoDriver* driver = SceneManager->getVideoDriver();
694                 driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
695                 renderMap(driver, SceneManager->getSceneNodeRenderPass());
696         }
697         
698         virtual const core::aabbox3d<f32>& getBoundingBox() const
699         {
700                 return m_box;
701         }
702
703         void renderMap(video::IVideoDriver* driver, s32 pass);
704
705         /*
706                 Methods for setting temporary modifications to nodes for
707                 drawing.
708
709                 Returns true if something changed.
710                 
711                 All blocks whose mesh could have been changed are inserted
712                 to affected_blocks.
713         */
714         bool setTempMod(v3s16 p, NodeMod mod,
715                         core::map<v3s16, MapBlock*> *affected_blocks=NULL);
716         bool clearTempMod(v3s16 p,
717                         core::map<v3s16, MapBlock*> *affected_blocks=NULL);
718         // Efficient implementation needs a cache of TempMods
719         //void clearTempMods();
720
721         void expireMeshes(bool only_daynight_diffed);
722         
723         /*
724                 Update the faces of the given block and blocks on the
725                 leading edge.
726         */
727         void updateMeshes(v3s16 blockpos, u32 daynight_ratio);
728         
729         // Update meshes that touch the node
730         //void updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio);
731
732         // For debug printing
733         virtual void PrintInfo(std::ostream &out);
734         
735 private:
736         Client *m_client;
737         
738         core::aabbox3d<f32> m_box;
739         
740         // This is the master heightmap mesh
741         //scene::SMesh *mesh;
742         //JMutex mesh_mutex;
743         
744         MapDrawControl &m_control;
745
746         v3f m_camera_position;
747         v3f m_camera_direction;
748         JMutex m_camera_mutex;
749
750 };
751
752 #endif
753
754 class MapVoxelManipulator : public VoxelManipulator
755 {
756 public:
757         MapVoxelManipulator(Map *map);
758         virtual ~MapVoxelManipulator();
759         
760         virtual void clear()
761         {
762                 VoxelManipulator::clear();
763                 m_loaded_blocks.clear();
764         }
765
766         virtual void emerge(VoxelArea a, s32 caller_id=-1);
767
768         void blitBack(core::map<v3s16, MapBlock*> & modified_blocks);
769
770 protected:
771         Map *m_map;
772         /*
773                 key = blockpos
774                 value = block existed when loaded
775         */
776         core::map<v3s16, bool> m_loaded_blocks;
777 };
778
779 class ManualMapVoxelManipulator : public MapVoxelManipulator
780 {
781 public:
782         ManualMapVoxelManipulator(Map *map);
783         virtual ~ManualMapVoxelManipulator();
784
785         void setMap(Map *map)
786         {m_map = map;}
787         
788         virtual void emerge(VoxelArea a, s32 caller_id=-1);
789
790         void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max);
791         
792         // This is much faster with big chunks of generated data
793         void blitBackAll(core::map<v3s16, MapBlock*> * modified_blocks);
794
795 protected:
796         bool m_create_area;
797 };
798
799 struct ChunkMakeData
800 {
801         bool no_op;
802         ManualMapVoxelManipulator vmanip;
803         u64 seed;
804         v2s16 chunkpos;
805         s16 y_blocks_min;
806         s16 y_blocks_max;
807         v2s16 sectorpos_base;
808         s16 sectorpos_base_size;
809         v2s16 sectorpos_bigbase;
810         s16 sectorpos_bigbase_size;
811         s16 max_spread_amount;
812         UniqueQueue<v3s16> transforming_liquid;
813
814         ChunkMakeData():
815                 no_op(false),
816                 vmanip(NULL),
817                 seed(0)
818         {}
819 };
820
821 void makeChunk(ChunkMakeData *data);
822
823 #endif
824