3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
21 #include "mapsector.h"
30 #include "nodemetadata.h"
36 SQLite format specification:
37 - Initially only replaces sectors/ and sectors2/
44 Map::Map(std::ostream &dout):
48 /*m_sector_mutex.Init();
49 assert(m_sector_mutex.IsInitialized());*/
57 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
58 for(; i.atEnd() == false; i++)
60 MapSector *sector = i.getNode()->getValue();
65 void Map::addEventReceiver(MapEventReceiver *event_receiver)
67 m_event_receivers.insert(event_receiver, false);
70 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
72 if(m_event_receivers.find(event_receiver) == NULL)
74 m_event_receivers.remove(event_receiver);
77 void Map::dispatchEvent(MapEditEvent *event)
79 for(core::map<MapEventReceiver*, bool>::Iterator
80 i = m_event_receivers.getIterator();
81 i.atEnd()==false; i++)
83 MapEventReceiver* event_receiver = i.getNode()->getKey();
84 event_receiver->onMapEditEvent(event);
88 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
90 if(m_sector_cache != NULL && p == m_sector_cache_p){
91 MapSector * sector = m_sector_cache;
95 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
100 MapSector *sector = n->getValue();
102 // Cache the last result
103 m_sector_cache_p = p;
104 m_sector_cache = sector;
109 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
111 return getSectorNoGenerateNoExNoLock(p);
114 MapSector * Map::getSectorNoGenerate(v2s16 p)
116 MapSector *sector = getSectorNoGenerateNoEx(p);
118 throw InvalidPositionException();
123 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
125 v2s16 p2d(p3d.X, p3d.Z);
126 MapSector * sector = getSectorNoGenerateNoEx(p2d);
129 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
133 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
135 MapBlock *block = getBlockNoCreateNoEx(p3d);
137 throw InvalidPositionException();
141 bool Map::isNodeUnderground(v3s16 p)
143 v3s16 blockpos = getNodeBlockPos(p);
145 MapBlock * block = getBlockNoCreate(blockpos);
146 return block->getIsUnderground();
148 catch(InvalidPositionException &e)
154 bool Map::isValidPosition(v3s16 p)
156 v3s16 blockpos = getNodeBlockPos(p);
157 MapBlock *block = getBlockNoCreate(blockpos);
158 return (block != NULL);
161 // Returns a CONTENT_IGNORE node if not found
162 MapNode Map::getNodeNoEx(v3s16 p)
164 v3s16 blockpos = getNodeBlockPos(p);
165 MapBlock *block = getBlockNoCreateNoEx(blockpos);
167 return MapNode(CONTENT_IGNORE);
168 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
169 return block->getNodeNoCheck(relpos);
172 // throws InvalidPositionException if not found
173 MapNode Map::getNode(v3s16 p)
175 v3s16 blockpos = getNodeBlockPos(p);
176 MapBlock *block = getBlockNoCreateNoEx(blockpos);
178 throw InvalidPositionException();
179 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
180 return block->getNodeNoCheck(relpos);
183 // throws InvalidPositionException if not found
184 void Map::setNode(v3s16 p, MapNode & n)
186 v3s16 blockpos = getNodeBlockPos(p);
187 MapBlock *block = getBlockNoCreate(blockpos);
188 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
189 block->setNodeNoCheck(relpos, n);
194 Goes recursively through the neighbours of the node.
196 Alters only transparent nodes.
198 If the lighting of the neighbour is lower than the lighting of
199 the node was (before changing it to 0 at the step before), the
200 lighting of the neighbour is set to 0 and then the same stuff
201 repeats for the neighbour.
203 The ending nodes of the routine are stored in light_sources.
204 This is useful when a light is removed. In such case, this
205 routine can be called for the light node and then again for
206 light_sources to re-light the area without the removed light.
208 values of from_nodes are lighting values.
210 void Map::unspreadLight(enum LightBank bank,
211 core::map<v3s16, u8> & from_nodes,
212 core::map<v3s16, bool> & light_sources,
213 core::map<v3s16, MapBlock*> & modified_blocks)
216 v3s16(0,0,1), // back
218 v3s16(1,0,0), // right
219 v3s16(0,0,-1), // front
220 v3s16(0,-1,0), // bottom
221 v3s16(-1,0,0), // left
224 if(from_nodes.size() == 0)
227 u32 blockchangecount = 0;
229 core::map<v3s16, u8> unlighted_nodes;
230 core::map<v3s16, u8>::Iterator j;
231 j = from_nodes.getIterator();
234 Initialize block cache
237 MapBlock *block = NULL;
238 // Cache this a bit, too
239 bool block_checked_in_modified = false;
241 for(; j.atEnd() == false; j++)
243 v3s16 pos = j.getNode()->getKey();
244 v3s16 blockpos = getNodeBlockPos(pos);
246 // Only fetch a new block if the block position has changed
248 if(block == NULL || blockpos != blockpos_last){
249 block = getBlockNoCreate(blockpos);
250 blockpos_last = blockpos;
252 block_checked_in_modified = false;
256 catch(InvalidPositionException &e)
264 // Calculate relative position in block
265 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
267 // Get node straight from the block
268 MapNode n = block->getNode(relpos);
270 u8 oldlight = j.getNode()->getValue();
272 // Loop through 6 neighbors
273 for(u16 i=0; i<6; i++)
275 // Get the position of the neighbor node
276 v3s16 n2pos = pos + dirs[i];
278 // Get the block where the node is located
279 v3s16 blockpos = getNodeBlockPos(n2pos);
283 // Only fetch a new block if the block position has changed
285 if(block == NULL || blockpos != blockpos_last){
286 block = getBlockNoCreate(blockpos);
287 blockpos_last = blockpos;
289 block_checked_in_modified = false;
293 catch(InvalidPositionException &e)
298 // Calculate relative position in block
299 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
300 // Get node straight from the block
301 MapNode n2 = block->getNode(relpos);
303 bool changed = false;
305 //TODO: Optimize output by optimizing light_sources?
308 If the neighbor is dimmer than what was specified
309 as oldlight (the light of the previous node)
311 if(n2.getLight(bank) < oldlight)
314 And the neighbor is transparent and it has some light
316 if(n2.light_propagates() && n2.getLight(bank) != 0)
319 Set light to 0 and add to queue
322 u8 current_light = n2.getLight(bank);
323 n2.setLight(bank, 0);
324 block->setNode(relpos, n2);
326 unlighted_nodes.insert(n2pos, current_light);
330 Remove from light_sources if it is there
331 NOTE: This doesn't happen nearly at all
333 /*if(light_sources.find(n2pos))
335 std::cout<<"Removed from light_sources"<<std::endl;
336 light_sources.remove(n2pos);
341 if(light_sources.find(n2pos) != NULL)
342 light_sources.remove(n2pos);*/
345 light_sources.insert(n2pos, true);
348 // Add to modified_blocks
349 if(changed == true && block_checked_in_modified == false)
351 // If the block is not found in modified_blocks, add.
352 if(modified_blocks.find(blockpos) == NULL)
354 modified_blocks.insert(blockpos, block);
356 block_checked_in_modified = true;
359 catch(InvalidPositionException &e)
366 /*dstream<<"unspreadLight(): Changed block "
367 <<blockchangecount<<" times"
368 <<" for "<<from_nodes.size()<<" nodes"
371 if(unlighted_nodes.size() > 0)
372 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
376 A single-node wrapper of the above
378 void Map::unLightNeighbors(enum LightBank bank,
379 v3s16 pos, u8 lightwas,
380 core::map<v3s16, bool> & light_sources,
381 core::map<v3s16, MapBlock*> & modified_blocks)
383 core::map<v3s16, u8> from_nodes;
384 from_nodes.insert(pos, lightwas);
386 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
390 Lights neighbors of from_nodes, collects all them and then
393 void Map::spreadLight(enum LightBank bank,
394 core::map<v3s16, bool> & from_nodes,
395 core::map<v3s16, MapBlock*> & modified_blocks)
397 const v3s16 dirs[6] = {
398 v3s16(0,0,1), // back
400 v3s16(1,0,0), // right
401 v3s16(0,0,-1), // front
402 v3s16(0,-1,0), // bottom
403 v3s16(-1,0,0), // left
406 if(from_nodes.size() == 0)
409 u32 blockchangecount = 0;
411 core::map<v3s16, bool> lighted_nodes;
412 core::map<v3s16, bool>::Iterator j;
413 j = from_nodes.getIterator();
416 Initialize block cache
419 MapBlock *block = NULL;
420 // Cache this a bit, too
421 bool block_checked_in_modified = false;
423 for(; j.atEnd() == false; j++)
424 //for(; j != from_nodes.end(); j++)
426 v3s16 pos = j.getNode()->getKey();
428 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
429 v3s16 blockpos = getNodeBlockPos(pos);
431 // Only fetch a new block if the block position has changed
433 if(block == NULL || blockpos != blockpos_last){
434 block = getBlockNoCreate(blockpos);
435 blockpos_last = blockpos;
437 block_checked_in_modified = false;
441 catch(InvalidPositionException &e)
449 // Calculate relative position in block
450 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
452 // Get node straight from the block
453 MapNode n = block->getNode(relpos);
455 u8 oldlight = n.getLight(bank);
456 u8 newlight = diminish_light(oldlight);
458 // Loop through 6 neighbors
459 for(u16 i=0; i<6; i++){
460 // Get the position of the neighbor node
461 v3s16 n2pos = pos + dirs[i];
463 // Get the block where the node is located
464 v3s16 blockpos = getNodeBlockPos(n2pos);
468 // Only fetch a new block if the block position has changed
470 if(block == NULL || blockpos != blockpos_last){
471 block = getBlockNoCreate(blockpos);
472 blockpos_last = blockpos;
474 block_checked_in_modified = false;
478 catch(InvalidPositionException &e)
483 // Calculate relative position in block
484 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
485 // Get node straight from the block
486 MapNode n2 = block->getNode(relpos);
488 bool changed = false;
490 If the neighbor is brighter than the current node,
491 add to list (it will light up this node on its turn)
493 if(n2.getLight(bank) > undiminish_light(oldlight))
495 lighted_nodes.insert(n2pos, true);
496 //lighted_nodes.push_back(n2pos);
500 If the neighbor is dimmer than how much light this node
501 would spread on it, add to list
503 if(n2.getLight(bank) < newlight)
505 if(n2.light_propagates())
507 n2.setLight(bank, newlight);
508 block->setNode(relpos, n2);
509 lighted_nodes.insert(n2pos, true);
510 //lighted_nodes.push_back(n2pos);
515 // Add to modified_blocks
516 if(changed == true && block_checked_in_modified == false)
518 // If the block is not found in modified_blocks, add.
519 if(modified_blocks.find(blockpos) == NULL)
521 modified_blocks.insert(blockpos, block);
523 block_checked_in_modified = true;
526 catch(InvalidPositionException &e)
533 /*dstream<<"spreadLight(): Changed block "
534 <<blockchangecount<<" times"
535 <<" for "<<from_nodes.size()<<" nodes"
538 if(lighted_nodes.size() > 0)
539 spreadLight(bank, lighted_nodes, modified_blocks);
543 A single-node source variation of the above.
545 void Map::lightNeighbors(enum LightBank bank,
547 core::map<v3s16, MapBlock*> & modified_blocks)
549 core::map<v3s16, bool> from_nodes;
550 from_nodes.insert(pos, true);
551 spreadLight(bank, from_nodes, modified_blocks);
554 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
557 v3s16(0,0,1), // back
559 v3s16(1,0,0), // right
560 v3s16(0,0,-1), // front
561 v3s16(0,-1,0), // bottom
562 v3s16(-1,0,0), // left
565 u8 brightest_light = 0;
566 v3s16 brightest_pos(0,0,0);
567 bool found_something = false;
569 // Loop through 6 neighbors
570 for(u16 i=0; i<6; i++){
571 // Get the position of the neighbor node
572 v3s16 n2pos = p + dirs[i];
577 catch(InvalidPositionException &e)
581 if(n2.getLight(bank) > brightest_light || found_something == false){
582 brightest_light = n2.getLight(bank);
583 brightest_pos = n2pos;
584 found_something = true;
588 if(found_something == false)
589 throw InvalidPositionException();
591 return brightest_pos;
595 Propagates sunlight down from a node.
596 Starting point gets sunlight.
598 Returns the lowest y value of where the sunlight went.
600 Mud is turned into grass in where the sunlight stops.
602 s16 Map::propagateSunlight(v3s16 start,
603 core::map<v3s16, MapBlock*> & modified_blocks)
608 v3s16 pos(start.X, y, start.Z);
610 v3s16 blockpos = getNodeBlockPos(pos);
613 block = getBlockNoCreate(blockpos);
615 catch(InvalidPositionException &e)
620 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
621 MapNode n = block->getNode(relpos);
623 if(n.sunlight_propagates())
625 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
626 block->setNode(relpos, n);
628 modified_blocks.insert(blockpos, block);
632 /*// Turn mud into grass
633 if(n.getContent() == CONTENT_MUD)
635 n.setContent(CONTENT_GRASS);
636 block->setNode(relpos, n);
637 modified_blocks.insert(blockpos, block);
640 // Sunlight goes no further
647 void Map::updateLighting(enum LightBank bank,
648 core::map<v3s16, MapBlock*> & a_blocks,
649 core::map<v3s16, MapBlock*> & modified_blocks)
651 /*m_dout<<DTIME<<"Map::updateLighting(): "
652 <<a_blocks.size()<<" blocks."<<std::endl;*/
654 //TimeTaker timer("updateLighting");
658 //u32 count_was = modified_blocks.size();
660 core::map<v3s16, MapBlock*> blocks_to_update;
662 core::map<v3s16, bool> light_sources;
664 core::map<v3s16, u8> unlight_from;
666 core::map<v3s16, MapBlock*>::Iterator i;
667 i = a_blocks.getIterator();
668 for(; i.atEnd() == false; i++)
670 MapBlock *block = i.getNode()->getValue();
674 // Don't bother with dummy blocks.
678 v3s16 pos = block->getPos();
679 modified_blocks.insert(pos, block);
681 blocks_to_update.insert(pos, block);
684 Clear all light from block
686 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
687 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
688 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
693 MapNode n = block->getNode(v3s16(x,y,z));
694 u8 oldlight = n.getLight(bank);
696 block->setNode(v3s16(x,y,z), n);
698 // Collect borders for unlighting
699 if(x==0 || x == MAP_BLOCKSIZE-1
700 || y==0 || y == MAP_BLOCKSIZE-1
701 || z==0 || z == MAP_BLOCKSIZE-1)
703 v3s16 p_map = p + v3s16(
706 MAP_BLOCKSIZE*pos.Z);
707 unlight_from.insert(p_map, oldlight);
710 catch(InvalidPositionException &e)
713 This would happen when dealing with a
717 dstream<<"updateLighting(): InvalidPositionException"
722 if(bank == LIGHTBANK_DAY)
724 bool bottom_valid = block->propagateSunlight(light_sources);
726 // If bottom is valid, we're done.
730 else if(bank == LIGHTBANK_NIGHT)
732 // For night lighting, sunlight is not propagated
737 // Invalid lighting bank
741 /*dstream<<"Bottom for sunlight-propagated block ("
742 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
745 // Bottom sunlight is not valid; get the block and loop to it
749 block = getBlockNoCreate(pos);
751 catch(InvalidPositionException &e)
760 Enable this to disable proper lighting for speeding up map
761 generation for testing or whatever
764 //if(g_settings.get(""))
766 core::map<v3s16, MapBlock*>::Iterator i;
767 i = blocks_to_update.getIterator();
768 for(; i.atEnd() == false; i++)
770 MapBlock *block = i.getNode()->getValue();
771 v3s16 p = block->getPos();
772 block->setLightingExpired(false);
780 TimeTaker timer("unspreadLight");
781 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
786 u32 diff = modified_blocks.size() - count_was;
787 count_was = modified_blocks.size();
788 dstream<<"unspreadLight modified "<<diff<<std::endl;
792 TimeTaker timer("spreadLight");
793 spreadLight(bank, light_sources, modified_blocks);
798 u32 diff = modified_blocks.size() - count_was;
799 count_was = modified_blocks.size();
800 dstream<<"spreadLight modified "<<diff<<std::endl;
805 //MapVoxelManipulator vmanip(this);
807 // Make a manual voxel manipulator and load all the blocks
808 // that touch the requested blocks
809 ManualMapVoxelManipulator vmanip(this);
810 core::map<v3s16, MapBlock*>::Iterator i;
811 i = blocks_to_update.getIterator();
812 for(; i.atEnd() == false; i++)
814 MapBlock *block = i.getNode()->getValue();
815 v3s16 p = block->getPos();
817 // Add all surrounding blocks
818 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
821 Add all surrounding blocks that have up-to-date lighting
822 NOTE: This doesn't quite do the job (not everything
823 appropriate is lighted)
825 /*for(s16 z=-1; z<=1; z++)
826 for(s16 y=-1; y<=1; y++)
827 for(s16 x=-1; x<=1; x++)
830 MapBlock *block = getBlockNoCreateNoEx(p);
835 if(block->getLightingExpired())
837 vmanip.initialEmerge(p, p);
840 // Lighting of block will be updated completely
841 block->setLightingExpired(false);
845 //TimeTaker timer("unSpreadLight");
846 vmanip.unspreadLight(bank, unlight_from, light_sources);
849 //TimeTaker timer("spreadLight");
850 vmanip.spreadLight(bank, light_sources);
853 //TimeTaker timer("blitBack");
854 vmanip.blitBack(modified_blocks);
856 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
860 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
863 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
864 core::map<v3s16, MapBlock*> & modified_blocks)
866 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
867 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
870 Update information about whether day and night light differ
872 for(core::map<v3s16, MapBlock*>::Iterator
873 i = modified_blocks.getIterator();
874 i.atEnd() == false; i++)
876 MapBlock *block = i.getNode()->getValue();
877 block->updateDayNightDiff();
883 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
884 core::map<v3s16, MapBlock*> &modified_blocks)
887 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
888 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
891 From this node to nodes underneath:
892 If lighting is sunlight (1.0), unlight neighbours and
897 v3s16 toppos = p + v3s16(0,1,0);
898 v3s16 bottompos = p + v3s16(0,-1,0);
900 bool node_under_sunlight = true;
901 core::map<v3s16, bool> light_sources;
904 If there is a node at top and it doesn't have sunlight,
905 there has not been any sunlight going down.
907 Otherwise there probably is.
910 MapNode topnode = getNode(toppos);
912 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
913 node_under_sunlight = false;
915 catch(InvalidPositionException &e)
921 If the new node is solid and there is grass below, change it to mud
923 if(content_features(n).walkable == true)
926 MapNode bottomnode = getNode(bottompos);
928 if(bottomnode.getContent() == CONTENT_GRASS
929 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
931 bottomnode.setContent(CONTENT_MUD);
932 setNode(bottompos, bottomnode);
935 catch(InvalidPositionException &e)
943 If the new node is mud and it is under sunlight, change it
946 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
948 n.setContent(CONTENT_GRASS);
953 Remove all light that has come out of this node
956 enum LightBank banks[] =
961 for(s32 i=0; i<2; i++)
963 enum LightBank bank = banks[i];
965 u8 lightwas = getNode(p).getLight(bank);
967 // Add the block of the added node to modified_blocks
968 v3s16 blockpos = getNodeBlockPos(p);
969 MapBlock * block = getBlockNoCreate(blockpos);
970 assert(block != NULL);
971 modified_blocks.insert(blockpos, block);
973 assert(isValidPosition(p));
975 // Unlight neighbours of node.
976 // This means setting light of all consequent dimmer nodes
978 // This also collects the nodes at the border which will spread
979 // light again into this.
980 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
986 If node lets sunlight through and is under sunlight, it has
989 if(node_under_sunlight && content_features(n).sunlight_propagates)
991 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
995 Set the node on the map
1004 NodeMetadata *meta_proto = content_features(n).initial_metadata;
1007 NodeMetadata *meta = meta_proto->clone();
1008 setNodeMetadata(p, meta);
1012 If node is under sunlight and doesn't let sunlight through,
1013 take all sunlighted nodes under it and clear light from them
1014 and from where the light has been spread.
1015 TODO: This could be optimized by mass-unlighting instead
1018 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1022 //m_dout<<DTIME<<"y="<<y<<std::endl;
1023 v3s16 n2pos(p.X, y, p.Z);
1027 n2 = getNode(n2pos);
1029 catch(InvalidPositionException &e)
1034 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1036 unLightNeighbors(LIGHTBANK_DAY,
1037 n2pos, n2.getLight(LIGHTBANK_DAY),
1038 light_sources, modified_blocks);
1039 n2.setLight(LIGHTBANK_DAY, 0);
1047 for(s32 i=0; i<2; i++)
1049 enum LightBank bank = banks[i];
1052 Spread light from all nodes that might be capable of doing so
1054 spreadLight(bank, light_sources, modified_blocks);
1058 Update information about whether day and night light differ
1060 for(core::map<v3s16, MapBlock*>::Iterator
1061 i = modified_blocks.getIterator();
1062 i.atEnd() == false; i++)
1064 MapBlock *block = i.getNode()->getValue();
1065 block->updateDayNightDiff();
1069 Add neighboring liquid nodes and the node itself if it is
1070 liquid (=water node was added) to transform queue.
1073 v3s16(0,0,0), // self
1074 v3s16(0,0,1), // back
1075 v3s16(0,1,0), // top
1076 v3s16(1,0,0), // right
1077 v3s16(0,0,-1), // front
1078 v3s16(0,-1,0), // bottom
1079 v3s16(-1,0,0), // left
1081 for(u16 i=0; i<7; i++)
1086 v3s16 p2 = p + dirs[i];
1088 MapNode n2 = getNode(p2);
1089 if(content_liquid(n2.getContent()))
1091 m_transforming_liquid.push_back(p2);
1094 }catch(InvalidPositionException &e)
1102 void Map::removeNodeAndUpdate(v3s16 p,
1103 core::map<v3s16, MapBlock*> &modified_blocks)
1105 /*PrintInfo(m_dout);
1106 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1107 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1109 bool node_under_sunlight = true;
1111 v3s16 toppos = p + v3s16(0,1,0);
1113 // Node will be replaced with this
1114 content_t replace_material = CONTENT_AIR;
1117 If there is a node at top and it doesn't have sunlight,
1118 there will be no sunlight going down.
1121 MapNode topnode = getNode(toppos);
1123 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1124 node_under_sunlight = false;
1126 catch(InvalidPositionException &e)
1130 core::map<v3s16, bool> light_sources;
1132 enum LightBank banks[] =
1137 for(s32 i=0; i<2; i++)
1139 enum LightBank bank = banks[i];
1142 Unlight neighbors (in case the node is a light source)
1144 unLightNeighbors(bank, p,
1145 getNode(p).getLight(bank),
1146 light_sources, modified_blocks);
1150 Remove node metadata
1153 removeNodeMetadata(p);
1157 This also clears the lighting.
1161 n.setContent(replace_material);
1164 for(s32 i=0; i<2; i++)
1166 enum LightBank bank = banks[i];
1169 Recalculate lighting
1171 spreadLight(bank, light_sources, modified_blocks);
1174 // Add the block of the removed node to modified_blocks
1175 v3s16 blockpos = getNodeBlockPos(p);
1176 MapBlock * block = getBlockNoCreate(blockpos);
1177 assert(block != NULL);
1178 modified_blocks.insert(blockpos, block);
1181 If the removed node was under sunlight, propagate the
1182 sunlight down from it and then light all neighbors
1183 of the propagated blocks.
1185 if(node_under_sunlight)
1187 s16 ybottom = propagateSunlight(p, modified_blocks);
1188 /*m_dout<<DTIME<<"Node was under sunlight. "
1189 "Propagating sunlight";
1190 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1192 for(; y >= ybottom; y--)
1194 v3s16 p2(p.X, y, p.Z);
1195 /*m_dout<<DTIME<<"lighting neighbors of node ("
1196 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1198 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1203 // Set the lighting of this node to 0
1204 // TODO: Is this needed? Lighting is cleared up there already.
1206 MapNode n = getNode(p);
1207 n.setLight(LIGHTBANK_DAY, 0);
1210 catch(InvalidPositionException &e)
1216 for(s32 i=0; i<2; i++)
1218 enum LightBank bank = banks[i];
1220 // Get the brightest neighbour node and propagate light from it
1221 v3s16 n2p = getBrightestNeighbour(bank, p);
1223 MapNode n2 = getNode(n2p);
1224 lightNeighbors(bank, n2p, modified_blocks);
1226 catch(InvalidPositionException &e)
1232 Update information about whether day and night light differ
1234 for(core::map<v3s16, MapBlock*>::Iterator
1235 i = modified_blocks.getIterator();
1236 i.atEnd() == false; i++)
1238 MapBlock *block = i.getNode()->getValue();
1239 block->updateDayNightDiff();
1243 Add neighboring liquid nodes and this node to transform queue.
1244 (it's vital for the node itself to get updated last.)
1247 v3s16(0,0,1), // back
1248 v3s16(0,1,0), // top
1249 v3s16(1,0,0), // right
1250 v3s16(0,0,-1), // front
1251 v3s16(0,-1,0), // bottom
1252 v3s16(-1,0,0), // left
1253 v3s16(0,0,0), // self
1255 for(u16 i=0; i<7; i++)
1260 v3s16 p2 = p + dirs[i];
1262 MapNode n2 = getNode(p2);
1263 if(content_liquid(n2.getContent()))
1265 m_transforming_liquid.push_back(p2);
1268 }catch(InvalidPositionException &e)
1274 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1277 event.type = MEET_ADDNODE;
1281 bool succeeded = true;
1283 core::map<v3s16, MapBlock*> modified_blocks;
1284 addNodeAndUpdate(p, n, modified_blocks);
1286 // Copy modified_blocks to event
1287 for(core::map<v3s16, MapBlock*>::Iterator
1288 i = modified_blocks.getIterator();
1289 i.atEnd()==false; i++)
1291 event.modified_blocks.insert(i.getNode()->getKey(), false);
1294 catch(InvalidPositionException &e){
1298 dispatchEvent(&event);
1303 bool Map::removeNodeWithEvent(v3s16 p)
1306 event.type = MEET_REMOVENODE;
1309 bool succeeded = true;
1311 core::map<v3s16, MapBlock*> modified_blocks;
1312 removeNodeAndUpdate(p, modified_blocks);
1314 // Copy modified_blocks to event
1315 for(core::map<v3s16, MapBlock*>::Iterator
1316 i = modified_blocks.getIterator();
1317 i.atEnd()==false; i++)
1319 event.modified_blocks.insert(i.getNode()->getKey(), false);
1322 catch(InvalidPositionException &e){
1326 dispatchEvent(&event);
1331 bool Map::dayNightDiffed(v3s16 blockpos)
1334 v3s16 p = blockpos + v3s16(0,0,0);
1335 MapBlock *b = getBlockNoCreate(p);
1336 if(b->dayNightDiffed())
1339 catch(InvalidPositionException &e){}
1342 v3s16 p = blockpos + v3s16(-1,0,0);
1343 MapBlock *b = getBlockNoCreate(p);
1344 if(b->dayNightDiffed())
1347 catch(InvalidPositionException &e){}
1349 v3s16 p = blockpos + v3s16(0,-1,0);
1350 MapBlock *b = getBlockNoCreate(p);
1351 if(b->dayNightDiffed())
1354 catch(InvalidPositionException &e){}
1356 v3s16 p = blockpos + v3s16(0,0,-1);
1357 MapBlock *b = getBlockNoCreate(p);
1358 if(b->dayNightDiffed())
1361 catch(InvalidPositionException &e){}
1364 v3s16 p = blockpos + v3s16(1,0,0);
1365 MapBlock *b = getBlockNoCreate(p);
1366 if(b->dayNightDiffed())
1369 catch(InvalidPositionException &e){}
1371 v3s16 p = blockpos + v3s16(0,1,0);
1372 MapBlock *b = getBlockNoCreate(p);
1373 if(b->dayNightDiffed())
1376 catch(InvalidPositionException &e){}
1378 v3s16 p = blockpos + v3s16(0,0,1);
1379 MapBlock *b = getBlockNoCreate(p);
1380 if(b->dayNightDiffed())
1383 catch(InvalidPositionException &e){}
1389 Updates usage timers
1391 void Map::timerUpdate(float dtime, float unload_timeout,
1392 core::list<v3s16> *unloaded_blocks)
1394 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1396 core::list<v2s16> sector_deletion_queue;
1397 u32 deleted_blocks_count = 0;
1398 u32 saved_blocks_count = 0;
1400 core::map<v2s16, MapSector*>::Iterator si;
1402 si = m_sectors.getIterator();
1403 for(; si.atEnd() == false; si++)
1405 MapSector *sector = si.getNode()->getValue();
1407 bool all_blocks_deleted = true;
1409 core::list<MapBlock*> blocks;
1410 sector->getBlocks(blocks);
1411 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1412 i != blocks.end(); i++)
1414 MapBlock *block = (*i);
1416 block->incrementUsageTimer(dtime);
1418 if(block->getUsageTimer() > unload_timeout)
1420 v3s16 p = block->getPos();
1423 if(block->getModified() != MOD_STATE_CLEAN
1424 && save_before_unloading)
1427 saved_blocks_count++;
1430 // Delete from memory
1431 sector->deleteBlock(block);
1434 unloaded_blocks->push_back(p);
1436 deleted_blocks_count++;
1440 all_blocks_deleted = false;
1444 if(all_blocks_deleted)
1446 sector_deletion_queue.push_back(si.getNode()->getKey());
1450 // Finally delete the empty sectors
1451 deleteSectors(sector_deletion_queue);
1453 if(deleted_blocks_count != 0)
1455 PrintInfo(dstream); // ServerMap/ClientMap:
1456 dstream<<"Unloaded "<<deleted_blocks_count
1457 <<" blocks from memory";
1458 if(save_before_unloading)
1459 dstream<<", of which "<<saved_blocks_count<<" were written";
1460 dstream<<"."<<std::endl;
1464 void Map::deleteSectors(core::list<v2s16> &list)
1466 core::list<v2s16>::Iterator j;
1467 for(j=list.begin(); j!=list.end(); j++)
1469 MapSector *sector = m_sectors[*j];
1470 // If sector is in sector cache, remove it from there
1471 if(m_sector_cache == sector)
1472 m_sector_cache = NULL;
1473 // Remove from map and delete
1474 m_sectors.remove(*j);
1480 void Map::unloadUnusedData(float timeout,
1481 core::list<v3s16> *deleted_blocks)
1483 core::list<v2s16> sector_deletion_queue;
1484 u32 deleted_blocks_count = 0;
1485 u32 saved_blocks_count = 0;
1487 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1488 for(; si.atEnd() == false; si++)
1490 MapSector *sector = si.getNode()->getValue();
1492 bool all_blocks_deleted = true;
1494 core::list<MapBlock*> blocks;
1495 sector->getBlocks(blocks);
1496 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1497 i != blocks.end(); i++)
1499 MapBlock *block = (*i);
1501 if(block->getUsageTimer() > timeout)
1504 if(block->getModified() != MOD_STATE_CLEAN)
1507 saved_blocks_count++;
1509 // Delete from memory
1510 sector->deleteBlock(block);
1511 deleted_blocks_count++;
1515 all_blocks_deleted = false;
1519 if(all_blocks_deleted)
1521 sector_deletion_queue.push_back(si.getNode()->getKey());
1525 deleteSectors(sector_deletion_queue);
1527 dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1528 <<", of which "<<saved_blocks_count<<" were wr."
1531 //return sector_deletion_queue.getSize();
1532 //return deleted_blocks_count;
1536 void Map::PrintInfo(std::ostream &out)
1541 #define WATER_DROP_BOOST 4
1543 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1545 DSTACK(__FUNCTION_NAME);
1546 //TimeTaker timer("transformLiquids()");
1549 u32 initial_size = m_transforming_liquid.size();
1551 /*if(initial_size != 0)
1552 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1554 while(m_transforming_liquid.size() != 0)
1557 Get a queued transforming liquid node
1559 v3s16 p0 = m_transforming_liquid.pop_front();
1561 MapNode n0 = getNodeNoEx(p0);
1563 // Don't deal with non-liquids
1564 if(content_liquid(n0.getContent()) == false)
1567 bool is_source = !content_flowing_liquid(n0.getContent());
1569 u8 liquid_level = 8;
1570 if(is_source == false)
1571 liquid_level = n0.param2 & 0x0f;
1573 // Turn possible source into non-source
1574 u8 nonsource_c = make_liquid_flowing(n0.getContent());
1577 If not source, check that some node flows into this one
1578 and what is the level of liquid in this one
1580 if(is_source == false)
1582 s8 new_liquid_level_max = -1;
1584 v3s16 dirs_from[5] = {
1585 v3s16(0,1,0), // top
1586 v3s16(0,0,1), // back
1587 v3s16(1,0,0), // right
1588 v3s16(0,0,-1), // front
1589 v3s16(-1,0,0), // left
1591 for(u16 i=0; i<5; i++)
1593 bool from_top = (i==0);
1595 v3s16 p2 = p0 + dirs_from[i];
1596 MapNode n2 = getNodeNoEx(p2);
1598 if(content_liquid(n2.getContent()))
1600 u8 n2_nonsource_c = make_liquid_flowing(n2.getContent());
1601 // Check that the liquids are the same type
1602 if(n2_nonsource_c != nonsource_c)
1604 dstream<<"WARNING: Not handling: different liquids"
1605 " collide"<<std::endl;
1608 bool n2_is_source = !content_flowing_liquid(n2.getContent());
1609 s8 n2_liquid_level = 8;
1610 if(n2_is_source == false)
1611 n2_liquid_level = n2.param2 & 0x07;
1613 s8 new_liquid_level = -1;
1616 //new_liquid_level = 7;
1617 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1618 new_liquid_level = 7;
1620 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1622 else if(n2_liquid_level > 0)
1624 new_liquid_level = n2_liquid_level - 1;
1627 if(new_liquid_level > new_liquid_level_max)
1628 new_liquid_level_max = new_liquid_level;
1633 If liquid level should be something else, update it and
1634 add all the neighboring water nodes to the transform queue.
1636 if(new_liquid_level_max != liquid_level)
1638 if(new_liquid_level_max == -1)
1640 // Remove water alltoghether
1641 n0.setContent(CONTENT_AIR);
1647 n0.param2 = new_liquid_level_max;
1651 // Block has been modified
1653 v3s16 blockpos = getNodeBlockPos(p0);
1654 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1656 modified_blocks.insert(blockpos, block);
1660 Add neighboring non-source liquid nodes to transform queue.
1663 v3s16(0,0,1), // back
1664 v3s16(0,1,0), // top
1665 v3s16(1,0,0), // right
1666 v3s16(0,0,-1), // front
1667 v3s16(0,-1,0), // bottom
1668 v3s16(-1,0,0), // left
1670 for(u16 i=0; i<6; i++)
1672 v3s16 p2 = p0 + dirs[i];
1674 MapNode n2 = getNodeNoEx(p2);
1675 if(content_flowing_liquid(n2.getContent()))
1677 m_transforming_liquid.push_back(p2);
1683 // Get a new one from queue if the node has turned into non-water
1684 if(content_liquid(n0.getContent()) == false)
1688 Flow water from this node
1690 v3s16 dirs_to[5] = {
1691 v3s16(0,-1,0), // bottom
1692 v3s16(0,0,1), // back
1693 v3s16(1,0,0), // right
1694 v3s16(0,0,-1), // front
1695 v3s16(-1,0,0), // left
1697 for(u16 i=0; i<5; i++)
1699 bool to_bottom = (i == 0);
1701 // If liquid is at lowest possible height, it's not going
1702 // anywhere except down
1703 if(liquid_level == 0 && to_bottom == false)
1706 u8 liquid_next_level = 0;
1707 // If going to bottom
1710 //liquid_next_level = 7;
1711 if(liquid_level >= 7 - WATER_DROP_BOOST)
1712 liquid_next_level = 7;
1714 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1717 liquid_next_level = liquid_level - 1;
1719 bool n2_changed = false;
1720 bool flowed = false;
1722 v3s16 p2 = p0 + dirs_to[i];
1724 MapNode n2 = getNodeNoEx(p2);
1725 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1727 if(content_liquid(n2.getContent()))
1729 u8 n2_nonsource_c = make_liquid_flowing(n2.getContent());
1730 // Check that the liquids are the same type
1731 if(n2_nonsource_c != nonsource_c)
1733 dstream<<"WARNING: Not handling: different liquids"
1734 " collide"<<std::endl;
1737 bool n2_is_source = !content_flowing_liquid(n2.getContent());
1738 u8 n2_liquid_level = 8;
1739 if(n2_is_source == false)
1740 n2_liquid_level = n2.param2 & 0x07;
1749 // Just flow into the source, nothing changes.
1750 // n2_changed is not set because destination didn't change
1755 if(liquid_next_level > liquid_level)
1757 n2.param2 = liquid_next_level;
1765 else if(n2.getContent() == CONTENT_AIR)
1767 n2.setContent(nonsource_c);
1768 n2.param2 = liquid_next_level;
1775 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1779 m_transforming_liquid.push_back(p2);
1781 v3s16 blockpos = getNodeBlockPos(p2);
1782 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1784 modified_blocks.insert(blockpos, block);
1787 // If n2_changed to bottom, don't flow anywhere else
1788 if(to_bottom && flowed && !is_source)
1793 //if(loopcount >= 100000)
1794 if(loopcount >= initial_size * 1)
1797 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1800 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1802 v3s16 blockpos = getNodeBlockPos(p);
1803 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1804 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1807 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1811 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1815 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1817 v3s16 blockpos = getNodeBlockPos(p);
1818 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1819 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1822 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1826 block->m_node_metadata.set(p_rel, meta);
1829 void Map::removeNodeMetadata(v3s16 p)
1831 v3s16 blockpos = getNodeBlockPos(p);
1832 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1833 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1836 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1840 block->m_node_metadata.remove(p_rel);
1843 void Map::nodeMetadataStep(float dtime,
1844 core::map<v3s16, MapBlock*> &changed_blocks)
1848 Currently there is no way to ensure that all the necessary
1849 blocks are loaded when this is run. (They might get unloaded)
1850 NOTE: ^- Actually, that might not be so. In a quick test it
1851 reloaded a block with a furnace when I walked back to it from
1854 core::map<v2s16, MapSector*>::Iterator si;
1855 si = m_sectors.getIterator();
1856 for(; si.atEnd() == false; si++)
1858 MapSector *sector = si.getNode()->getValue();
1859 core::list< MapBlock * > sectorblocks;
1860 sector->getBlocks(sectorblocks);
1861 core::list< MapBlock * >::Iterator i;
1862 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1864 MapBlock *block = *i;
1865 bool changed = block->m_node_metadata.step(dtime);
1867 changed_blocks[block->getPos()] = block;
1876 ServerMap::ServerMap(std::string savedir):
1879 m_map_metadata_changed(true)
1881 dstream<<__FUNCTION_NAME<<std::endl;
1883 //m_chunksize = 8; // Takes a few seconds
1885 m_seed = (((u64)(myrand()%0xffff)<<0)
1886 + ((u64)(myrand()%0xffff)<<16)
1887 + ((u64)(myrand()%0xffff)<<32)
1888 + ((u64)(myrand()%0xffff)<<48));
1891 Experimental and debug stuff
1898 Try to load map; if not found, create a new one.
1901 m_savedir = savedir;
1902 m_map_saving_enabled = false;
1906 // If directory exists, check contents and load if possible
1907 if(fs::PathExists(m_savedir))
1909 // If directory is empty, it is safe to save into it.
1910 if(fs::GetDirListing(m_savedir).size() == 0)
1912 dstream<<DTIME<<"Server: Empty save directory is valid."
1914 m_map_saving_enabled = true;
1919 // Load map metadata (seed, chunksize)
1922 catch(FileNotGoodException &e){
1923 dstream<<DTIME<<"WARNING: Could not load map metadata"
1924 //<<" Disabling chunk-based generator."
1930 // Load chunk metadata
1933 catch(FileNotGoodException &e){
1934 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1935 <<" Disabling chunk-based generator."
1940 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1941 "metadata and sector (0,0) from "<<savedir<<
1942 ", assuming valid save directory."
1945 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1946 <<"and chunk metadata from "<<savedir
1947 <<", assuming valid save directory."
1950 m_map_saving_enabled = true;
1951 // Map loaded, not creating new one
1955 // If directory doesn't exist, it is safe to save to it
1957 m_map_saving_enabled = true;
1960 catch(std::exception &e)
1962 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1963 <<", exception: "<<e.what()<<std::endl;
1964 dstream<<"Please remove the map or fix it."<<std::endl;
1965 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1968 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1970 // Create zero sector
1971 emergeSector(v2s16(0,0));
1973 // Initially write whole map
1977 ServerMap::~ServerMap()
1979 dstream<<__FUNCTION_NAME<<std::endl;
1983 if(m_map_saving_enabled)
1985 // Save only changed parts
1987 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1991 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1994 catch(std::exception &e)
1996 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1997 <<", exception: "<<e.what()<<std::endl;
2004 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2005 for(; i.atEnd() == false; i++)
2007 MapChunk *chunk = i.getNode()->getValue();
2013 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2015 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2016 <<blockpos.Z<<")"<<std::endl;
2018 // Do nothing if not inside limits (+-1 because of neighbors)
2019 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2020 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2026 data->no_op = false;
2027 data->seed = m_seed;
2028 data->blockpos = blockpos;
2031 Create the whole area of this and the neighboring blocks
2034 //TimeTaker timer("initBlockMake() create area");
2036 for(s16 x=-1; x<=1; x++)
2037 for(s16 z=-1; z<=1; z++)
2039 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2040 // Sector metadata is loaded from disk if not already loaded.
2041 ServerMapSector *sector = createSector(sectorpos);
2044 for(s16 y=-1; y<=1; y++)
2046 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2047 //MapBlock *block = createBlock(p);
2048 // 1) get from memory, 2) load from disk
2049 MapBlock *block = emergeBlock(p, false);
2050 // 3) create a blank one
2053 block = createBlock(p);
2056 Block gets sunlight if this is true.
2058 Refer to the map generator heuristics.
2060 bool ug = mapgen::block_is_underground(data->seed, p);
2061 block->setIsUnderground(ug);
2064 // Lighting will not be valid after make_chunk is called
2065 block->setLightingExpired(true);
2066 // Lighting will be calculated
2067 //block->setLightingExpired(false);
2073 Now we have a big empty area.
2075 Make a ManualMapVoxelManipulator that contains this and the
2079 // The area that contains this block and it's neighbors
2080 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2081 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2083 data->vmanip = new ManualMapVoxelManipulator(this);
2084 //data->vmanip->setMap(this);
2088 //TimeTaker timer("initBlockMake() initialEmerge");
2089 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2092 // Data is ready now.
2095 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2096 core::map<v3s16, MapBlock*> &changed_blocks)
2098 v3s16 blockpos = data->blockpos;
2099 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2100 <<blockpos.Z<<")"<<std::endl;*/
2104 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2108 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2110 /*dstream<<"Resulting vmanip:"<<std::endl;
2111 data->vmanip.print(dstream);*/
2114 Blit generated stuff to map
2115 NOTE: blitBackAll adds nearly everything to changed_blocks
2119 //TimeTaker timer("finishBlockMake() blitBackAll");
2120 data->vmanip->blitBackAll(&changed_blocks);
2123 if(enable_mapgen_debug_info)
2124 dstream<<"finishBlockMake: changed_blocks.size()="
2125 <<changed_blocks.size()<<std::endl;
2128 Copy transforming liquid information
2130 while(data->transforming_liquid.size() > 0)
2132 v3s16 p = data->transforming_liquid.pop_front();
2133 m_transforming_liquid.push_back(p);
2139 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2143 Set is_underground flag for lighting with sunlight.
2145 Refer to map generator heuristics.
2147 NOTE: This is done in initChunkMake
2149 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2153 Add sunlight to central block.
2154 This makes in-dark-spawning monsters to not flood the whole thing.
2155 Do not spread the light, though.
2157 /*core::map<v3s16, bool> light_sources;
2158 bool black_air_left = false;
2159 block->propagateSunlight(light_sources, true, &black_air_left);*/
2162 NOTE: Lighting and object adding shouldn't really be here, but
2163 lighting is a bit tricky to move properly to makeBlock.
2164 TODO: Do this the right way anyway, that is, move it to makeBlock.
2165 - There needs to be some way for makeBlock to report back if
2166 the lighting update is going further down because of the
2167 new block blocking light
2172 NOTE: This takes ~60ms, TODO: Investigate why
2175 TimeTaker t("finishBlockMake lighting update");
2177 core::map<v3s16, MapBlock*> lighting_update_blocks;
2180 lighting_update_blocks.insert(block->getPos(), block);
2185 v3s16 p = block->getPos()+v3s16(x,1,z);
2186 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2190 // All modified blocks
2191 // NOTE: Should this be done? If this is not done, then the lighting
2192 // of the others will be updated in a different place, one by one, i
2193 // think... or they might not? Well, at least they are left marked as
2194 // "lighting expired"; it seems that is not handled at all anywhere,
2195 // so enabling this will slow it down A LOT because otherwise it
2196 // would not do this at all. This causes the black trees.
2197 for(core::map<v3s16, MapBlock*>::Iterator
2198 i = changed_blocks.getIterator();
2199 i.atEnd() == false; i++)
2201 lighting_update_blocks.insert(i.getNode()->getKey(),
2202 i.getNode()->getValue());
2204 /*// Also force-add all the upmost blocks for proper sunlight
2205 for(s16 x=-1; x<=1; x++)
2206 for(s16 z=-1; z<=1; z++)
2208 v3s16 p = block->getPos()+v3s16(x,1,z);
2209 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2212 updateLighting(lighting_update_blocks, changed_blocks);
2215 Set lighting to non-expired state in all of them.
2216 This is cheating, but it is not fast enough if all of them
2217 would actually be updated.
2219 for(s16 x=-1; x<=1; x++)
2220 for(s16 y=-1; y<=1; y++)
2221 for(s16 z=-1; z<=1; z++)
2223 v3s16 p = block->getPos()+v3s16(x,y,z);
2224 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2227 if(enable_mapgen_debug_info == false)
2228 t.stop(true); // Hide output
2232 Add random objects to block
2234 mapgen::add_random_objects(block);
2237 Go through changed blocks
2239 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2240 i.atEnd() == false; i++)
2242 MapBlock *block = i.getNode()->getValue();
2245 Update day/night difference cache of the MapBlocks
2247 block->updateDayNightDiff();
2249 Set block as modified
2251 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2255 Set central block as generated
2257 block->setGenerated(true);
2260 Save changed parts of map
2261 NOTE: Will be saved later.
2265 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2266 <<blockpos.Z<<")"<<std::endl;*/
2271 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2273 DSTACKF("%s: p2d=(%d,%d)",
2278 Check if it exists already in memory
2280 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2285 Try to load it from disk (with blocks)
2287 //if(loadSectorFull(p2d) == true)
2290 Try to load metadata from disk
2292 if(loadSectorMeta(p2d) == true)
2294 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2297 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2298 throw InvalidPositionException("");
2304 Do not create over-limit
2306 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2307 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2308 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2309 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2310 throw InvalidPositionException("createSector(): pos. over limit");
2313 Generate blank sector
2316 sector = new ServerMapSector(this, p2d);
2318 // Sector position on map in nodes
2319 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2324 m_sectors.insert(p2d, sector);
2330 This is a quick-hand function for calling makeBlock().
2332 MapBlock * ServerMap::generateBlock(
2334 core::map<v3s16, MapBlock*> &modified_blocks
2337 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2339 /*dstream<<"generateBlock(): "
2340 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2343 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2345 TimeTaker timer("generateBlock");
2347 //MapBlock *block = original_dummy;
2349 v2s16 p2d(p.X, p.Z);
2350 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2353 Do not generate over-limit
2355 if(blockpos_over_limit(p))
2357 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2358 throw InvalidPositionException("generateBlock(): pos. over limit");
2362 Create block make data
2364 mapgen::BlockMakeData data;
2365 initBlockMake(&data, p);
2371 TimeTaker t("mapgen::make_block()");
2372 mapgen::make_block(&data);
2374 if(enable_mapgen_debug_info == false)
2375 t.stop(true); // Hide output
2379 Blit data back on map, update lighting, add mobs and whatever this does
2381 finishBlockMake(&data, modified_blocks);
2386 MapBlock *block = getBlockNoCreateNoEx(p);
2394 bool erroneus_content = false;
2395 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2396 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2397 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2400 MapNode n = block->getNode(p);
2401 if(n.getContent() == CONTENT_IGNORE)
2403 dstream<<"CONTENT_IGNORE at "
2404 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2406 erroneus_content = true;
2410 if(erroneus_content)
2419 Generate a completely empty block
2423 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2424 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2426 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2430 n.setContent(CONTENT_AIR);
2432 n.setContent(CONTENT_STONE);
2433 block->setNode(v3s16(x0,y0,z0), n);
2439 if(enable_mapgen_debug_info == false)
2440 timer.stop(true); // Hide output
2445 MapBlock * ServerMap::createBlock(v3s16 p)
2447 DSTACKF("%s: p=(%d,%d,%d)",
2448 __FUNCTION_NAME, p.X, p.Y, p.Z);
2451 Do not create over-limit
2453 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2454 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2455 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2456 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2457 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2458 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2459 throw InvalidPositionException("createBlock(): pos. over limit");
2461 v2s16 p2d(p.X, p.Z);
2464 This will create or load a sector if not found in memory.
2465 If block exists on disk, it will be loaded.
2467 NOTE: On old save formats, this will be slow, as it generates
2468 lighting on blocks for them.
2470 ServerMapSector *sector;
2472 sector = (ServerMapSector*)createSector(p2d);
2473 assert(sector->getId() == MAPSECTOR_SERVER);
2475 catch(InvalidPositionException &e)
2477 dstream<<"createBlock: createSector() failed"<<std::endl;
2481 NOTE: This should not be done, or at least the exception
2482 should not be passed on as std::exception, because it
2483 won't be catched at all.
2485 /*catch(std::exception &e)
2487 dstream<<"createBlock: createSector() failed: "
2488 <<e.what()<<std::endl;
2493 Try to get a block from the sector
2496 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2499 if(block->isDummy())
2504 block = sector->createBlankBlock(block_y);
2508 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2510 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2512 p.X, p.Y, p.Z, allow_generate);
2515 MapBlock *block = getBlockNoCreateNoEx(p);
2516 if(block && block->isDummy() == false)
2521 MapBlock *block = loadBlock(p);
2528 core::map<v3s16, MapBlock*> modified_blocks;
2529 MapBlock *block = generateBlock(p, modified_blocks);
2533 event.type = MEET_OTHER;
2536 // Copy modified_blocks to event
2537 for(core::map<v3s16, MapBlock*>::Iterator
2538 i = modified_blocks.getIterator();
2539 i.atEnd()==false; i++)
2541 event.modified_blocks.insert(i.getNode()->getKey(), false);
2545 dispatchEvent(&event);
2556 Do not generate over-limit
2558 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2559 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2560 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2561 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2562 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2563 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2564 throw InvalidPositionException("emergeBlock(): pos. over limit");
2566 v2s16 p2d(p.X, p.Z);
2569 This will create or load a sector if not found in memory.
2570 If block exists on disk, it will be loaded.
2572 ServerMapSector *sector;
2574 sector = createSector(p2d);
2575 //sector = emergeSector(p2d, changed_blocks);
2577 catch(InvalidPositionException &e)
2579 dstream<<"emergeBlock: createSector() failed: "
2580 <<e.what()<<std::endl;
2581 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2583 <<"You could try to delete it."<<std::endl;
2586 catch(VersionMismatchException &e)
2588 dstream<<"emergeBlock: createSector() failed: "
2589 <<e.what()<<std::endl;
2590 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2592 <<"You could try to delete it."<<std::endl;
2597 Try to get a block from the sector
2600 bool does_not_exist = false;
2601 bool lighting_expired = false;
2602 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2604 // If not found, try loading from disk
2607 block = loadBlock(p);
2613 does_not_exist = true;
2615 else if(block->isDummy() == true)
2617 does_not_exist = true;
2619 else if(block->getLightingExpired())
2621 lighting_expired = true;
2626 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2631 If block was not found on disk and not going to generate a
2632 new one, make sure there is a dummy block in place.
2634 if(only_from_disk && (does_not_exist || lighting_expired))
2636 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2640 // Create dummy block
2641 block = new MapBlock(this, p, true);
2643 // Add block to sector
2644 sector->insertBlock(block);
2650 //dstream<<"Not found on disk, generating."<<std::endl;
2652 //TimeTaker("emergeBlock() generate");
2654 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2657 If the block doesn't exist, generate the block.
2661 block = generateBlock(p, block, sector, changed_blocks,
2662 lighting_invalidated_blocks);
2665 if(lighting_expired)
2667 lighting_invalidated_blocks.insert(p, block);
2672 Initially update sunlight
2675 core::map<v3s16, bool> light_sources;
2676 bool black_air_left = false;
2677 bool bottom_invalid =
2678 block->propagateSunlight(light_sources, true,
2681 // If sunlight didn't reach everywhere and part of block is
2682 // above ground, lighting has to be properly updated
2683 //if(black_air_left && some_part_underground)
2686 lighting_invalidated_blocks[block->getPos()] = block;
2691 lighting_invalidated_blocks[block->getPos()] = block;
2700 s16 ServerMap::findGroundLevel(v2s16 p2d)
2704 Uh, just do something random...
2706 // Find existing map from top to down
2709 v3s16 p(p2d.X, max, p2d.Y);
2710 for(; p.Y>min; p.Y--)
2712 MapNode n = getNodeNoEx(p);
2713 if(n.getContent() != CONTENT_IGNORE)
2718 // If this node is not air, go to plan b
2719 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2721 // Search existing walkable and return it
2722 for(; p.Y>min; p.Y--)
2724 MapNode n = getNodeNoEx(p);
2725 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2734 Determine from map generator noise functions
2737 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2740 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2741 //return (s16)level;
2744 void ServerMap::createDirs(std::string path)
2746 if(fs::CreateAllDirs(path) == false)
2748 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2749 <<"\""<<path<<"\""<<std::endl;
2750 throw BaseException("ServerMap failed to create directory");
2754 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2760 snprintf(cc, 9, "%.4x%.4x",
2761 (unsigned int)pos.X&0xffff,
2762 (unsigned int)pos.Y&0xffff);
2764 return m_savedir + "/sectors/" + cc;
2766 snprintf(cc, 9, "%.3x/%.3x",
2767 (unsigned int)pos.X&0xfff,
2768 (unsigned int)pos.Y&0xfff);
2770 return m_savedir + "/sectors2/" + cc;
2776 v2s16 ServerMap::getSectorPos(std::string dirname)
2780 size_t spos = dirname.rfind('/') + 1;
2781 assert(spos != std::string::npos);
2782 if(dirname.size() - spos == 8)
2785 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2787 else if(dirname.size() - spos == 3)
2790 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2791 // Sign-extend the 12 bit values up to 16 bits...
2792 if(x&0x800) x|=0xF000;
2793 if(y&0x800) y|=0xF000;
2800 v2s16 pos((s16)x, (s16)y);
2804 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2806 v2s16 p2d = getSectorPos(sectordir);
2808 if(blockfile.size() != 4){
2809 throw InvalidFilenameException("Invalid block filename");
2812 int r = sscanf(blockfile.c_str(), "%4x", &y);
2814 throw InvalidFilenameException("Invalid block filename");
2815 return v3s16(p2d.X, y, p2d.Y);
2818 std::string ServerMap::getBlockFilename(v3s16 p)
2821 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2825 void ServerMap::save(bool only_changed)
2827 DSTACK(__FUNCTION_NAME);
2828 if(m_map_saving_enabled == false)
2830 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2834 if(only_changed == false)
2835 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2838 if(only_changed == false || m_map_metadata_changed)
2843 u32 sector_meta_count = 0;
2844 u32 block_count = 0;
2845 u32 block_count_all = 0; // Number of blocks in memory
2847 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2848 for(; i.atEnd() == false; i++)
2850 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2851 assert(sector->getId() == MAPSECTOR_SERVER);
2853 if(sector->differs_from_disk || only_changed == false)
2855 saveSectorMeta(sector);
2856 sector_meta_count++;
2858 core::list<MapBlock*> blocks;
2859 sector->getBlocks(blocks);
2860 core::list<MapBlock*>::Iterator j;
2861 for(j=blocks.begin(); j!=blocks.end(); j++)
2863 MapBlock *block = *j;
2867 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2868 || only_changed == false)
2873 /*dstream<<"ServerMap: Written block ("
2874 <<block->getPos().X<<","
2875 <<block->getPos().Y<<","
2876 <<block->getPos().Z<<")"
2883 Only print if something happened or saved whole map
2885 if(only_changed == false || sector_meta_count != 0
2886 || block_count != 0)
2888 dstream<<DTIME<<"ServerMap: Written: "
2889 <<sector_meta_count<<" sector metadata files, "
2890 <<block_count<<" block files"
2891 <<", "<<block_count_all<<" blocks in memory."
2896 void ServerMap::saveMapMeta()
2898 DSTACK(__FUNCTION_NAME);
2900 dstream<<"INFO: ServerMap::saveMapMeta(): "
2904 createDirs(m_savedir);
2906 std::string fullpath = m_savedir + "/map_meta.txt";
2907 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2908 if(os.good() == false)
2910 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2911 <<"could not open"<<fullpath<<std::endl;
2912 throw FileNotGoodException("Cannot open chunk metadata");
2916 params.setU64("seed", m_seed);
2918 params.writeLines(os);
2920 os<<"[end_of_params]\n";
2922 m_map_metadata_changed = false;
2925 void ServerMap::loadMapMeta()
2927 DSTACK(__FUNCTION_NAME);
2929 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2932 std::string fullpath = m_savedir + "/map_meta.txt";
2933 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2934 if(is.good() == false)
2936 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2937 <<"could not open"<<fullpath<<std::endl;
2938 throw FileNotGoodException("Cannot open map metadata");
2946 throw SerializationError
2947 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2949 std::getline(is, line);
2950 std::string trimmedline = trim(line);
2951 if(trimmedline == "[end_of_params]")
2953 params.parseConfigLine(line);
2956 m_seed = params.getU64("seed");
2958 dstream<<"INFO: ServerMap::loadMapMeta(): "
2963 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2965 DSTACK(__FUNCTION_NAME);
2966 // Format used for writing
2967 u8 version = SER_FMT_VER_HIGHEST;
2969 v2s16 pos = sector->getPos();
2970 std::string dir = getSectorDir(pos);
2973 std::string fullpath = dir + "/meta";
2974 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2975 if(o.good() == false)
2976 throw FileNotGoodException("Cannot open sector metafile");
2978 sector->serialize(o, version);
2980 sector->differs_from_disk = false;
2983 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2985 DSTACK(__FUNCTION_NAME);
2987 v2s16 p2d = getSectorPos(sectordir);
2989 ServerMapSector *sector = NULL;
2991 std::string fullpath = sectordir + "/meta";
2992 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2993 if(is.good() == false)
2995 // If the directory exists anyway, it probably is in some old
2996 // format. Just go ahead and create the sector.
2997 if(fs::PathExists(sectordir))
2999 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
3000 <<fullpath<<" doesn't exist but directory does."
3001 <<" Continuing with a sector with no metadata."
3003 sector = new ServerMapSector(this, p2d);
3004 m_sectors.insert(p2d, sector);
3008 throw FileNotGoodException("Cannot open sector metafile");
3013 sector = ServerMapSector::deSerialize
3014 (is, this, p2d, m_sectors);
3016 saveSectorMeta(sector);
3019 sector->differs_from_disk = false;
3024 bool ServerMap::loadSectorMeta(v2s16 p2d)
3026 DSTACK(__FUNCTION_NAME);
3028 MapSector *sector = NULL;
3030 // The directory layout we're going to load from.
3031 // 1 - original sectors/xxxxzzzz/
3032 // 2 - new sectors2/xxx/zzz/
3033 // If we load from anything but the latest structure, we will
3034 // immediately save to the new one, and remove the old.
3036 std::string sectordir1 = getSectorDir(p2d, 1);
3037 std::string sectordir;
3038 if(fs::PathExists(sectordir1))
3040 sectordir = sectordir1;
3045 sectordir = getSectorDir(p2d, 2);
3049 sector = loadSectorMeta(sectordir, loadlayout != 2);
3051 catch(InvalidFilenameException &e)
3055 catch(FileNotGoodException &e)
3059 catch(std::exception &e)
3068 bool ServerMap::loadSectorFull(v2s16 p2d)
3070 DSTACK(__FUNCTION_NAME);
3072 MapSector *sector = NULL;
3074 // The directory layout we're going to load from.
3075 // 1 - original sectors/xxxxzzzz/
3076 // 2 - new sectors2/xxx/zzz/
3077 // If we load from anything but the latest structure, we will
3078 // immediately save to the new one, and remove the old.
3080 std::string sectordir1 = getSectorDir(p2d, 1);
3081 std::string sectordir;
3082 if(fs::PathExists(sectordir1))
3084 sectordir = sectordir1;
3089 sectordir = getSectorDir(p2d, 2);
3093 sector = loadSectorMeta(sectordir, loadlayout != 2);
3095 catch(InvalidFilenameException &e)
3099 catch(FileNotGoodException &e)
3103 catch(std::exception &e)
3111 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3113 std::vector<fs::DirListNode>::iterator i2;
3114 for(i2=list2.begin(); i2!=list2.end(); i2++)
3120 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3122 catch(InvalidFilenameException &e)
3124 // This catches unknown crap in directory
3130 dstream<<"Sector converted to new layout - deleting "<<
3131 sectordir1<<std::endl;
3132 fs::RecursiveDelete(sectordir1);
3139 void ServerMap::saveBlock(MapBlock *block)
3141 DSTACK(__FUNCTION_NAME);
3143 Dummy blocks are not written
3145 if(block->isDummy())
3147 /*v3s16 p = block->getPos();
3148 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3149 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3153 // Format used for writing
3154 u8 version = SER_FMT_VER_HIGHEST;
3156 v3s16 p3d = block->getPos();
3158 v2s16 p2d(p3d.X, p3d.Z);
3159 std::string sectordir = getSectorDir(p2d);
3161 createDirs(sectordir);
3163 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3164 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3165 if(o.good() == false)
3166 throw FileNotGoodException("Cannot open block data");
3169 [0] u8 serialization version
3172 o.write((char*)&version, 1);
3175 block->serialize(o, version);
3177 // Write extra data stored on disk
3178 block->serializeDiskExtra(o, version);
3180 // We just wrote it to the disk so clear modified flag
3181 block->resetModified();
3184 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3186 DSTACK(__FUNCTION_NAME);
3188 std::string fullpath = sectordir+"/"+blockfile;
3191 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3192 if(is.good() == false)
3193 throw FileNotGoodException("Cannot open block file");
3195 v3s16 p3d = getBlockPos(sectordir, blockfile);
3196 v2s16 p2d(p3d.X, p3d.Z);
3198 assert(sector->getPos() == p2d);
3200 u8 version = SER_FMT_VER_INVALID;
3201 is.read((char*)&version, 1);
3204 throw SerializationError("ServerMap::loadBlock(): Failed"
3205 " to read MapBlock version");
3207 /*u32 block_size = MapBlock::serializedLength(version);
3208 SharedBuffer<u8> data(block_size);
3209 is.read((char*)*data, block_size);*/
3211 // This will always return a sector because we're the server
3212 //MapSector *sector = emergeSector(p2d);
3214 MapBlock *block = NULL;
3215 bool created_new = false;
3216 block = sector->getBlockNoCreateNoEx(p3d.Y);
3219 block = sector->createBlankBlockNoInsert(p3d.Y);
3224 block->deSerialize(is, version);
3226 // Read extra data stored on disk
3227 block->deSerializeDiskExtra(is, version);
3229 // If it's a new block, insert it to the map
3231 sector->insertBlock(block);
3234 Save blocks loaded in old format in new format
3237 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3242 // We just loaded it from the disk, so it's up-to-date.
3243 block->resetModified();
3246 catch(SerializationError &e)
3248 dstream<<"WARNING: Invalid block data on disk "
3249 <<"fullpath="<<fullpath
3250 <<" (SerializationError). "
3251 <<"what()="<<e.what()
3253 //" Ignoring. A new one will be generated.
3256 // TODO: Backup file; name is in fullpath.
3260 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3262 DSTACK(__FUNCTION_NAME);
3264 v2s16 p2d(blockpos.X, blockpos.Z);
3266 // The directory layout we're going to load from.
3267 // 1 - original sectors/xxxxzzzz/
3268 // 2 - new sectors2/xxx/zzz/
3269 // If we load from anything but the latest structure, we will
3270 // immediately save to the new one, and remove the old.
3272 std::string sectordir1 = getSectorDir(p2d, 1);
3273 std::string sectordir;
3274 if(fs::PathExists(sectordir1))
3276 sectordir = sectordir1;
3281 sectordir = getSectorDir(p2d, 2);
3285 Make sure sector is loaded
3287 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3291 sector = loadSectorMeta(sectordir, loadlayout != 2);
3293 catch(InvalidFilenameException &e)
3297 catch(FileNotGoodException &e)
3301 catch(std::exception &e)
3308 Make sure file exists
3311 std::string blockfilename = getBlockFilename(blockpos);
3312 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3318 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3319 return getBlockNoCreateNoEx(blockpos);
3322 void ServerMap::PrintInfo(std::ostream &out)
3333 ClientMap::ClientMap(
3335 MapDrawControl &control,
3336 scene::ISceneNode* parent,
3337 scene::ISceneManager* mgr,
3341 scene::ISceneNode(parent, mgr, id),
3344 m_camera_position(0,0,0),
3345 m_camera_direction(0,0,1)
3347 m_camera_mutex.Init();
3348 assert(m_camera_mutex.IsInitialized());
3350 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3351 BS*1000000,BS*1000000,BS*1000000);
3354 ClientMap::~ClientMap()
3356 /*JMutexAutoLock lock(mesh_mutex);
3365 MapSector * ClientMap::emergeSector(v2s16 p2d)
3367 DSTACK(__FUNCTION_NAME);
3368 // Check that it doesn't exist already
3370 return getSectorNoGenerate(p2d);
3372 catch(InvalidPositionException &e)
3377 ClientMapSector *sector = new ClientMapSector(this, p2d);
3380 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3381 m_sectors.insert(p2d, sector);
3388 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3390 DSTACK(__FUNCTION_NAME);
3391 ClientMapSector *sector = NULL;
3393 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3395 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3399 sector = (ClientMapSector*)n->getValue();
3400 assert(sector->getId() == MAPSECTOR_CLIENT);
3404 sector = new ClientMapSector(this, p2d);
3406 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3407 m_sectors.insert(p2d, sector);
3411 sector->deSerialize(is);
3415 void ClientMap::OnRegisterSceneNode()
3419 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3420 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3423 ISceneNode::OnRegisterSceneNode();
3426 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3428 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3429 DSTACK(__FUNCTION_NAME);
3431 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3434 This is called two times per frame, reset on the non-transparent one
3436 if(pass == scene::ESNRP_SOLID)
3438 m_last_drawn_sectors.clear();
3442 Get time for measuring timeout.
3444 Measuring time is very useful for long delays when the
3445 machine is swapping a lot.
3447 int time1 = time(0);
3449 //u32 daynight_ratio = m_client->getDayNightRatio();
3451 m_camera_mutex.Lock();
3452 v3f camera_position = m_camera_position;
3453 v3f camera_direction = m_camera_direction;
3454 m_camera_mutex.Unlock();
3457 Get all blocks and draw all visible ones
3460 v3s16 cam_pos_nodes(
3461 camera_position.X / BS,
3462 camera_position.Y / BS,
3463 camera_position.Z / BS);
3465 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3467 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3468 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3470 // Take a fair amount as we will be dropping more out later
3472 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3473 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3474 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3476 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3477 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3478 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3480 u32 vertex_count = 0;
3482 // For limiting number of mesh updates per frame
3483 u32 mesh_update_count = 0;
3485 u32 blocks_would_have_drawn = 0;
3486 u32 blocks_drawn = 0;
3488 int timecheck_counter = 0;
3489 core::map<v2s16, MapSector*>::Iterator si;
3490 si = m_sectors.getIterator();
3491 for(; si.atEnd() == false; si++)
3494 timecheck_counter++;
3495 if(timecheck_counter > 50)
3497 timecheck_counter = 0;
3498 int time2 = time(0);
3499 if(time2 > time1 + 4)
3501 dstream<<"ClientMap::renderMap(): "
3502 "Rendering takes ages, returning."
3509 MapSector *sector = si.getNode()->getValue();
3510 v2s16 sp = sector->getPos();
3512 if(m_control.range_all == false)
3514 if(sp.X < p_blocks_min.X
3515 || sp.X > p_blocks_max.X
3516 || sp.Y < p_blocks_min.Z
3517 || sp.Y > p_blocks_max.Z)
3521 core::list< MapBlock * > sectorblocks;
3522 sector->getBlocks(sectorblocks);
3528 u32 sector_blocks_drawn = 0;
3530 core::list< MapBlock * >::Iterator i;
3531 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3533 MapBlock *block = *i;
3536 Compare block position to camera position, skip
3537 if not seen on display
3540 float range = 100000 * BS;
3541 if(m_control.range_all == false)
3542 range = m_control.wanted_range * BS;
3545 if(isBlockInSight(block->getPos(), camera_position,
3546 camera_direction, range, &d) == false)
3551 // Okay, this block will be drawn. Reset usage timer.
3552 block->resetUsageTimer();
3554 // This is ugly (spherical distance limit?)
3555 /*if(m_control.range_all == false &&
3556 d - 0.5*BS*MAP_BLOCKSIZE > range)
3561 Update expired mesh (used for day/night change)
3563 It doesn't work exactly like it should now with the
3564 tasked mesh update but whatever.
3567 bool mesh_expired = false;
3570 JMutexAutoLock lock(block->mesh_mutex);
3572 mesh_expired = block->getMeshExpired();
3574 // Mesh has not been expired and there is no mesh:
3575 // block has no content
3576 if(block->mesh == NULL && mesh_expired == false)
3580 f32 faraway = BS*50;
3581 //f32 faraway = m_control.wanted_range * BS;
3584 This has to be done with the mesh_mutex unlocked
3586 // Pretty random but this should work somewhat nicely
3587 if(mesh_expired && (
3588 (mesh_update_count < 3
3589 && (d < faraway || mesh_update_count < 2)
3592 (m_control.range_all && mesh_update_count < 20)
3595 /*if(mesh_expired && mesh_update_count < 6
3596 && (d < faraway || mesh_update_count < 3))*/
3598 mesh_update_count++;
3600 // Mesh has been expired: generate new mesh
3601 //block->updateMesh(daynight_ratio);
3602 m_client->addUpdateMeshTask(block->getPos());
3604 mesh_expired = false;
3609 Draw the faces of the block
3612 JMutexAutoLock lock(block->mesh_mutex);
3614 scene::SMesh *mesh = block->mesh;
3619 blocks_would_have_drawn++;
3620 if(blocks_drawn >= m_control.wanted_max_blocks
3621 && m_control.range_all == false
3622 && d > m_control.wanted_min_range * BS)
3626 sector_blocks_drawn++;
3628 u32 c = mesh->getMeshBufferCount();
3630 for(u32 i=0; i<c; i++)
3632 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3633 const video::SMaterial& material = buf->getMaterial();
3634 video::IMaterialRenderer* rnd =
3635 driver->getMaterialRenderer(material.MaterialType);
3636 bool transparent = (rnd && rnd->isTransparent());
3637 // Render transparent on transparent pass and likewise.
3638 if(transparent == is_transparent_pass)
3641 This *shouldn't* hurt too much because Irrlicht
3642 doesn't change opengl textures if the old
3643 material is set again.
3645 driver->setMaterial(buf->getMaterial());
3646 driver->drawMeshBuffer(buf);
3647 vertex_count += buf->getVertexCount();
3651 } // foreach sectorblocks
3653 if(sector_blocks_drawn != 0)
3655 m_last_drawn_sectors[sp] = true;
3659 m_control.blocks_drawn = blocks_drawn;
3660 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3662 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3663 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3666 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3667 core::map<v3s16, MapBlock*> *affected_blocks)
3669 bool changed = false;
3671 Add it to all blocks touching it
3674 v3s16(0,0,0), // this
3675 v3s16(0,0,1), // back
3676 v3s16(0,1,0), // top
3677 v3s16(1,0,0), // right
3678 v3s16(0,0,-1), // front
3679 v3s16(0,-1,0), // bottom
3680 v3s16(-1,0,0), // left
3682 for(u16 i=0; i<7; i++)
3684 v3s16 p2 = p + dirs[i];
3685 // Block position of neighbor (or requested) node
3686 v3s16 blockpos = getNodeBlockPos(p2);
3687 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3688 if(blockref == NULL)
3690 // Relative position of requested node
3691 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3692 if(blockref->setTempMod(relpos, mod))
3697 if(changed && affected_blocks!=NULL)
3699 for(u16 i=0; i<7; i++)
3701 v3s16 p2 = p + dirs[i];
3702 // Block position of neighbor (or requested) node
3703 v3s16 blockpos = getNodeBlockPos(p2);
3704 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3705 if(blockref == NULL)
3707 affected_blocks->insert(blockpos, blockref);
3713 bool ClientMap::clearTempMod(v3s16 p,
3714 core::map<v3s16, MapBlock*> *affected_blocks)
3716 bool changed = false;
3718 v3s16(0,0,0), // this
3719 v3s16(0,0,1), // back
3720 v3s16(0,1,0), // top
3721 v3s16(1,0,0), // right
3722 v3s16(0,0,-1), // front
3723 v3s16(0,-1,0), // bottom
3724 v3s16(-1,0,0), // left
3726 for(u16 i=0; i<7; i++)
3728 v3s16 p2 = p + dirs[i];
3729 // Block position of neighbor (or requested) node
3730 v3s16 blockpos = getNodeBlockPos(p2);
3731 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3732 if(blockref == NULL)
3734 // Relative position of requested node
3735 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3736 if(blockref->clearTempMod(relpos))
3741 if(changed && affected_blocks!=NULL)
3743 for(u16 i=0; i<7; i++)
3745 v3s16 p2 = p + dirs[i];
3746 // Block position of neighbor (or requested) node
3747 v3s16 blockpos = getNodeBlockPos(p2);
3748 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3749 if(blockref == NULL)
3751 affected_blocks->insert(blockpos, blockref);
3757 void ClientMap::expireMeshes(bool only_daynight_diffed)
3759 TimeTaker timer("expireMeshes()");
3761 core::map<v2s16, MapSector*>::Iterator si;
3762 si = m_sectors.getIterator();
3763 for(; si.atEnd() == false; si++)
3765 MapSector *sector = si.getNode()->getValue();
3767 core::list< MapBlock * > sectorblocks;
3768 sector->getBlocks(sectorblocks);
3770 core::list< MapBlock * >::Iterator i;
3771 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3773 MapBlock *block = *i;
3775 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3781 JMutexAutoLock lock(block->mesh_mutex);
3782 if(block->mesh != NULL)
3784 /*block->mesh->drop();
3785 block->mesh = NULL;*/
3786 block->setMeshExpired(true);
3793 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3795 assert(mapType() == MAPTYPE_CLIENT);
3798 v3s16 p = blockpos + v3s16(0,0,0);
3799 MapBlock *b = getBlockNoCreate(p);
3800 b->updateMesh(daynight_ratio);
3801 //b->setMeshExpired(true);
3803 catch(InvalidPositionException &e){}
3806 v3s16 p = blockpos + v3s16(-1,0,0);
3807 MapBlock *b = getBlockNoCreate(p);
3808 b->updateMesh(daynight_ratio);
3809 //b->setMeshExpired(true);
3811 catch(InvalidPositionException &e){}
3813 v3s16 p = blockpos + v3s16(0,-1,0);
3814 MapBlock *b = getBlockNoCreate(p);
3815 b->updateMesh(daynight_ratio);
3816 //b->setMeshExpired(true);
3818 catch(InvalidPositionException &e){}
3820 v3s16 p = blockpos + v3s16(0,0,-1);
3821 MapBlock *b = getBlockNoCreate(p);
3822 b->updateMesh(daynight_ratio);
3823 //b->setMeshExpired(true);
3825 catch(InvalidPositionException &e){}
3830 Update mesh of block in which the node is, and if the node is at the
3831 leading edge, update the appropriate leading blocks too.
3833 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3841 v3s16 blockposes[4];
3842 for(u32 i=0; i<4; i++)
3844 v3s16 np = nodepos + dirs[i];
3845 blockposes[i] = getNodeBlockPos(np);
3846 // Don't update mesh of block if it has been done already
3847 bool already_updated = false;
3848 for(u32 j=0; j<i; j++)
3850 if(blockposes[j] == blockposes[i])
3852 already_updated = true;
3859 MapBlock *b = getBlockNoCreate(blockposes[i]);
3860 b->updateMesh(daynight_ratio);
3865 void ClientMap::PrintInfo(std::ostream &out)
3876 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3881 MapVoxelManipulator::~MapVoxelManipulator()
3883 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3887 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3889 TimeTaker timer1("emerge", &emerge_time);
3891 // Units of these are MapBlocks
3892 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3893 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3895 VoxelArea block_area_nodes
3896 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3898 addArea(block_area_nodes);
3900 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3901 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3902 for(s32 x=p_min.X; x<=p_max.X; x++)
3905 core::map<v3s16, bool>::Node *n;
3906 n = m_loaded_blocks.find(p);
3910 bool block_data_inexistent = false;
3913 TimeTaker timer1("emerge load", &emerge_load_time);
3915 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3916 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3919 dstream<<std::endl;*/
3921 MapBlock *block = m_map->getBlockNoCreate(p);
3922 if(block->isDummy())
3923 block_data_inexistent = true;
3925 block->copyTo(*this);
3927 catch(InvalidPositionException &e)
3929 block_data_inexistent = true;
3932 if(block_data_inexistent)
3934 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3935 // Fill with VOXELFLAG_INEXISTENT
3936 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3937 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3939 s32 i = m_area.index(a.MinEdge.X,y,z);
3940 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3944 m_loaded_blocks.insert(p, !block_data_inexistent);
3947 //dstream<<"emerge done"<<std::endl;
3951 SUGG: Add an option to only update eg. water and air nodes.
3952 This will make it interfere less with important stuff if
3955 void MapVoxelManipulator::blitBack
3956 (core::map<v3s16, MapBlock*> & modified_blocks)
3958 if(m_area.getExtent() == v3s16(0,0,0))
3961 //TimeTaker timer1("blitBack");
3963 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3964 <<m_loaded_blocks.size()<<std::endl;*/
3967 Initialize block cache
3969 v3s16 blockpos_last;
3970 MapBlock *block = NULL;
3971 bool block_checked_in_modified = false;
3973 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3974 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3975 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3979 u8 f = m_flags[m_area.index(p)];
3980 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3983 MapNode &n = m_data[m_area.index(p)];
3985 v3s16 blockpos = getNodeBlockPos(p);
3990 if(block == NULL || blockpos != blockpos_last){
3991 block = m_map->getBlockNoCreate(blockpos);
3992 blockpos_last = blockpos;
3993 block_checked_in_modified = false;
3996 // Calculate relative position in block
3997 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3999 // Don't continue if nothing has changed here
4000 if(block->getNode(relpos) == n)
4003 //m_map->setNode(m_area.MinEdge + p, n);
4004 block->setNode(relpos, n);
4007 Make sure block is in modified_blocks
4009 if(block_checked_in_modified == false)
4011 modified_blocks[blockpos] = block;
4012 block_checked_in_modified = true;
4015 catch(InvalidPositionException &e)
4021 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4022 MapVoxelManipulator(map),
4023 m_create_area(false)
4027 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4031 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4033 // Just create the area so that it can be pointed to
4034 VoxelManipulator::emerge(a, caller_id);
4037 void ManualMapVoxelManipulator::initialEmerge(
4038 v3s16 blockpos_min, v3s16 blockpos_max)
4040 TimeTaker timer1("initialEmerge", &emerge_time);
4042 // Units of these are MapBlocks
4043 v3s16 p_min = blockpos_min;
4044 v3s16 p_max = blockpos_max;
4046 VoxelArea block_area_nodes
4047 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4049 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4052 dstream<<"initialEmerge: area: ";
4053 block_area_nodes.print(dstream);
4054 dstream<<" ("<<size_MB<<"MB)";
4058 addArea(block_area_nodes);
4060 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4061 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4062 for(s32 x=p_min.X; x<=p_max.X; x++)
4065 core::map<v3s16, bool>::Node *n;
4066 n = m_loaded_blocks.find(p);
4070 bool block_data_inexistent = false;
4073 TimeTaker timer1("emerge load", &emerge_load_time);
4075 MapBlock *block = m_map->getBlockNoCreate(p);
4076 if(block->isDummy())
4077 block_data_inexistent = true;
4079 block->copyTo(*this);
4081 catch(InvalidPositionException &e)
4083 block_data_inexistent = true;
4086 if(block_data_inexistent)
4089 Mark area inexistent
4091 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4092 // Fill with VOXELFLAG_INEXISTENT
4093 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4094 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4096 s32 i = m_area.index(a.MinEdge.X,y,z);
4097 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4101 m_loaded_blocks.insert(p, !block_data_inexistent);
4105 void ManualMapVoxelManipulator::blitBackAll(
4106 core::map<v3s16, MapBlock*> * modified_blocks)
4108 if(m_area.getExtent() == v3s16(0,0,0))
4112 Copy data of all blocks
4114 for(core::map<v3s16, bool>::Iterator
4115 i = m_loaded_blocks.getIterator();
4116 i.atEnd() == false; i++)
4118 v3s16 p = i.getNode()->getKey();
4119 bool existed = i.getNode()->getValue();
4120 if(existed == false)
4122 // The Great Bug was found using this
4123 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4124 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4128 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4131 dstream<<"WARNING: "<<__FUNCTION_NAME
4132 <<": got NULL block "
4133 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4138 block->copyFrom(*this);
4141 modified_blocks->insert(p, block);