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()) || n2.getContent() == CONTENT_AIR)
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()) || n2.getContent() == CONTENT_AIR)
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
1545 NEIGHBOR_SAME_LEVEL,
1548 struct NodeNeighbor {
1554 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1556 DSTACK(__FUNCTION_NAME);
1557 //TimeTaker timer("transformLiquids()");
1560 u32 initial_size = m_transforming_liquid.size();
1562 /*if(initial_size != 0)
1563 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1565 while(m_transforming_liquid.size() != 0)
1568 Get a queued transforming liquid node
1570 v3s16 p0 = m_transforming_liquid.pop_front();
1572 MapNode n0 = getNodeNoEx(p0);
1575 Collect information about current node
1577 s8 liquid_level = -1;
1578 u8 liquid_kind = CONTENT_IGNORE;
1579 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1580 switch (liquid_type) {
1583 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1585 case LIQUID_FLOWING:
1586 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1587 liquid_kind = n0.getContent();
1590 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1591 // continue with the next node.
1592 if (n0.getContent() != CONTENT_AIR)
1594 liquid_kind = CONTENT_AIR;
1599 Collect information about the environment
1602 v3s16( 0, 1, 0), // top
1603 v3s16( 0,-1, 0), // bottom
1604 v3s16( 1, 0, 0), // right
1605 v3s16(-1, 0, 0), // left
1606 v3s16( 0, 0, 1), // back
1607 v3s16( 0, 0,-1), // front
1609 NodeNeighbor sources[6]; // surrounding sources
1610 int num_sources = 0;
1611 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1613 NodeNeighbor airs[6]; // surrounding air
1615 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1616 int num_neutrals = 0;
1617 bool flowing_down = false;
1618 for (u16 i = 0; i < 6; i++) {
1619 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1622 nt = NEIGHBOR_UPPER;
1625 nt = NEIGHBOR_LOWER;
1628 v3s16 npos = p0 + dirs[i];
1629 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1630 switch (content_features(nb.n.getContent()).liquid_type) {
1632 if (nb.n.getContent() == CONTENT_AIR) {
1633 airs[num_airs++] = nb;
1634 // if the current node happens to be a flowing node, it will start to flow down here.
1635 if (nb.t == NEIGHBOR_LOWER)
1636 flowing_down = true;
1638 neutrals[num_neutrals++] = nb;
1642 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1643 if (liquid_kind == CONTENT_AIR)
1644 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1645 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1646 neutrals[num_neutrals++] = nb;
1648 sources[num_sources++] = nb;
1651 case LIQUID_FLOWING:
1652 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1653 if (liquid_kind == CONTENT_AIR)
1654 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1655 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1656 neutrals[num_neutrals++] = nb;
1658 flows[num_flows++] = nb;
1659 if (nb.t == NEIGHBOR_LOWER)
1660 flowing_down = true;
1667 decide on the type (and possibly level) of the current node
1669 content_t new_node_content;
1670 s8 new_node_level = -1;
1671 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1672 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1673 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1674 // it's perfectly safe to use liquid_kind here to determine the new node content.
1675 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1676 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1677 // liquid_kind is set properly, see above
1678 new_node_content = liquid_kind;
1681 // no surrounding sources, so get the maximum level that can flow into this node
1682 for (u16 i = 0; i < num_flows; i++) {
1683 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1684 switch (flows[i].t) {
1685 case NEIGHBOR_UPPER:
1686 if (nb_liquid_level + WATER_DROP_BOOST > new_node_level) {
1688 if (nb_liquid_level + WATER_DROP_BOOST < 7)
1689 new_node_level = nb_liquid_level + WATER_DROP_BOOST;
1692 case NEIGHBOR_LOWER:
1694 case NEIGHBOR_SAME_LEVEL:
1695 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1696 nb_liquid_level > 0 && nb_liquid_level - 1 > new_node_level) {
1697 new_node_level = nb_liquid_level - 1;
1702 // don't flow as far in open terrain - if there isn't at least one adjacent solid block,
1703 // substract another unit from the resulting water level.
1704 if (!flowing_down && new_node_level >= 1) {
1705 bool at_wall = false;
1706 for (u16 i = 0; i < num_neutrals; i++) {
1707 if (neutrals[i].t == NEIGHBOR_SAME_LEVEL) {
1713 new_node_level -= 1;
1716 if (new_node_level >= 0)
1717 new_node_content = liquid_kind;
1719 new_node_content = CONTENT_AIR;
1723 check if anything has changed. if not, just continue with the next node.
1725 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1726 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1727 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1733 update the current node
1735 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1736 n0.setContent(new_node_content);
1737 if (content_features(n0.getContent()).liquid_type == LIQUID_FLOWING) {
1738 // set level to last 3 bits, flowing down bit to 4th bit
1739 n0.param2 |= (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1741 // set the liquid level and flow bit to 0
1742 n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1745 v3s16 blockpos = getNodeBlockPos(p0);
1746 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1748 modified_blocks.insert(blockpos, block);
1751 enqueue neighbors for update if neccessary
1753 switch (content_features(n0.getContent()).liquid_type) {
1755 // make sure source flows into all neighboring nodes
1756 for (u16 i = 0; i < num_flows; i++)
1757 if (flows[i].t != NEIGHBOR_UPPER)
1758 m_transforming_liquid.push_back(flows[i].p);
1759 for (u16 i = 0; i < num_airs; i++)
1760 if (airs[i].t != NEIGHBOR_UPPER)
1761 m_transforming_liquid.push_back(airs[i].p);
1764 // this flow has turned to air; neighboring flows might need to do the same
1765 for (u16 i = 0; i < num_flows; i++)
1766 m_transforming_liquid.push_back(flows[i].p);
1768 case LIQUID_FLOWING:
1769 for (u16 i = 0; i < num_flows; i++) {
1770 u8 flow_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1771 // liquid_level is still the ORIGINAL level of this node.
1772 if (flows[i].t != NEIGHBOR_UPPER && ((flow_level < liquid_level || flow_level < new_node_level) ||
1774 m_transforming_liquid.push_back(flows[i].p);
1776 for (u16 i = 0; i < num_airs; i++) {
1777 if (airs[i].t != NEIGHBOR_UPPER && (airs[i].t == NEIGHBOR_LOWER || new_node_level > 0))
1778 m_transforming_liquid.push_back(airs[i].p);
1784 //if(loopcount >= 100000)
1785 if(loopcount >= initial_size * 10) {
1789 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1792 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1794 v3s16 blockpos = getNodeBlockPos(p);
1795 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1796 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1799 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1803 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1807 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1809 v3s16 blockpos = getNodeBlockPos(p);
1810 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1811 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1814 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1818 block->m_node_metadata.set(p_rel, meta);
1821 void Map::removeNodeMetadata(v3s16 p)
1823 v3s16 blockpos = getNodeBlockPos(p);
1824 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1825 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1828 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1832 block->m_node_metadata.remove(p_rel);
1835 void Map::nodeMetadataStep(float dtime,
1836 core::map<v3s16, MapBlock*> &changed_blocks)
1840 Currently there is no way to ensure that all the necessary
1841 blocks are loaded when this is run. (They might get unloaded)
1842 NOTE: ^- Actually, that might not be so. In a quick test it
1843 reloaded a block with a furnace when I walked back to it from
1846 core::map<v2s16, MapSector*>::Iterator si;
1847 si = m_sectors.getIterator();
1848 for(; si.atEnd() == false; si++)
1850 MapSector *sector = si.getNode()->getValue();
1851 core::list< MapBlock * > sectorblocks;
1852 sector->getBlocks(sectorblocks);
1853 core::list< MapBlock * >::Iterator i;
1854 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1856 MapBlock *block = *i;
1857 bool changed = block->m_node_metadata.step(dtime);
1859 changed_blocks[block->getPos()] = block;
1868 ServerMap::ServerMap(std::string savedir):
1871 m_map_metadata_changed(true)
1873 dstream<<__FUNCTION_NAME<<std::endl;
1875 //m_chunksize = 8; // Takes a few seconds
1877 m_seed = (((u64)(myrand()%0xffff)<<0)
1878 + ((u64)(myrand()%0xffff)<<16)
1879 + ((u64)(myrand()%0xffff)<<32)
1880 + ((u64)(myrand()%0xffff)<<48));
1883 Experimental and debug stuff
1890 Try to load map; if not found, create a new one.
1893 m_savedir = savedir;
1894 m_map_saving_enabled = false;
1898 // If directory exists, check contents and load if possible
1899 if(fs::PathExists(m_savedir))
1901 // If directory is empty, it is safe to save into it.
1902 if(fs::GetDirListing(m_savedir).size() == 0)
1904 dstream<<DTIME<<"Server: Empty save directory is valid."
1906 m_map_saving_enabled = true;
1911 // Load map metadata (seed, chunksize)
1914 catch(FileNotGoodException &e){
1915 dstream<<DTIME<<"WARNING: Could not load map metadata"
1916 //<<" Disabling chunk-based generator."
1922 // Load chunk metadata
1925 catch(FileNotGoodException &e){
1926 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1927 <<" Disabling chunk-based generator."
1932 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1933 "metadata and sector (0,0) from "<<savedir<<
1934 ", assuming valid save directory."
1937 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1938 <<"and chunk metadata from "<<savedir
1939 <<", assuming valid save directory."
1942 m_map_saving_enabled = true;
1943 // Map loaded, not creating new one
1947 // If directory doesn't exist, it is safe to save to it
1949 m_map_saving_enabled = true;
1952 catch(std::exception &e)
1954 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1955 <<", exception: "<<e.what()<<std::endl;
1956 dstream<<"Please remove the map or fix it."<<std::endl;
1957 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1960 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1962 // Create zero sector
1963 emergeSector(v2s16(0,0));
1965 // Initially write whole map
1969 ServerMap::~ServerMap()
1971 dstream<<__FUNCTION_NAME<<std::endl;
1975 if(m_map_saving_enabled)
1977 // Save only changed parts
1979 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1983 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1986 catch(std::exception &e)
1988 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1989 <<", exception: "<<e.what()<<std::endl;
1996 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1997 for(; i.atEnd() == false; i++)
1999 MapChunk *chunk = i.getNode()->getValue();
2005 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2007 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2008 if(enable_mapgen_debug_info)
2009 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2010 <<blockpos.Z<<")"<<std::endl;
2012 // Do nothing if not inside limits (+-1 because of neighbors)
2013 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2014 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2020 data->no_op = false;
2021 data->seed = m_seed;
2022 data->blockpos = blockpos;
2025 Create the whole area of this and the neighboring blocks
2028 //TimeTaker timer("initBlockMake() create area");
2030 for(s16 x=-1; x<=1; x++)
2031 for(s16 z=-1; z<=1; z++)
2033 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2034 // Sector metadata is loaded from disk if not already loaded.
2035 ServerMapSector *sector = createSector(sectorpos);
2038 for(s16 y=-1; y<=1; y++)
2040 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2041 //MapBlock *block = createBlock(p);
2042 // 1) get from memory, 2) load from disk
2043 MapBlock *block = emergeBlock(p, false);
2044 // 3) create a blank one
2047 block = createBlock(p);
2050 Block gets sunlight if this is true.
2052 Refer to the map generator heuristics.
2054 bool ug = mapgen::block_is_underground(data->seed, p);
2055 block->setIsUnderground(ug);
2058 // Lighting will not be valid after make_chunk is called
2059 block->setLightingExpired(true);
2060 // Lighting will be calculated
2061 //block->setLightingExpired(false);
2067 Now we have a big empty area.
2069 Make a ManualMapVoxelManipulator that contains this and the
2073 // The area that contains this block and it's neighbors
2074 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2075 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2077 data->vmanip = new ManualMapVoxelManipulator(this);
2078 //data->vmanip->setMap(this);
2082 //TimeTaker timer("initBlockMake() initialEmerge");
2083 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2086 // Data is ready now.
2089 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2090 core::map<v3s16, MapBlock*> &changed_blocks)
2092 v3s16 blockpos = data->blockpos;
2093 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2094 <<blockpos.Z<<")"<<std::endl;*/
2098 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2102 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2104 /*dstream<<"Resulting vmanip:"<<std::endl;
2105 data->vmanip.print(dstream);*/
2108 Blit generated stuff to map
2109 NOTE: blitBackAll adds nearly everything to changed_blocks
2113 //TimeTaker timer("finishBlockMake() blitBackAll");
2114 data->vmanip->blitBackAll(&changed_blocks);
2117 if(enable_mapgen_debug_info)
2118 dstream<<"finishBlockMake: changed_blocks.size()="
2119 <<changed_blocks.size()<<std::endl;
2122 Copy transforming liquid information
2124 while(data->transforming_liquid.size() > 0)
2126 v3s16 p = data->transforming_liquid.pop_front();
2127 m_transforming_liquid.push_back(p);
2133 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2137 Set is_underground flag for lighting with sunlight.
2139 Refer to map generator heuristics.
2141 NOTE: This is done in initChunkMake
2143 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2147 Add sunlight to central block.
2148 This makes in-dark-spawning monsters to not flood the whole thing.
2149 Do not spread the light, though.
2151 /*core::map<v3s16, bool> light_sources;
2152 bool black_air_left = false;
2153 block->propagateSunlight(light_sources, true, &black_air_left);*/
2156 NOTE: Lighting and object adding shouldn't really be here, but
2157 lighting is a bit tricky to move properly to makeBlock.
2158 TODO: Do this the right way anyway, that is, move it to makeBlock.
2159 - There needs to be some way for makeBlock to report back if
2160 the lighting update is going further down because of the
2161 new block blocking light
2166 NOTE: This takes ~60ms, TODO: Investigate why
2169 TimeTaker t("finishBlockMake lighting update");
2171 core::map<v3s16, MapBlock*> lighting_update_blocks;
2174 lighting_update_blocks.insert(block->getPos(), block);
2179 v3s16 p = block->getPos()+v3s16(x,1,z);
2180 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2184 // All modified blocks
2185 // NOTE: Should this be done? If this is not done, then the lighting
2186 // of the others will be updated in a different place, one by one, i
2187 // think... or they might not? Well, at least they are left marked as
2188 // "lighting expired"; it seems that is not handled at all anywhere,
2189 // so enabling this will slow it down A LOT because otherwise it
2190 // would not do this at all. This causes the black trees.
2191 for(core::map<v3s16, MapBlock*>::Iterator
2192 i = changed_blocks.getIterator();
2193 i.atEnd() == false; i++)
2195 lighting_update_blocks.insert(i.getNode()->getKey(),
2196 i.getNode()->getValue());
2198 /*// Also force-add all the upmost blocks for proper sunlight
2199 for(s16 x=-1; x<=1; x++)
2200 for(s16 z=-1; z<=1; z++)
2202 v3s16 p = block->getPos()+v3s16(x,1,z);
2203 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2206 updateLighting(lighting_update_blocks, changed_blocks);
2209 Set lighting to non-expired state in all of them.
2210 This is cheating, but it is not fast enough if all of them
2211 would actually be updated.
2213 for(s16 x=-1; x<=1; x++)
2214 for(s16 y=-1; y<=1; y++)
2215 for(s16 z=-1; z<=1; z++)
2217 v3s16 p = block->getPos()+v3s16(x,y,z);
2218 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2221 if(enable_mapgen_debug_info == false)
2222 t.stop(true); // Hide output
2226 Add random objects to block
2228 mapgen::add_random_objects(block);
2231 Go through changed blocks
2233 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2234 i.atEnd() == false; i++)
2236 MapBlock *block = i.getNode()->getValue();
2239 Update day/night difference cache of the MapBlocks
2241 block->updateDayNightDiff();
2243 Set block as modified
2245 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2249 Set central block as generated
2251 block->setGenerated(true);
2254 Save changed parts of map
2255 NOTE: Will be saved later.
2259 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2260 <<blockpos.Z<<")"<<std::endl;*/
2262 if(enable_mapgen_debug_info)
2265 Analyze resulting blocks
2267 for(s16 x=-1; x<=1; x++)
2268 for(s16 y=-1; y<=1; y++)
2269 for(s16 z=-1; z<=1; z++)
2271 v3s16 p = block->getPos()+v3s16(x,y,z);
2272 MapBlock *block = getBlockNoCreateNoEx(p);
2274 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2275 dstream<<"Generated "<<spos<<": "
2276 <<analyze_block(block)<<std::endl;
2284 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2286 DSTACKF("%s: p2d=(%d,%d)",
2291 Check if it exists already in memory
2293 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2298 Try to load it from disk (with blocks)
2300 //if(loadSectorFull(p2d) == true)
2303 Try to load metadata from disk
2305 if(loadSectorMeta(p2d) == true)
2307 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2310 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2311 throw InvalidPositionException("");
2317 Do not create over-limit
2319 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2320 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2321 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2322 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2323 throw InvalidPositionException("createSector(): pos. over limit");
2326 Generate blank sector
2329 sector = new ServerMapSector(this, p2d);
2331 // Sector position on map in nodes
2332 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2337 m_sectors.insert(p2d, sector);
2343 This is a quick-hand function for calling makeBlock().
2345 MapBlock * ServerMap::generateBlock(
2347 core::map<v3s16, MapBlock*> &modified_blocks
2350 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2352 /*dstream<<"generateBlock(): "
2353 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2356 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2358 TimeTaker timer("generateBlock");
2360 //MapBlock *block = original_dummy;
2362 v2s16 p2d(p.X, p.Z);
2363 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2366 Do not generate over-limit
2368 if(blockpos_over_limit(p))
2370 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2371 throw InvalidPositionException("generateBlock(): pos. over limit");
2375 Create block make data
2377 mapgen::BlockMakeData data;
2378 initBlockMake(&data, p);
2384 TimeTaker t("mapgen::make_block()");
2385 mapgen::make_block(&data);
2387 if(enable_mapgen_debug_info == false)
2388 t.stop(true); // Hide output
2392 Blit data back on map, update lighting, add mobs and whatever this does
2394 finishBlockMake(&data, modified_blocks);
2399 MapBlock *block = getBlockNoCreateNoEx(p);
2407 bool erroneus_content = false;
2408 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2409 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2410 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2413 MapNode n = block->getNode(p);
2414 if(n.getContent() == CONTENT_IGNORE)
2416 dstream<<"CONTENT_IGNORE at "
2417 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2419 erroneus_content = true;
2423 if(erroneus_content)
2432 Generate a completely empty block
2436 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2437 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2439 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2443 n.setContent(CONTENT_AIR);
2445 n.setContent(CONTENT_STONE);
2446 block->setNode(v3s16(x0,y0,z0), n);
2452 if(enable_mapgen_debug_info == false)
2453 timer.stop(true); // Hide output
2458 MapBlock * ServerMap::createBlock(v3s16 p)
2460 DSTACKF("%s: p=(%d,%d,%d)",
2461 __FUNCTION_NAME, p.X, p.Y, p.Z);
2464 Do not create over-limit
2466 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2467 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2468 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2469 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2470 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2471 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2472 throw InvalidPositionException("createBlock(): pos. over limit");
2474 v2s16 p2d(p.X, p.Z);
2477 This will create or load a sector if not found in memory.
2478 If block exists on disk, it will be loaded.
2480 NOTE: On old save formats, this will be slow, as it generates
2481 lighting on blocks for them.
2483 ServerMapSector *sector;
2485 sector = (ServerMapSector*)createSector(p2d);
2486 assert(sector->getId() == MAPSECTOR_SERVER);
2488 catch(InvalidPositionException &e)
2490 dstream<<"createBlock: createSector() failed"<<std::endl;
2494 NOTE: This should not be done, or at least the exception
2495 should not be passed on as std::exception, because it
2496 won't be catched at all.
2498 /*catch(std::exception &e)
2500 dstream<<"createBlock: createSector() failed: "
2501 <<e.what()<<std::endl;
2506 Try to get a block from the sector
2509 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2512 if(block->isDummy())
2517 block = sector->createBlankBlock(block_y);
2521 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2523 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2525 p.X, p.Y, p.Z, allow_generate);
2528 MapBlock *block = getBlockNoCreateNoEx(p);
2529 if(block && block->isDummy() == false)
2534 MapBlock *block = loadBlock(p);
2541 core::map<v3s16, MapBlock*> modified_blocks;
2542 MapBlock *block = generateBlock(p, modified_blocks);
2546 event.type = MEET_OTHER;
2549 // Copy modified_blocks to event
2550 for(core::map<v3s16, MapBlock*>::Iterator
2551 i = modified_blocks.getIterator();
2552 i.atEnd()==false; i++)
2554 event.modified_blocks.insert(i.getNode()->getKey(), false);
2558 dispatchEvent(&event);
2569 Do not generate over-limit
2571 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2572 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2573 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2574 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2575 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2576 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2577 throw InvalidPositionException("emergeBlock(): pos. over limit");
2579 v2s16 p2d(p.X, p.Z);
2582 This will create or load a sector if not found in memory.
2583 If block exists on disk, it will be loaded.
2585 ServerMapSector *sector;
2587 sector = createSector(p2d);
2588 //sector = emergeSector(p2d, changed_blocks);
2590 catch(InvalidPositionException &e)
2592 dstream<<"emergeBlock: createSector() failed: "
2593 <<e.what()<<std::endl;
2594 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2596 <<"You could try to delete it."<<std::endl;
2599 catch(VersionMismatchException &e)
2601 dstream<<"emergeBlock: createSector() failed: "
2602 <<e.what()<<std::endl;
2603 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2605 <<"You could try to delete it."<<std::endl;
2610 Try to get a block from the sector
2613 bool does_not_exist = false;
2614 bool lighting_expired = false;
2615 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2617 // If not found, try loading from disk
2620 block = loadBlock(p);
2626 does_not_exist = true;
2628 else if(block->isDummy() == true)
2630 does_not_exist = true;
2632 else if(block->getLightingExpired())
2634 lighting_expired = true;
2639 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2644 If block was not found on disk and not going to generate a
2645 new one, make sure there is a dummy block in place.
2647 if(only_from_disk && (does_not_exist || lighting_expired))
2649 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2653 // Create dummy block
2654 block = new MapBlock(this, p, true);
2656 // Add block to sector
2657 sector->insertBlock(block);
2663 //dstream<<"Not found on disk, generating."<<std::endl;
2665 //TimeTaker("emergeBlock() generate");
2667 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2670 If the block doesn't exist, generate the block.
2674 block = generateBlock(p, block, sector, changed_blocks,
2675 lighting_invalidated_blocks);
2678 if(lighting_expired)
2680 lighting_invalidated_blocks.insert(p, block);
2685 Initially update sunlight
2688 core::map<v3s16, bool> light_sources;
2689 bool black_air_left = false;
2690 bool bottom_invalid =
2691 block->propagateSunlight(light_sources, true,
2694 // If sunlight didn't reach everywhere and part of block is
2695 // above ground, lighting has to be properly updated
2696 //if(black_air_left && some_part_underground)
2699 lighting_invalidated_blocks[block->getPos()] = block;
2704 lighting_invalidated_blocks[block->getPos()] = block;
2713 s16 ServerMap::findGroundLevel(v2s16 p2d)
2717 Uh, just do something random...
2719 // Find existing map from top to down
2722 v3s16 p(p2d.X, max, p2d.Y);
2723 for(; p.Y>min; p.Y--)
2725 MapNode n = getNodeNoEx(p);
2726 if(n.getContent() != CONTENT_IGNORE)
2731 // If this node is not air, go to plan b
2732 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2734 // Search existing walkable and return it
2735 for(; p.Y>min; p.Y--)
2737 MapNode n = getNodeNoEx(p);
2738 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2747 Determine from map generator noise functions
2750 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2753 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2754 //return (s16)level;
2757 void ServerMap::createDirs(std::string path)
2759 if(fs::CreateAllDirs(path) == false)
2761 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2762 <<"\""<<path<<"\""<<std::endl;
2763 throw BaseException("ServerMap failed to create directory");
2767 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2773 snprintf(cc, 9, "%.4x%.4x",
2774 (unsigned int)pos.X&0xffff,
2775 (unsigned int)pos.Y&0xffff);
2777 return m_savedir + "/sectors/" + cc;
2779 snprintf(cc, 9, "%.3x/%.3x",
2780 (unsigned int)pos.X&0xfff,
2781 (unsigned int)pos.Y&0xfff);
2783 return m_savedir + "/sectors2/" + cc;
2789 v2s16 ServerMap::getSectorPos(std::string dirname)
2793 size_t spos = dirname.rfind('/') + 1;
2794 assert(spos != std::string::npos);
2795 if(dirname.size() - spos == 8)
2798 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2800 else if(dirname.size() - spos == 3)
2803 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2804 // Sign-extend the 12 bit values up to 16 bits...
2805 if(x&0x800) x|=0xF000;
2806 if(y&0x800) y|=0xF000;
2813 v2s16 pos((s16)x, (s16)y);
2817 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2819 v2s16 p2d = getSectorPos(sectordir);
2821 if(blockfile.size() != 4){
2822 throw InvalidFilenameException("Invalid block filename");
2825 int r = sscanf(blockfile.c_str(), "%4x", &y);
2827 throw InvalidFilenameException("Invalid block filename");
2828 return v3s16(p2d.X, y, p2d.Y);
2831 std::string ServerMap::getBlockFilename(v3s16 p)
2834 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2838 void ServerMap::save(bool only_changed)
2840 DSTACK(__FUNCTION_NAME);
2841 if(m_map_saving_enabled == false)
2843 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2847 if(only_changed == false)
2848 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2851 if(only_changed == false || m_map_metadata_changed)
2856 u32 sector_meta_count = 0;
2857 u32 block_count = 0;
2858 u32 block_count_all = 0; // Number of blocks in memory
2860 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2861 for(; i.atEnd() == false; i++)
2863 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2864 assert(sector->getId() == MAPSECTOR_SERVER);
2866 if(sector->differs_from_disk || only_changed == false)
2868 saveSectorMeta(sector);
2869 sector_meta_count++;
2871 core::list<MapBlock*> blocks;
2872 sector->getBlocks(blocks);
2873 core::list<MapBlock*>::Iterator j;
2874 for(j=blocks.begin(); j!=blocks.end(); j++)
2876 MapBlock *block = *j;
2880 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2881 || only_changed == false)
2886 /*dstream<<"ServerMap: Written block ("
2887 <<block->getPos().X<<","
2888 <<block->getPos().Y<<","
2889 <<block->getPos().Z<<")"
2896 Only print if something happened or saved whole map
2898 if(only_changed == false || sector_meta_count != 0
2899 || block_count != 0)
2901 dstream<<DTIME<<"ServerMap: Written: "
2902 <<sector_meta_count<<" sector metadata files, "
2903 <<block_count<<" block files"
2904 <<", "<<block_count_all<<" blocks in memory."
2909 void ServerMap::saveMapMeta()
2911 DSTACK(__FUNCTION_NAME);
2913 dstream<<"INFO: ServerMap::saveMapMeta(): "
2917 createDirs(m_savedir);
2919 std::string fullpath = m_savedir + "/map_meta.txt";
2920 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2921 if(os.good() == false)
2923 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2924 <<"could not open"<<fullpath<<std::endl;
2925 throw FileNotGoodException("Cannot open chunk metadata");
2929 params.setU64("seed", m_seed);
2931 params.writeLines(os);
2933 os<<"[end_of_params]\n";
2935 m_map_metadata_changed = false;
2938 void ServerMap::loadMapMeta()
2940 DSTACK(__FUNCTION_NAME);
2942 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2945 std::string fullpath = m_savedir + "/map_meta.txt";
2946 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2947 if(is.good() == false)
2949 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2950 <<"could not open"<<fullpath<<std::endl;
2951 throw FileNotGoodException("Cannot open map metadata");
2959 throw SerializationError
2960 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2962 std::getline(is, line);
2963 std::string trimmedline = trim(line);
2964 if(trimmedline == "[end_of_params]")
2966 params.parseConfigLine(line);
2969 m_seed = params.getU64("seed");
2971 dstream<<"INFO: ServerMap::loadMapMeta(): "
2976 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2978 DSTACK(__FUNCTION_NAME);
2979 // Format used for writing
2980 u8 version = SER_FMT_VER_HIGHEST;
2982 v2s16 pos = sector->getPos();
2983 std::string dir = getSectorDir(pos);
2986 std::string fullpath = dir + "/meta";
2987 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2988 if(o.good() == false)
2989 throw FileNotGoodException("Cannot open sector metafile");
2991 sector->serialize(o, version);
2993 sector->differs_from_disk = false;
2996 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2998 DSTACK(__FUNCTION_NAME);
3000 v2s16 p2d = getSectorPos(sectordir);
3002 ServerMapSector *sector = NULL;
3004 std::string fullpath = sectordir + "/meta";
3005 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3006 if(is.good() == false)
3008 // If the directory exists anyway, it probably is in some old
3009 // format. Just go ahead and create the sector.
3010 if(fs::PathExists(sectordir))
3012 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
3013 <<fullpath<<" doesn't exist but directory does."
3014 <<" Continuing with a sector with no metadata."
3016 sector = new ServerMapSector(this, p2d);
3017 m_sectors.insert(p2d, sector);
3021 throw FileNotGoodException("Cannot open sector metafile");
3026 sector = ServerMapSector::deSerialize
3027 (is, this, p2d, m_sectors);
3029 saveSectorMeta(sector);
3032 sector->differs_from_disk = false;
3037 bool ServerMap::loadSectorMeta(v2s16 p2d)
3039 DSTACK(__FUNCTION_NAME);
3041 MapSector *sector = NULL;
3043 // The directory layout we're going to load from.
3044 // 1 - original sectors/xxxxzzzz/
3045 // 2 - new sectors2/xxx/zzz/
3046 // If we load from anything but the latest structure, we will
3047 // immediately save to the new one, and remove the old.
3049 std::string sectordir1 = getSectorDir(p2d, 1);
3050 std::string sectordir;
3051 if(fs::PathExists(sectordir1))
3053 sectordir = sectordir1;
3058 sectordir = getSectorDir(p2d, 2);
3062 sector = loadSectorMeta(sectordir, loadlayout != 2);
3064 catch(InvalidFilenameException &e)
3068 catch(FileNotGoodException &e)
3072 catch(std::exception &e)
3081 bool ServerMap::loadSectorFull(v2s16 p2d)
3083 DSTACK(__FUNCTION_NAME);
3085 MapSector *sector = NULL;
3087 // The directory layout we're going to load from.
3088 // 1 - original sectors/xxxxzzzz/
3089 // 2 - new sectors2/xxx/zzz/
3090 // If we load from anything but the latest structure, we will
3091 // immediately save to the new one, and remove the old.
3093 std::string sectordir1 = getSectorDir(p2d, 1);
3094 std::string sectordir;
3095 if(fs::PathExists(sectordir1))
3097 sectordir = sectordir1;
3102 sectordir = getSectorDir(p2d, 2);
3106 sector = loadSectorMeta(sectordir, loadlayout != 2);
3108 catch(InvalidFilenameException &e)
3112 catch(FileNotGoodException &e)
3116 catch(std::exception &e)
3124 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3126 std::vector<fs::DirListNode>::iterator i2;
3127 for(i2=list2.begin(); i2!=list2.end(); i2++)
3133 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3135 catch(InvalidFilenameException &e)
3137 // This catches unknown crap in directory
3143 dstream<<"Sector converted to new layout - deleting "<<
3144 sectordir1<<std::endl;
3145 fs::RecursiveDelete(sectordir1);
3152 void ServerMap::saveBlock(MapBlock *block)
3154 DSTACK(__FUNCTION_NAME);
3156 Dummy blocks are not written
3158 if(block->isDummy())
3160 /*v3s16 p = block->getPos();
3161 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3162 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3166 // Format used for writing
3167 u8 version = SER_FMT_VER_HIGHEST;
3169 v3s16 p3d = block->getPos();
3171 v2s16 p2d(p3d.X, p3d.Z);
3172 std::string sectordir = getSectorDir(p2d);
3174 createDirs(sectordir);
3176 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3177 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3178 if(o.good() == false)
3179 throw FileNotGoodException("Cannot open block data");
3182 [0] u8 serialization version
3185 o.write((char*)&version, 1);
3188 block->serialize(o, version);
3190 // Write extra data stored on disk
3191 block->serializeDiskExtra(o, version);
3193 // We just wrote it to the disk so clear modified flag
3194 block->resetModified();
3197 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3199 DSTACK(__FUNCTION_NAME);
3201 std::string fullpath = sectordir+"/"+blockfile;
3204 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3205 if(is.good() == false)
3206 throw FileNotGoodException("Cannot open block file");
3208 v3s16 p3d = getBlockPos(sectordir, blockfile);
3209 v2s16 p2d(p3d.X, p3d.Z);
3211 assert(sector->getPos() == p2d);
3213 u8 version = SER_FMT_VER_INVALID;
3214 is.read((char*)&version, 1);
3217 throw SerializationError("ServerMap::loadBlock(): Failed"
3218 " to read MapBlock version");
3220 /*u32 block_size = MapBlock::serializedLength(version);
3221 SharedBuffer<u8> data(block_size);
3222 is.read((char*)*data, block_size);*/
3224 // This will always return a sector because we're the server
3225 //MapSector *sector = emergeSector(p2d);
3227 MapBlock *block = NULL;
3228 bool created_new = false;
3229 block = sector->getBlockNoCreateNoEx(p3d.Y);
3232 block = sector->createBlankBlockNoInsert(p3d.Y);
3237 block->deSerialize(is, version);
3239 // Read extra data stored on disk
3240 block->deSerializeDiskExtra(is, version);
3242 // If it's a new block, insert it to the map
3244 sector->insertBlock(block);
3247 Save blocks loaded in old format in new format
3250 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3255 // We just loaded it from the disk, so it's up-to-date.
3256 block->resetModified();
3259 catch(SerializationError &e)
3261 dstream<<"WARNING: Invalid block data on disk "
3262 <<"fullpath="<<fullpath
3263 <<" (SerializationError). "
3264 <<"what()="<<e.what()
3266 //" Ignoring. A new one will be generated.
3269 // TODO: Backup file; name is in fullpath.
3273 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3275 DSTACK(__FUNCTION_NAME);
3277 v2s16 p2d(blockpos.X, blockpos.Z);
3279 // The directory layout we're going to load from.
3280 // 1 - original sectors/xxxxzzzz/
3281 // 2 - new sectors2/xxx/zzz/
3282 // If we load from anything but the latest structure, we will
3283 // immediately save to the new one, and remove the old.
3285 std::string sectordir1 = getSectorDir(p2d, 1);
3286 std::string sectordir;
3287 if(fs::PathExists(sectordir1))
3289 sectordir = sectordir1;
3294 sectordir = getSectorDir(p2d, 2);
3298 Make sure sector is loaded
3300 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3304 sector = loadSectorMeta(sectordir, loadlayout != 2);
3306 catch(InvalidFilenameException &e)
3310 catch(FileNotGoodException &e)
3314 catch(std::exception &e)
3321 Make sure file exists
3324 std::string blockfilename = getBlockFilename(blockpos);
3325 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3331 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3332 return getBlockNoCreateNoEx(blockpos);
3335 void ServerMap::PrintInfo(std::ostream &out)
3346 ClientMap::ClientMap(
3348 MapDrawControl &control,
3349 scene::ISceneNode* parent,
3350 scene::ISceneManager* mgr,
3354 scene::ISceneNode(parent, mgr, id),
3357 m_camera_position(0,0,0),
3358 m_camera_direction(0,0,1)
3360 m_camera_mutex.Init();
3361 assert(m_camera_mutex.IsInitialized());
3363 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3364 BS*1000000,BS*1000000,BS*1000000);
3367 ClientMap::~ClientMap()
3369 /*JMutexAutoLock lock(mesh_mutex);
3378 MapSector * ClientMap::emergeSector(v2s16 p2d)
3380 DSTACK(__FUNCTION_NAME);
3381 // Check that it doesn't exist already
3383 return getSectorNoGenerate(p2d);
3385 catch(InvalidPositionException &e)
3390 ClientMapSector *sector = new ClientMapSector(this, p2d);
3393 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3394 m_sectors.insert(p2d, sector);
3401 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3403 DSTACK(__FUNCTION_NAME);
3404 ClientMapSector *sector = NULL;
3406 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3408 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3412 sector = (ClientMapSector*)n->getValue();
3413 assert(sector->getId() == MAPSECTOR_CLIENT);
3417 sector = new ClientMapSector(this, p2d);
3419 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3420 m_sectors.insert(p2d, sector);
3424 sector->deSerialize(is);
3428 void ClientMap::OnRegisterSceneNode()
3432 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3433 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3436 ISceneNode::OnRegisterSceneNode();
3439 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3441 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3442 DSTACK(__FUNCTION_NAME);
3444 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3447 This is called two times per frame, reset on the non-transparent one
3449 if(pass == scene::ESNRP_SOLID)
3451 m_last_drawn_sectors.clear();
3455 Get time for measuring timeout.
3457 Measuring time is very useful for long delays when the
3458 machine is swapping a lot.
3460 int time1 = time(0);
3462 //u32 daynight_ratio = m_client->getDayNightRatio();
3464 m_camera_mutex.Lock();
3465 v3f camera_position = m_camera_position;
3466 v3f camera_direction = m_camera_direction;
3467 m_camera_mutex.Unlock();
3470 Get all blocks and draw all visible ones
3473 v3s16 cam_pos_nodes(
3474 camera_position.X / BS,
3475 camera_position.Y / BS,
3476 camera_position.Z / BS);
3478 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3480 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3481 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3483 // Take a fair amount as we will be dropping more out later
3485 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3486 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3487 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3489 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3490 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3491 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3493 u32 vertex_count = 0;
3495 // For limiting number of mesh updates per frame
3496 u32 mesh_update_count = 0;
3498 u32 blocks_would_have_drawn = 0;
3499 u32 blocks_drawn = 0;
3501 int timecheck_counter = 0;
3502 core::map<v2s16, MapSector*>::Iterator si;
3503 si = m_sectors.getIterator();
3504 for(; si.atEnd() == false; si++)
3507 timecheck_counter++;
3508 if(timecheck_counter > 50)
3510 timecheck_counter = 0;
3511 int time2 = time(0);
3512 if(time2 > time1 + 4)
3514 dstream<<"ClientMap::renderMap(): "
3515 "Rendering takes ages, returning."
3522 MapSector *sector = si.getNode()->getValue();
3523 v2s16 sp = sector->getPos();
3525 if(m_control.range_all == false)
3527 if(sp.X < p_blocks_min.X
3528 || sp.X > p_blocks_max.X
3529 || sp.Y < p_blocks_min.Z
3530 || sp.Y > p_blocks_max.Z)
3534 core::list< MapBlock * > sectorblocks;
3535 sector->getBlocks(sectorblocks);
3541 u32 sector_blocks_drawn = 0;
3543 core::list< MapBlock * >::Iterator i;
3544 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3546 MapBlock *block = *i;
3549 Compare block position to camera position, skip
3550 if not seen on display
3553 float range = 100000 * BS;
3554 if(m_control.range_all == false)
3555 range = m_control.wanted_range * BS;
3558 if(isBlockInSight(block->getPos(), camera_position,
3559 camera_direction, range, &d) == false)
3564 // Okay, this block will be drawn. Reset usage timer.
3565 block->resetUsageTimer();
3567 // This is ugly (spherical distance limit?)
3568 /*if(m_control.range_all == false &&
3569 d - 0.5*BS*MAP_BLOCKSIZE > range)
3574 Update expired mesh (used for day/night change)
3576 It doesn't work exactly like it should now with the
3577 tasked mesh update but whatever.
3580 bool mesh_expired = false;
3583 JMutexAutoLock lock(block->mesh_mutex);
3585 mesh_expired = block->getMeshExpired();
3587 // Mesh has not been expired and there is no mesh:
3588 // block has no content
3589 if(block->mesh == NULL && mesh_expired == false)
3593 f32 faraway = BS*50;
3594 //f32 faraway = m_control.wanted_range * BS;
3597 This has to be done with the mesh_mutex unlocked
3599 // Pretty random but this should work somewhat nicely
3600 if(mesh_expired && (
3601 (mesh_update_count < 3
3602 && (d < faraway || mesh_update_count < 2)
3605 (m_control.range_all && mesh_update_count < 20)
3608 /*if(mesh_expired && mesh_update_count < 6
3609 && (d < faraway || mesh_update_count < 3))*/
3611 mesh_update_count++;
3613 // Mesh has been expired: generate new mesh
3614 //block->updateMesh(daynight_ratio);
3615 m_client->addUpdateMeshTask(block->getPos());
3617 mesh_expired = false;
3622 Draw the faces of the block
3625 JMutexAutoLock lock(block->mesh_mutex);
3627 scene::SMesh *mesh = block->mesh;
3632 blocks_would_have_drawn++;
3633 if(blocks_drawn >= m_control.wanted_max_blocks
3634 && m_control.range_all == false
3635 && d > m_control.wanted_min_range * BS)
3639 sector_blocks_drawn++;
3641 u32 c = mesh->getMeshBufferCount();
3643 for(u32 i=0; i<c; i++)
3645 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3646 const video::SMaterial& material = buf->getMaterial();
3647 video::IMaterialRenderer* rnd =
3648 driver->getMaterialRenderer(material.MaterialType);
3649 bool transparent = (rnd && rnd->isTransparent());
3650 // Render transparent on transparent pass and likewise.
3651 if(transparent == is_transparent_pass)
3654 This *shouldn't* hurt too much because Irrlicht
3655 doesn't change opengl textures if the old
3656 material is set again.
3658 driver->setMaterial(buf->getMaterial());
3659 driver->drawMeshBuffer(buf);
3660 vertex_count += buf->getVertexCount();
3664 } // foreach sectorblocks
3666 if(sector_blocks_drawn != 0)
3668 m_last_drawn_sectors[sp] = true;
3672 m_control.blocks_drawn = blocks_drawn;
3673 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3675 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3676 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3679 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3680 core::map<v3s16, MapBlock*> *affected_blocks)
3682 bool changed = false;
3684 Add it to all blocks touching it
3687 v3s16(0,0,0), // this
3688 v3s16(0,0,1), // back
3689 v3s16(0,1,0), // top
3690 v3s16(1,0,0), // right
3691 v3s16(0,0,-1), // front
3692 v3s16(0,-1,0), // bottom
3693 v3s16(-1,0,0), // left
3695 for(u16 i=0; i<7; i++)
3697 v3s16 p2 = p + dirs[i];
3698 // Block position of neighbor (or requested) node
3699 v3s16 blockpos = getNodeBlockPos(p2);
3700 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3701 if(blockref == NULL)
3703 // Relative position of requested node
3704 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3705 if(blockref->setTempMod(relpos, mod))
3710 if(changed && affected_blocks!=NULL)
3712 for(u16 i=0; i<7; i++)
3714 v3s16 p2 = p + dirs[i];
3715 // Block position of neighbor (or requested) node
3716 v3s16 blockpos = getNodeBlockPos(p2);
3717 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3718 if(blockref == NULL)
3720 affected_blocks->insert(blockpos, blockref);
3726 bool ClientMap::clearTempMod(v3s16 p,
3727 core::map<v3s16, MapBlock*> *affected_blocks)
3729 bool changed = false;
3731 v3s16(0,0,0), // this
3732 v3s16(0,0,1), // back
3733 v3s16(0,1,0), // top
3734 v3s16(1,0,0), // right
3735 v3s16(0,0,-1), // front
3736 v3s16(0,-1,0), // bottom
3737 v3s16(-1,0,0), // left
3739 for(u16 i=0; i<7; i++)
3741 v3s16 p2 = p + dirs[i];
3742 // Block position of neighbor (or requested) node
3743 v3s16 blockpos = getNodeBlockPos(p2);
3744 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3745 if(blockref == NULL)
3747 // Relative position of requested node
3748 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3749 if(blockref->clearTempMod(relpos))
3754 if(changed && affected_blocks!=NULL)
3756 for(u16 i=0; i<7; i++)
3758 v3s16 p2 = p + dirs[i];
3759 // Block position of neighbor (or requested) node
3760 v3s16 blockpos = getNodeBlockPos(p2);
3761 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3762 if(blockref == NULL)
3764 affected_blocks->insert(blockpos, blockref);
3770 void ClientMap::expireMeshes(bool only_daynight_diffed)
3772 TimeTaker timer("expireMeshes()");
3774 core::map<v2s16, MapSector*>::Iterator si;
3775 si = m_sectors.getIterator();
3776 for(; si.atEnd() == false; si++)
3778 MapSector *sector = si.getNode()->getValue();
3780 core::list< MapBlock * > sectorblocks;
3781 sector->getBlocks(sectorblocks);
3783 core::list< MapBlock * >::Iterator i;
3784 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3786 MapBlock *block = *i;
3788 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3794 JMutexAutoLock lock(block->mesh_mutex);
3795 if(block->mesh != NULL)
3797 /*block->mesh->drop();
3798 block->mesh = NULL;*/
3799 block->setMeshExpired(true);
3806 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3808 assert(mapType() == MAPTYPE_CLIENT);
3811 v3s16 p = blockpos + v3s16(0,0,0);
3812 MapBlock *b = getBlockNoCreate(p);
3813 b->updateMesh(daynight_ratio);
3814 //b->setMeshExpired(true);
3816 catch(InvalidPositionException &e){}
3819 v3s16 p = blockpos + v3s16(-1,0,0);
3820 MapBlock *b = getBlockNoCreate(p);
3821 b->updateMesh(daynight_ratio);
3822 //b->setMeshExpired(true);
3824 catch(InvalidPositionException &e){}
3826 v3s16 p = blockpos + v3s16(0,-1,0);
3827 MapBlock *b = getBlockNoCreate(p);
3828 b->updateMesh(daynight_ratio);
3829 //b->setMeshExpired(true);
3831 catch(InvalidPositionException &e){}
3833 v3s16 p = blockpos + v3s16(0,0,-1);
3834 MapBlock *b = getBlockNoCreate(p);
3835 b->updateMesh(daynight_ratio);
3836 //b->setMeshExpired(true);
3838 catch(InvalidPositionException &e){}
3843 Update mesh of block in which the node is, and if the node is at the
3844 leading edge, update the appropriate leading blocks too.
3846 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3854 v3s16 blockposes[4];
3855 for(u32 i=0; i<4; i++)
3857 v3s16 np = nodepos + dirs[i];
3858 blockposes[i] = getNodeBlockPos(np);
3859 // Don't update mesh of block if it has been done already
3860 bool already_updated = false;
3861 for(u32 j=0; j<i; j++)
3863 if(blockposes[j] == blockposes[i])
3865 already_updated = true;
3872 MapBlock *b = getBlockNoCreate(blockposes[i]);
3873 b->updateMesh(daynight_ratio);
3878 void ClientMap::PrintInfo(std::ostream &out)
3889 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3894 MapVoxelManipulator::~MapVoxelManipulator()
3896 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3900 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3902 TimeTaker timer1("emerge", &emerge_time);
3904 // Units of these are MapBlocks
3905 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3906 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3908 VoxelArea block_area_nodes
3909 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3911 addArea(block_area_nodes);
3913 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3914 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3915 for(s32 x=p_min.X; x<=p_max.X; x++)
3918 core::map<v3s16, bool>::Node *n;
3919 n = m_loaded_blocks.find(p);
3923 bool block_data_inexistent = false;
3926 TimeTaker timer1("emerge load", &emerge_load_time);
3928 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3929 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3932 dstream<<std::endl;*/
3934 MapBlock *block = m_map->getBlockNoCreate(p);
3935 if(block->isDummy())
3936 block_data_inexistent = true;
3938 block->copyTo(*this);
3940 catch(InvalidPositionException &e)
3942 block_data_inexistent = true;
3945 if(block_data_inexistent)
3947 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3948 // Fill with VOXELFLAG_INEXISTENT
3949 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3950 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3952 s32 i = m_area.index(a.MinEdge.X,y,z);
3953 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3957 m_loaded_blocks.insert(p, !block_data_inexistent);
3960 //dstream<<"emerge done"<<std::endl;
3964 SUGG: Add an option to only update eg. water and air nodes.
3965 This will make it interfere less with important stuff if
3968 void MapVoxelManipulator::blitBack
3969 (core::map<v3s16, MapBlock*> & modified_blocks)
3971 if(m_area.getExtent() == v3s16(0,0,0))
3974 //TimeTaker timer1("blitBack");
3976 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3977 <<m_loaded_blocks.size()<<std::endl;*/
3980 Initialize block cache
3982 v3s16 blockpos_last;
3983 MapBlock *block = NULL;
3984 bool block_checked_in_modified = false;
3986 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3987 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3988 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3992 u8 f = m_flags[m_area.index(p)];
3993 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3996 MapNode &n = m_data[m_area.index(p)];
3998 v3s16 blockpos = getNodeBlockPos(p);
4003 if(block == NULL || blockpos != blockpos_last){
4004 block = m_map->getBlockNoCreate(blockpos);
4005 blockpos_last = blockpos;
4006 block_checked_in_modified = false;
4009 // Calculate relative position in block
4010 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4012 // Don't continue if nothing has changed here
4013 if(block->getNode(relpos) == n)
4016 //m_map->setNode(m_area.MinEdge + p, n);
4017 block->setNode(relpos, n);
4020 Make sure block is in modified_blocks
4022 if(block_checked_in_modified == false)
4024 modified_blocks[blockpos] = block;
4025 block_checked_in_modified = true;
4028 catch(InvalidPositionException &e)
4034 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4035 MapVoxelManipulator(map),
4036 m_create_area(false)
4040 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4044 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4046 // Just create the area so that it can be pointed to
4047 VoxelManipulator::emerge(a, caller_id);
4050 void ManualMapVoxelManipulator::initialEmerge(
4051 v3s16 blockpos_min, v3s16 blockpos_max)
4053 TimeTaker timer1("initialEmerge", &emerge_time);
4055 // Units of these are MapBlocks
4056 v3s16 p_min = blockpos_min;
4057 v3s16 p_max = blockpos_max;
4059 VoxelArea block_area_nodes
4060 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4062 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4065 dstream<<"initialEmerge: area: ";
4066 block_area_nodes.print(dstream);
4067 dstream<<" ("<<size_MB<<"MB)";
4071 addArea(block_area_nodes);
4073 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4074 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4075 for(s32 x=p_min.X; x<=p_max.X; x++)
4078 core::map<v3s16, bool>::Node *n;
4079 n = m_loaded_blocks.find(p);
4083 bool block_data_inexistent = false;
4086 TimeTaker timer1("emerge load", &emerge_load_time);
4088 MapBlock *block = m_map->getBlockNoCreate(p);
4089 if(block->isDummy())
4090 block_data_inexistent = true;
4092 block->copyTo(*this);
4094 catch(InvalidPositionException &e)
4096 block_data_inexistent = true;
4099 if(block_data_inexistent)
4102 Mark area inexistent
4104 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4105 // Fill with VOXELFLAG_INEXISTENT
4106 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4107 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4109 s32 i = m_area.index(a.MinEdge.X,y,z);
4110 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4114 m_loaded_blocks.insert(p, !block_data_inexistent);
4118 void ManualMapVoxelManipulator::blitBackAll(
4119 core::map<v3s16, MapBlock*> * modified_blocks)
4121 if(m_area.getExtent() == v3s16(0,0,0))
4125 Copy data of all blocks
4127 for(core::map<v3s16, bool>::Iterator
4128 i = m_loaded_blocks.getIterator();
4129 i.atEnd() == false; i++)
4131 v3s16 p = i.getNode()->getKey();
4132 bool existed = i.getNode()->getValue();
4133 if(existed == false)
4135 // The Great Bug was found using this
4136 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4137 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4141 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4144 dstream<<"WARNING: "<<__FUNCTION_NAME
4145 <<": got NULL block "
4146 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4151 block->copyFrom(*this);
4154 modified_blocks->insert(p, block);