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 // list of nodes that due to viscosity have not reached their max level height
1566 UniqueQueue<v3s16> must_reflow;
1568 // List of MapBlocks that will require a lighting update (due to lava)
1569 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1571 while(m_transforming_liquid.size() != 0)
1573 // This should be done here so that it is done when continue is used
1574 if(loopcount >= initial_size * 3)
1579 Get a queued transforming liquid node
1581 v3s16 p0 = m_transforming_liquid.pop_front();
1583 MapNode n0 = getNodeNoEx(p0);
1586 Collect information about current node
1588 s8 liquid_level = -1;
1589 u8 liquid_kind = CONTENT_IGNORE;
1590 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1591 switch (liquid_type) {
1593 liquid_level = LIQUID_LEVEL_SOURCE;
1594 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1596 case LIQUID_FLOWING:
1597 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1598 liquid_kind = n0.getContent();
1601 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1602 // continue with the next node.
1603 if (n0.getContent() != CONTENT_AIR)
1605 liquid_kind = CONTENT_AIR;
1610 Collect information about the environment
1612 const v3s16 *dirs = g_6dirs;
1613 NodeNeighbor sources[6]; // surrounding sources
1614 int num_sources = 0;
1615 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1617 NodeNeighbor airs[6]; // surrounding air
1619 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1620 int num_neutrals = 0;
1621 bool flowing_down = false;
1622 for (u16 i = 0; i < 6; i++) {
1623 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1626 nt = NEIGHBOR_UPPER;
1629 nt = NEIGHBOR_LOWER;
1632 v3s16 npos = p0 + dirs[i];
1633 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1634 switch (content_features(nb.n.getContent()).liquid_type) {
1636 if (nb.n.getContent() == CONTENT_AIR) {
1637 airs[num_airs++] = nb;
1638 // if the current node is a water source the neighbor
1639 // should be enqueded for transformation regardless of whether the
1640 // current node changes or not.
1641 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1642 m_transforming_liquid.push_back(npos);
1643 // if the current node happens to be a flowing node, it will start to flow down here.
1644 if (nb.t == NEIGHBOR_LOWER) {
1645 flowing_down = true;
1648 neutrals[num_neutrals++] = nb;
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 sources[num_sources++] = nb;
1661 case LIQUID_FLOWING:
1662 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1663 if (liquid_kind == CONTENT_AIR)
1664 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1665 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1666 neutrals[num_neutrals++] = nb;
1668 flows[num_flows++] = nb;
1669 if (nb.t == NEIGHBOR_LOWER)
1670 flowing_down = true;
1677 decide on the type (and possibly level) of the current node
1679 content_t new_node_content;
1680 s8 new_node_level = -1;
1681 s8 max_node_level = -1;
1682 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1683 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1684 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1685 // it's perfectly safe to use liquid_kind here to determine the new node content.
1686 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1687 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1688 // liquid_kind is set properly, see above
1689 new_node_content = liquid_kind;
1690 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1692 // no surrounding sources, so get the maximum level that can flow into this node
1693 for (u16 i = 0; i < num_flows; i++) {
1694 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1695 switch (flows[i].t) {
1696 case NEIGHBOR_UPPER:
1697 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1698 max_node_level = LIQUID_LEVEL_MAX;
1699 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1700 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1701 } else if (nb_liquid_level > max_node_level)
1702 max_node_level = nb_liquid_level;
1704 case NEIGHBOR_LOWER:
1706 case NEIGHBOR_SAME_LEVEL:
1707 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1708 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1709 max_node_level = nb_liquid_level - 1;
1715 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1716 if (viscosity > 1 && max_node_level != liquid_level) {
1717 // amount to gain, limited by viscosity
1718 // must be at least 1 in absolute value
1719 s8 level_inc = max_node_level - liquid_level;
1720 if (level_inc < -viscosity || level_inc > viscosity)
1721 new_node_level = liquid_level + level_inc/viscosity;
1722 else if (level_inc < 0)
1723 new_node_level = liquid_level - 1;
1724 else if (level_inc > 0)
1725 new_node_level = liquid_level + 1;
1726 if (new_node_level != max_node_level)
1727 must_reflow.push_back(p0);
1729 new_node_level = max_node_level;
1731 if (new_node_level >= 0)
1732 new_node_content = liquid_kind;
1734 new_node_content = CONTENT_AIR;
1739 check if anything has changed. if not, just continue with the next node.
1741 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1742 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1743 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1749 update the current node
1751 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1752 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1753 // set level to last 3 bits, flowing down bit to 4th bit
1754 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1756 // set the liquid level and flow bit to 0
1757 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1759 n0.setContent(new_node_content);
1761 v3s16 blockpos = getNodeBlockPos(p0);
1762 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1764 modified_blocks.insert(blockpos, block);
1765 // If node emits light, MapBlock requires lighting update
1766 if(content_features(n0).light_source != 0)
1767 lighting_modified_blocks[block->getPos()] = block;
1771 enqueue neighbors for update if neccessary
1773 switch (content_features(n0.getContent()).liquid_type) {
1775 case LIQUID_FLOWING:
1776 // make sure source flows into all neighboring nodes
1777 for (u16 i = 0; i < num_flows; i++)
1778 if (flows[i].t != NEIGHBOR_UPPER)
1779 m_transforming_liquid.push_back(flows[i].p);
1780 for (u16 i = 0; i < num_airs; i++)
1781 if (airs[i].t != NEIGHBOR_UPPER)
1782 m_transforming_liquid.push_back(airs[i].p);
1785 // this flow has turned to air; neighboring flows might need to do the same
1786 for (u16 i = 0; i < num_flows; i++)
1787 m_transforming_liquid.push_back(flows[i].p);
1791 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1792 while (must_reflow.size() > 0)
1793 m_transforming_liquid.push_back(must_reflow.pop_front());
1794 updateLighting(lighting_modified_blocks, modified_blocks);
1797 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1799 v3s16 blockpos = getNodeBlockPos(p);
1800 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1801 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1804 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1808 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1812 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1814 v3s16 blockpos = getNodeBlockPos(p);
1815 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1816 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1819 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1823 block->m_node_metadata.set(p_rel, meta);
1826 void Map::removeNodeMetadata(v3s16 p)
1828 v3s16 blockpos = getNodeBlockPos(p);
1829 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1830 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1833 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1837 block->m_node_metadata.remove(p_rel);
1840 void Map::nodeMetadataStep(float dtime,
1841 core::map<v3s16, MapBlock*> &changed_blocks)
1845 Currently there is no way to ensure that all the necessary
1846 blocks are loaded when this is run. (They might get unloaded)
1847 NOTE: ^- Actually, that might not be so. In a quick test it
1848 reloaded a block with a furnace when I walked back to it from
1851 core::map<v2s16, MapSector*>::Iterator si;
1852 si = m_sectors.getIterator();
1853 for(; si.atEnd() == false; si++)
1855 MapSector *sector = si.getNode()->getValue();
1856 core::list< MapBlock * > sectorblocks;
1857 sector->getBlocks(sectorblocks);
1858 core::list< MapBlock * >::Iterator i;
1859 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1861 MapBlock *block = *i;
1862 bool changed = block->m_node_metadata.step(dtime);
1864 changed_blocks[block->getPos()] = block;
1873 ServerMap::ServerMap(std::string savedir):
1876 m_map_metadata_changed(true)
1878 dstream<<__FUNCTION_NAME<<std::endl;
1880 //m_chunksize = 8; // Takes a few seconds
1882 if (g_settings.get("fixed_map_seed").empty())
1884 m_seed = (((u64)(myrand()%0xffff)<<0)
1885 + ((u64)(myrand()%0xffff)<<16)
1886 + ((u64)(myrand()%0xffff)<<32)
1887 + ((u64)(myrand()%0xffff)<<48));
1891 m_seed = g_settings.getU64("fixed_map_seed");
1895 Experimental and debug stuff
1902 Try to load map; if not found, create a new one.
1905 m_savedir = savedir;
1906 m_map_saving_enabled = false;
1910 // If directory exists, check contents and load if possible
1911 if(fs::PathExists(m_savedir))
1913 // If directory is empty, it is safe to save into it.
1914 if(fs::GetDirListing(m_savedir).size() == 0)
1916 dstream<<DTIME<<"Server: Empty save directory is valid."
1918 m_map_saving_enabled = true;
1923 // Load map metadata (seed, chunksize)
1926 catch(FileNotGoodException &e){
1927 dstream<<DTIME<<"WARNING: Could not load map metadata"
1928 //<<" Disabling chunk-based generator."
1934 // Load chunk metadata
1937 catch(FileNotGoodException &e){
1938 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1939 <<" Disabling chunk-based generator."
1944 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1945 "metadata and sector (0,0) from "<<savedir<<
1946 ", assuming valid save directory."
1949 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1950 <<"and chunk metadata from "<<savedir
1951 <<", assuming valid save directory."
1954 m_map_saving_enabled = true;
1955 // Map loaded, not creating new one
1959 // If directory doesn't exist, it is safe to save to it
1961 m_map_saving_enabled = true;
1964 catch(std::exception &e)
1966 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1967 <<", exception: "<<e.what()<<std::endl;
1968 dstream<<"Please remove the map or fix it."<<std::endl;
1969 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1972 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1974 // Create zero sector
1975 emergeSector(v2s16(0,0));
1977 // Initially write whole map
1981 ServerMap::~ServerMap()
1983 dstream<<__FUNCTION_NAME<<std::endl;
1987 if(m_map_saving_enabled)
1989 // Save only changed parts
1991 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1995 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1998 catch(std::exception &e)
2000 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2001 <<", exception: "<<e.what()<<std::endl;
2008 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2009 for(; i.atEnd() == false; i++)
2011 MapChunk *chunk = i.getNode()->getValue();
2017 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2019 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2020 if(enable_mapgen_debug_info)
2021 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2022 <<blockpos.Z<<")"<<std::endl;
2024 // Do nothing if not inside limits (+-1 because of neighbors)
2025 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2026 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2032 data->no_op = false;
2033 data->seed = m_seed;
2034 data->blockpos = blockpos;
2037 Create the whole area of this and the neighboring blocks
2040 //TimeTaker timer("initBlockMake() create area");
2042 for(s16 x=-1; x<=1; x++)
2043 for(s16 z=-1; z<=1; z++)
2045 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2046 // Sector metadata is loaded from disk if not already loaded.
2047 ServerMapSector *sector = createSector(sectorpos);
2050 for(s16 y=-1; y<=1; y++)
2052 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2053 //MapBlock *block = createBlock(p);
2054 // 1) get from memory, 2) load from disk
2055 MapBlock *block = emergeBlock(p, false);
2056 // 3) create a blank one
2059 block = createBlock(p);
2062 Block gets sunlight if this is true.
2064 Refer to the map generator heuristics.
2066 bool ug = mapgen::block_is_underground(data->seed, p);
2067 block->setIsUnderground(ug);
2070 // Lighting will not be valid after make_chunk is called
2071 block->setLightingExpired(true);
2072 // Lighting will be calculated
2073 //block->setLightingExpired(false);
2079 Now we have a big empty area.
2081 Make a ManualMapVoxelManipulator that contains this and the
2085 // The area that contains this block and it's neighbors
2086 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2087 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2089 data->vmanip = new ManualMapVoxelManipulator(this);
2090 //data->vmanip->setMap(this);
2094 //TimeTaker timer("initBlockMake() initialEmerge");
2095 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2098 // Data is ready now.
2101 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2102 core::map<v3s16, MapBlock*> &changed_blocks)
2104 v3s16 blockpos = data->blockpos;
2105 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2106 <<blockpos.Z<<")"<<std::endl;*/
2110 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2114 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2116 /*dstream<<"Resulting vmanip:"<<std::endl;
2117 data->vmanip.print(dstream);*/
2120 Blit generated stuff to map
2121 NOTE: blitBackAll adds nearly everything to changed_blocks
2125 //TimeTaker timer("finishBlockMake() blitBackAll");
2126 data->vmanip->blitBackAll(&changed_blocks);
2129 if(enable_mapgen_debug_info)
2130 dstream<<"finishBlockMake: changed_blocks.size()="
2131 <<changed_blocks.size()<<std::endl;
2134 Copy transforming liquid information
2136 while(data->transforming_liquid.size() > 0)
2138 v3s16 p = data->transforming_liquid.pop_front();
2139 m_transforming_liquid.push_back(p);
2145 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2149 Set is_underground flag for lighting with sunlight.
2151 Refer to map generator heuristics.
2153 NOTE: This is done in initChunkMake
2155 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2159 Add sunlight to central block.
2160 This makes in-dark-spawning monsters to not flood the whole thing.
2161 Do not spread the light, though.
2163 /*core::map<v3s16, bool> light_sources;
2164 bool black_air_left = false;
2165 block->propagateSunlight(light_sources, true, &black_air_left);*/
2168 NOTE: Lighting and object adding shouldn't really be here, but
2169 lighting is a bit tricky to move properly to makeBlock.
2170 TODO: Do this the right way anyway, that is, move it to makeBlock.
2171 - There needs to be some way for makeBlock to report back if
2172 the lighting update is going further down because of the
2173 new block blocking light
2178 NOTE: This takes ~60ms, TODO: Investigate why
2181 TimeTaker t("finishBlockMake lighting update");
2183 core::map<v3s16, MapBlock*> lighting_update_blocks;
2186 lighting_update_blocks.insert(block->getPos(), block);
2191 v3s16 p = block->getPos()+v3s16(x,1,z);
2192 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2196 // All modified blocks
2197 // NOTE: Should this be done? If this is not done, then the lighting
2198 // of the others will be updated in a different place, one by one, i
2199 // think... or they might not? Well, at least they are left marked as
2200 // "lighting expired"; it seems that is not handled at all anywhere,
2201 // so enabling this will slow it down A LOT because otherwise it
2202 // would not do this at all. This causes the black trees.
2203 for(core::map<v3s16, MapBlock*>::Iterator
2204 i = changed_blocks.getIterator();
2205 i.atEnd() == false; i++)
2207 lighting_update_blocks.insert(i.getNode()->getKey(),
2208 i.getNode()->getValue());
2210 /*// Also force-add all the upmost blocks for proper sunlight
2211 for(s16 x=-1; x<=1; x++)
2212 for(s16 z=-1; z<=1; z++)
2214 v3s16 p = block->getPos()+v3s16(x,1,z);
2215 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2218 updateLighting(lighting_update_blocks, changed_blocks);
2221 Set lighting to non-expired state in all of them.
2222 This is cheating, but it is not fast enough if all of them
2223 would actually be updated.
2225 for(s16 x=-1; x<=1; x++)
2226 for(s16 y=-1; y<=1; y++)
2227 for(s16 z=-1; z<=1; z++)
2229 v3s16 p = block->getPos()+v3s16(x,y,z);
2230 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2233 if(enable_mapgen_debug_info == false)
2234 t.stop(true); // Hide output
2238 Add random objects to block
2240 mapgen::add_random_objects(block);
2243 Go through changed blocks
2245 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2246 i.atEnd() == false; i++)
2248 MapBlock *block = i.getNode()->getValue();
2251 Update day/night difference cache of the MapBlocks
2253 block->updateDayNightDiff();
2255 Set block as modified
2257 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2261 Set central block as generated
2263 block->setGenerated(true);
2266 Save changed parts of map
2267 NOTE: Will be saved later.
2271 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2272 <<blockpos.Z<<")"<<std::endl;*/
2274 if(enable_mapgen_debug_info)
2277 Analyze resulting blocks
2279 for(s16 x=-1; x<=1; x++)
2280 for(s16 y=-1; y<=1; y++)
2281 for(s16 z=-1; z<=1; z++)
2283 v3s16 p = block->getPos()+v3s16(x,y,z);
2284 MapBlock *block = getBlockNoCreateNoEx(p);
2286 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2287 dstream<<"Generated "<<spos<<": "
2288 <<analyze_block(block)<<std::endl;
2296 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2298 DSTACKF("%s: p2d=(%d,%d)",
2303 Check if it exists already in memory
2305 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2310 Try to load it from disk (with blocks)
2312 //if(loadSectorFull(p2d) == true)
2315 Try to load metadata from disk
2317 if(loadSectorMeta(p2d) == true)
2319 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2322 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2323 throw InvalidPositionException("");
2329 Do not create over-limit
2331 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2332 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2333 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2334 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2335 throw InvalidPositionException("createSector(): pos. over limit");
2338 Generate blank sector
2341 sector = new ServerMapSector(this, p2d);
2343 // Sector position on map in nodes
2344 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2349 m_sectors.insert(p2d, sector);
2355 This is a quick-hand function for calling makeBlock().
2357 MapBlock * ServerMap::generateBlock(
2359 core::map<v3s16, MapBlock*> &modified_blocks
2362 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2364 /*dstream<<"generateBlock(): "
2365 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2368 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2370 TimeTaker timer("generateBlock");
2372 //MapBlock *block = original_dummy;
2374 v2s16 p2d(p.X, p.Z);
2375 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2378 Do not generate over-limit
2380 if(blockpos_over_limit(p))
2382 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2383 throw InvalidPositionException("generateBlock(): pos. over limit");
2387 Create block make data
2389 mapgen::BlockMakeData data;
2390 initBlockMake(&data, p);
2396 TimeTaker t("mapgen::make_block()");
2397 mapgen::make_block(&data);
2399 if(enable_mapgen_debug_info == false)
2400 t.stop(true); // Hide output
2404 Blit data back on map, update lighting, add mobs and whatever this does
2406 finishBlockMake(&data, modified_blocks);
2411 MapBlock *block = getBlockNoCreateNoEx(p);
2419 bool erroneus_content = false;
2420 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2421 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2422 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2425 MapNode n = block->getNode(p);
2426 if(n.getContent() == CONTENT_IGNORE)
2428 dstream<<"CONTENT_IGNORE at "
2429 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2431 erroneus_content = true;
2435 if(erroneus_content)
2444 Generate a completely empty block
2448 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2449 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2451 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2455 n.setContent(CONTENT_AIR);
2457 n.setContent(CONTENT_STONE);
2458 block->setNode(v3s16(x0,y0,z0), n);
2464 if(enable_mapgen_debug_info == false)
2465 timer.stop(true); // Hide output
2470 MapBlock * ServerMap::createBlock(v3s16 p)
2472 DSTACKF("%s: p=(%d,%d,%d)",
2473 __FUNCTION_NAME, p.X, p.Y, p.Z);
2476 Do not create over-limit
2478 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2479 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2480 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2481 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2482 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2483 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2484 throw InvalidPositionException("createBlock(): pos. over limit");
2486 v2s16 p2d(p.X, p.Z);
2489 This will create or load a sector if not found in memory.
2490 If block exists on disk, it will be loaded.
2492 NOTE: On old save formats, this will be slow, as it generates
2493 lighting on blocks for them.
2495 ServerMapSector *sector;
2497 sector = (ServerMapSector*)createSector(p2d);
2498 assert(sector->getId() == MAPSECTOR_SERVER);
2500 catch(InvalidPositionException &e)
2502 dstream<<"createBlock: createSector() failed"<<std::endl;
2506 NOTE: This should not be done, or at least the exception
2507 should not be passed on as std::exception, because it
2508 won't be catched at all.
2510 /*catch(std::exception &e)
2512 dstream<<"createBlock: createSector() failed: "
2513 <<e.what()<<std::endl;
2518 Try to get a block from the sector
2521 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2524 if(block->isDummy())
2529 block = sector->createBlankBlock(block_y);
2533 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2535 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2537 p.X, p.Y, p.Z, allow_generate);
2540 MapBlock *block = getBlockNoCreateNoEx(p);
2541 if(block && block->isDummy() == false)
2546 MapBlock *block = loadBlock(p);
2553 core::map<v3s16, MapBlock*> modified_blocks;
2554 MapBlock *block = generateBlock(p, modified_blocks);
2558 event.type = MEET_OTHER;
2561 // Copy modified_blocks to event
2562 for(core::map<v3s16, MapBlock*>::Iterator
2563 i = modified_blocks.getIterator();
2564 i.atEnd()==false; i++)
2566 event.modified_blocks.insert(i.getNode()->getKey(), false);
2570 dispatchEvent(&event);
2581 Do not generate over-limit
2583 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2584 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2585 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2586 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2587 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2588 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2589 throw InvalidPositionException("emergeBlock(): pos. over limit");
2591 v2s16 p2d(p.X, p.Z);
2594 This will create or load a sector if not found in memory.
2595 If block exists on disk, it will be loaded.
2597 ServerMapSector *sector;
2599 sector = createSector(p2d);
2600 //sector = emergeSector(p2d, changed_blocks);
2602 catch(InvalidPositionException &e)
2604 dstream<<"emergeBlock: createSector() failed: "
2605 <<e.what()<<std::endl;
2606 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2608 <<"You could try to delete it."<<std::endl;
2611 catch(VersionMismatchException &e)
2613 dstream<<"emergeBlock: createSector() failed: "
2614 <<e.what()<<std::endl;
2615 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2617 <<"You could try to delete it."<<std::endl;
2622 Try to get a block from the sector
2625 bool does_not_exist = false;
2626 bool lighting_expired = false;
2627 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2629 // If not found, try loading from disk
2632 block = loadBlock(p);
2638 does_not_exist = true;
2640 else if(block->isDummy() == true)
2642 does_not_exist = true;
2644 else if(block->getLightingExpired())
2646 lighting_expired = true;
2651 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2656 If block was not found on disk and not going to generate a
2657 new one, make sure there is a dummy block in place.
2659 if(only_from_disk && (does_not_exist || lighting_expired))
2661 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2665 // Create dummy block
2666 block = new MapBlock(this, p, true);
2668 // Add block to sector
2669 sector->insertBlock(block);
2675 //dstream<<"Not found on disk, generating."<<std::endl;
2677 //TimeTaker("emergeBlock() generate");
2679 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2682 If the block doesn't exist, generate the block.
2686 block = generateBlock(p, block, sector, changed_blocks,
2687 lighting_invalidated_blocks);
2690 if(lighting_expired)
2692 lighting_invalidated_blocks.insert(p, block);
2697 Initially update sunlight
2700 core::map<v3s16, bool> light_sources;
2701 bool black_air_left = false;
2702 bool bottom_invalid =
2703 block->propagateSunlight(light_sources, true,
2706 // If sunlight didn't reach everywhere and part of block is
2707 // above ground, lighting has to be properly updated
2708 //if(black_air_left && some_part_underground)
2711 lighting_invalidated_blocks[block->getPos()] = block;
2716 lighting_invalidated_blocks[block->getPos()] = block;
2725 s16 ServerMap::findGroundLevel(v2s16 p2d)
2729 Uh, just do something random...
2731 // Find existing map from top to down
2734 v3s16 p(p2d.X, max, p2d.Y);
2735 for(; p.Y>min; p.Y--)
2737 MapNode n = getNodeNoEx(p);
2738 if(n.getContent() != CONTENT_IGNORE)
2743 // If this node is not air, go to plan b
2744 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2746 // Search existing walkable and return it
2747 for(; p.Y>min; p.Y--)
2749 MapNode n = getNodeNoEx(p);
2750 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2759 Determine from map generator noise functions
2762 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2765 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2766 //return (s16)level;
2769 void ServerMap::createDirs(std::string path)
2771 if(fs::CreateAllDirs(path) == false)
2773 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2774 <<"\""<<path<<"\""<<std::endl;
2775 throw BaseException("ServerMap failed to create directory");
2779 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2785 snprintf(cc, 9, "%.4x%.4x",
2786 (unsigned int)pos.X&0xffff,
2787 (unsigned int)pos.Y&0xffff);
2789 return m_savedir + "/sectors/" + cc;
2791 snprintf(cc, 9, "%.3x/%.3x",
2792 (unsigned int)pos.X&0xfff,
2793 (unsigned int)pos.Y&0xfff);
2795 return m_savedir + "/sectors2/" + cc;
2801 v2s16 ServerMap::getSectorPos(std::string dirname)
2805 size_t spos = dirname.rfind('/') + 1;
2806 assert(spos != std::string::npos);
2807 if(dirname.size() - spos == 8)
2810 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2812 else if(dirname.size() - spos == 3)
2815 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2816 // Sign-extend the 12 bit values up to 16 bits...
2817 if(x&0x800) x|=0xF000;
2818 if(y&0x800) y|=0xF000;
2825 v2s16 pos((s16)x, (s16)y);
2829 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2831 v2s16 p2d = getSectorPos(sectordir);
2833 if(blockfile.size() != 4){
2834 throw InvalidFilenameException("Invalid block filename");
2837 int r = sscanf(blockfile.c_str(), "%4x", &y);
2839 throw InvalidFilenameException("Invalid block filename");
2840 return v3s16(p2d.X, y, p2d.Y);
2843 std::string ServerMap::getBlockFilename(v3s16 p)
2846 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2850 void ServerMap::save(bool only_changed)
2852 DSTACK(__FUNCTION_NAME);
2853 if(m_map_saving_enabled == false)
2855 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2859 if(only_changed == false)
2860 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2863 if(only_changed == false || m_map_metadata_changed)
2868 u32 sector_meta_count = 0;
2869 u32 block_count = 0;
2870 u32 block_count_all = 0; // Number of blocks in memory
2872 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2873 for(; i.atEnd() == false; i++)
2875 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2876 assert(sector->getId() == MAPSECTOR_SERVER);
2878 if(sector->differs_from_disk || only_changed == false)
2880 saveSectorMeta(sector);
2881 sector_meta_count++;
2883 core::list<MapBlock*> blocks;
2884 sector->getBlocks(blocks);
2885 core::list<MapBlock*>::Iterator j;
2886 for(j=blocks.begin(); j!=blocks.end(); j++)
2888 MapBlock *block = *j;
2892 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2893 || only_changed == false)
2898 /*dstream<<"ServerMap: Written block ("
2899 <<block->getPos().X<<","
2900 <<block->getPos().Y<<","
2901 <<block->getPos().Z<<")"
2908 Only print if something happened or saved whole map
2910 if(only_changed == false || sector_meta_count != 0
2911 || block_count != 0)
2913 dstream<<DTIME<<"ServerMap: Written: "
2914 <<sector_meta_count<<" sector metadata files, "
2915 <<block_count<<" block files"
2916 <<", "<<block_count_all<<" blocks in memory."
2921 void ServerMap::saveMapMeta()
2923 DSTACK(__FUNCTION_NAME);
2925 dstream<<"INFO: ServerMap::saveMapMeta(): "
2929 createDirs(m_savedir);
2931 std::string fullpath = m_savedir + "/map_meta.txt";
2932 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2933 if(os.good() == false)
2935 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2936 <<"could not open"<<fullpath<<std::endl;
2937 throw FileNotGoodException("Cannot open chunk metadata");
2941 params.setU64("seed", m_seed);
2943 params.writeLines(os);
2945 os<<"[end_of_params]\n";
2947 m_map_metadata_changed = false;
2950 void ServerMap::loadMapMeta()
2952 DSTACK(__FUNCTION_NAME);
2954 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2957 std::string fullpath = m_savedir + "/map_meta.txt";
2958 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2959 if(is.good() == false)
2961 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2962 <<"could not open"<<fullpath<<std::endl;
2963 throw FileNotGoodException("Cannot open map metadata");
2971 throw SerializationError
2972 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2974 std::getline(is, line);
2975 std::string trimmedline = trim(line);
2976 if(trimmedline == "[end_of_params]")
2978 params.parseConfigLine(line);
2981 m_seed = params.getU64("seed");
2983 dstream<<"INFO: ServerMap::loadMapMeta(): "
2988 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2990 DSTACK(__FUNCTION_NAME);
2991 // Format used for writing
2992 u8 version = SER_FMT_VER_HIGHEST;
2994 v2s16 pos = sector->getPos();
2995 std::string dir = getSectorDir(pos);
2998 std::string fullpath = dir + "/meta";
2999 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3000 if(o.good() == false)
3001 throw FileNotGoodException("Cannot open sector metafile");
3003 sector->serialize(o, version);
3005 sector->differs_from_disk = false;
3008 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3010 DSTACK(__FUNCTION_NAME);
3012 v2s16 p2d = getSectorPos(sectordir);
3014 ServerMapSector *sector = NULL;
3016 std::string fullpath = sectordir + "/meta";
3017 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3018 if(is.good() == false)
3020 // If the directory exists anyway, it probably is in some old
3021 // format. Just go ahead and create the sector.
3022 if(fs::PathExists(sectordir))
3024 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
3025 <<fullpath<<" doesn't exist but directory does."
3026 <<" Continuing with a sector with no metadata."
3028 sector = new ServerMapSector(this, p2d);
3029 m_sectors.insert(p2d, sector);
3033 throw FileNotGoodException("Cannot open sector metafile");
3038 sector = ServerMapSector::deSerialize
3039 (is, this, p2d, m_sectors);
3041 saveSectorMeta(sector);
3044 sector->differs_from_disk = false;
3049 bool ServerMap::loadSectorMeta(v2s16 p2d)
3051 DSTACK(__FUNCTION_NAME);
3053 MapSector *sector = NULL;
3055 // The directory layout we're going to load from.
3056 // 1 - original sectors/xxxxzzzz/
3057 // 2 - new sectors2/xxx/zzz/
3058 // If we load from anything but the latest structure, we will
3059 // immediately save to the new one, and remove the old.
3061 std::string sectordir1 = getSectorDir(p2d, 1);
3062 std::string sectordir;
3063 if(fs::PathExists(sectordir1))
3065 sectordir = sectordir1;
3070 sectordir = getSectorDir(p2d, 2);
3074 sector = loadSectorMeta(sectordir, loadlayout != 2);
3076 catch(InvalidFilenameException &e)
3080 catch(FileNotGoodException &e)
3084 catch(std::exception &e)
3093 bool ServerMap::loadSectorFull(v2s16 p2d)
3095 DSTACK(__FUNCTION_NAME);
3097 MapSector *sector = NULL;
3099 // The directory layout we're going to load from.
3100 // 1 - original sectors/xxxxzzzz/
3101 // 2 - new sectors2/xxx/zzz/
3102 // If we load from anything but the latest structure, we will
3103 // immediately save to the new one, and remove the old.
3105 std::string sectordir1 = getSectorDir(p2d, 1);
3106 std::string sectordir;
3107 if(fs::PathExists(sectordir1))
3109 sectordir = sectordir1;
3114 sectordir = getSectorDir(p2d, 2);
3118 sector = loadSectorMeta(sectordir, loadlayout != 2);
3120 catch(InvalidFilenameException &e)
3124 catch(FileNotGoodException &e)
3128 catch(std::exception &e)
3136 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3138 std::vector<fs::DirListNode>::iterator i2;
3139 for(i2=list2.begin(); i2!=list2.end(); i2++)
3145 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3147 catch(InvalidFilenameException &e)
3149 // This catches unknown crap in directory
3155 dstream<<"Sector converted to new layout - deleting "<<
3156 sectordir1<<std::endl;
3157 fs::RecursiveDelete(sectordir1);
3164 void ServerMap::saveBlock(MapBlock *block)
3166 DSTACK(__FUNCTION_NAME);
3168 Dummy blocks are not written
3170 if(block->isDummy())
3172 /*v3s16 p = block->getPos();
3173 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3174 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3178 // Format used for writing
3179 u8 version = SER_FMT_VER_HIGHEST;
3181 v3s16 p3d = block->getPos();
3183 v2s16 p2d(p3d.X, p3d.Z);
3184 std::string sectordir = getSectorDir(p2d);
3186 createDirs(sectordir);
3188 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3189 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3190 if(o.good() == false)
3191 throw FileNotGoodException("Cannot open block data");
3194 [0] u8 serialization version
3197 o.write((char*)&version, 1);
3200 block->serialize(o, version);
3202 // Write extra data stored on disk
3203 block->serializeDiskExtra(o, version);
3205 // We just wrote it to the disk so clear modified flag
3206 block->resetModified();
3209 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3211 DSTACK(__FUNCTION_NAME);
3213 std::string fullpath = sectordir+"/"+blockfile;
3216 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3217 if(is.good() == false)
3218 throw FileNotGoodException("Cannot open block file");
3220 v3s16 p3d = getBlockPos(sectordir, blockfile);
3221 v2s16 p2d(p3d.X, p3d.Z);
3223 assert(sector->getPos() == p2d);
3225 u8 version = SER_FMT_VER_INVALID;
3226 is.read((char*)&version, 1);
3229 throw SerializationError("ServerMap::loadBlock(): Failed"
3230 " to read MapBlock version");
3232 /*u32 block_size = MapBlock::serializedLength(version);
3233 SharedBuffer<u8> data(block_size);
3234 is.read((char*)*data, block_size);*/
3236 // This will always return a sector because we're the server
3237 //MapSector *sector = emergeSector(p2d);
3239 MapBlock *block = NULL;
3240 bool created_new = false;
3241 block = sector->getBlockNoCreateNoEx(p3d.Y);
3244 block = sector->createBlankBlockNoInsert(p3d.Y);
3249 block->deSerialize(is, version);
3251 // Read extra data stored on disk
3252 block->deSerializeDiskExtra(is, version);
3254 // If it's a new block, insert it to the map
3256 sector->insertBlock(block);
3259 Save blocks loaded in old format in new format
3262 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3267 // We just loaded it from the disk, so it's up-to-date.
3268 block->resetModified();
3271 catch(SerializationError &e)
3273 dstream<<"WARNING: Invalid block data on disk "
3274 <<"fullpath="<<fullpath
3275 <<" (SerializationError). "
3276 <<"what()="<<e.what()
3278 //" Ignoring. A new one will be generated.
3281 // TODO: Backup file; name is in fullpath.
3285 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3287 DSTACK(__FUNCTION_NAME);
3289 v2s16 p2d(blockpos.X, blockpos.Z);
3291 // The directory layout we're going to load from.
3292 // 1 - original sectors/xxxxzzzz/
3293 // 2 - new sectors2/xxx/zzz/
3294 // If we load from anything but the latest structure, we will
3295 // immediately save to the new one, and remove the old.
3297 std::string sectordir1 = getSectorDir(p2d, 1);
3298 std::string sectordir;
3299 if(fs::PathExists(sectordir1))
3301 sectordir = sectordir1;
3306 sectordir = getSectorDir(p2d, 2);
3310 Make sure sector is loaded
3312 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3316 sector = loadSectorMeta(sectordir, loadlayout != 2);
3318 catch(InvalidFilenameException &e)
3322 catch(FileNotGoodException &e)
3326 catch(std::exception &e)
3333 Make sure file exists
3336 std::string blockfilename = getBlockFilename(blockpos);
3337 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3343 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3344 return getBlockNoCreateNoEx(blockpos);
3347 void ServerMap::PrintInfo(std::ostream &out)
3358 ClientMap::ClientMap(
3360 MapDrawControl &control,
3361 scene::ISceneNode* parent,
3362 scene::ISceneManager* mgr,
3366 scene::ISceneNode(parent, mgr, id),
3369 m_camera_position(0,0,0),
3370 m_camera_direction(0,0,1)
3372 m_camera_mutex.Init();
3373 assert(m_camera_mutex.IsInitialized());
3375 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3376 BS*1000000,BS*1000000,BS*1000000);
3379 ClientMap::~ClientMap()
3381 /*JMutexAutoLock lock(mesh_mutex);
3390 MapSector * ClientMap::emergeSector(v2s16 p2d)
3392 DSTACK(__FUNCTION_NAME);
3393 // Check that it doesn't exist already
3395 return getSectorNoGenerate(p2d);
3397 catch(InvalidPositionException &e)
3402 ClientMapSector *sector = new ClientMapSector(this, p2d);
3405 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3406 m_sectors.insert(p2d, sector);
3413 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3415 DSTACK(__FUNCTION_NAME);
3416 ClientMapSector *sector = NULL;
3418 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3420 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3424 sector = (ClientMapSector*)n->getValue();
3425 assert(sector->getId() == MAPSECTOR_CLIENT);
3429 sector = new ClientMapSector(this, p2d);
3431 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3432 m_sectors.insert(p2d, sector);
3436 sector->deSerialize(is);
3440 void ClientMap::OnRegisterSceneNode()
3444 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3445 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3448 ISceneNode::OnRegisterSceneNode();
3451 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3453 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3454 DSTACK(__FUNCTION_NAME);
3456 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3459 This is called two times per frame, reset on the non-transparent one
3461 if(pass == scene::ESNRP_SOLID)
3463 m_last_drawn_sectors.clear();
3467 Get time for measuring timeout.
3469 Measuring time is very useful for long delays when the
3470 machine is swapping a lot.
3472 int time1 = time(0);
3474 //u32 daynight_ratio = m_client->getDayNightRatio();
3476 m_camera_mutex.Lock();
3477 v3f camera_position = m_camera_position;
3478 v3f camera_direction = m_camera_direction;
3479 m_camera_mutex.Unlock();
3482 Get all blocks and draw all visible ones
3485 v3s16 cam_pos_nodes(
3486 camera_position.X / BS,
3487 camera_position.Y / BS,
3488 camera_position.Z / BS);
3490 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3492 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3493 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3495 // Take a fair amount as we will be dropping more out later
3497 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3498 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3499 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3501 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3502 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3503 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3505 u32 vertex_count = 0;
3507 // For limiting number of mesh updates per frame
3508 u32 mesh_update_count = 0;
3510 u32 blocks_would_have_drawn = 0;
3511 u32 blocks_drawn = 0;
3513 int timecheck_counter = 0;
3514 core::map<v2s16, MapSector*>::Iterator si;
3515 si = m_sectors.getIterator();
3516 for(; si.atEnd() == false; si++)
3519 timecheck_counter++;
3520 if(timecheck_counter > 50)
3522 timecheck_counter = 0;
3523 int time2 = time(0);
3524 if(time2 > time1 + 4)
3526 dstream<<"ClientMap::renderMap(): "
3527 "Rendering takes ages, returning."
3534 MapSector *sector = si.getNode()->getValue();
3535 v2s16 sp = sector->getPos();
3537 if(m_control.range_all == false)
3539 if(sp.X < p_blocks_min.X
3540 || sp.X > p_blocks_max.X
3541 || sp.Y < p_blocks_min.Z
3542 || sp.Y > p_blocks_max.Z)
3546 core::list< MapBlock * > sectorblocks;
3547 sector->getBlocks(sectorblocks);
3553 u32 sector_blocks_drawn = 0;
3555 core::list< MapBlock * >::Iterator i;
3556 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3558 MapBlock *block = *i;
3561 Compare block position to camera position, skip
3562 if not seen on display
3565 float range = 100000 * BS;
3566 if(m_control.range_all == false)
3567 range = m_control.wanted_range * BS;
3570 if(isBlockInSight(block->getPos(), camera_position,
3571 camera_direction, range, &d) == false)
3576 // Okay, this block will be drawn. Reset usage timer.
3577 block->resetUsageTimer();
3579 // This is ugly (spherical distance limit?)
3580 /*if(m_control.range_all == false &&
3581 d - 0.5*BS*MAP_BLOCKSIZE > range)
3586 Update expired mesh (used for day/night change)
3588 It doesn't work exactly like it should now with the
3589 tasked mesh update but whatever.
3592 bool mesh_expired = false;
3595 JMutexAutoLock lock(block->mesh_mutex);
3597 mesh_expired = block->getMeshExpired();
3599 // Mesh has not been expired and there is no mesh:
3600 // block has no content
3601 if(block->mesh == NULL && mesh_expired == false)
3605 f32 faraway = BS*50;
3606 //f32 faraway = m_control.wanted_range * BS;
3609 This has to be done with the mesh_mutex unlocked
3611 // Pretty random but this should work somewhat nicely
3612 if(mesh_expired && (
3613 (mesh_update_count < 3
3614 && (d < faraway || mesh_update_count < 2)
3617 (m_control.range_all && mesh_update_count < 20)
3620 /*if(mesh_expired && mesh_update_count < 6
3621 && (d < faraway || mesh_update_count < 3))*/
3623 mesh_update_count++;
3625 // Mesh has been expired: generate new mesh
3626 //block->updateMesh(daynight_ratio);
3627 m_client->addUpdateMeshTask(block->getPos());
3629 mesh_expired = false;
3634 Draw the faces of the block
3637 JMutexAutoLock lock(block->mesh_mutex);
3639 scene::SMesh *mesh = block->mesh;
3644 blocks_would_have_drawn++;
3645 if(blocks_drawn >= m_control.wanted_max_blocks
3646 && m_control.range_all == false
3647 && d > m_control.wanted_min_range * BS)
3651 sector_blocks_drawn++;
3653 u32 c = mesh->getMeshBufferCount();
3655 for(u32 i=0; i<c; i++)
3657 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3658 const video::SMaterial& material = buf->getMaterial();
3659 video::IMaterialRenderer* rnd =
3660 driver->getMaterialRenderer(material.MaterialType);
3661 bool transparent = (rnd && rnd->isTransparent());
3662 // Render transparent on transparent pass and likewise.
3663 if(transparent == is_transparent_pass)
3666 This *shouldn't* hurt too much because Irrlicht
3667 doesn't change opengl textures if the old
3668 material is set again.
3670 driver->setMaterial(buf->getMaterial());
3671 driver->drawMeshBuffer(buf);
3672 vertex_count += buf->getVertexCount();
3676 } // foreach sectorblocks
3678 if(sector_blocks_drawn != 0)
3680 m_last_drawn_sectors[sp] = true;
3684 m_control.blocks_drawn = blocks_drawn;
3685 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3687 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3688 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3691 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3692 core::map<v3s16, MapBlock*> *affected_blocks)
3694 bool changed = false;
3696 Add it to all blocks touching it
3699 v3s16(0,0,0), // this
3700 v3s16(0,0,1), // back
3701 v3s16(0,1,0), // top
3702 v3s16(1,0,0), // right
3703 v3s16(0,0,-1), // front
3704 v3s16(0,-1,0), // bottom
3705 v3s16(-1,0,0), // left
3707 for(u16 i=0; i<7; i++)
3709 v3s16 p2 = p + dirs[i];
3710 // Block position of neighbor (or requested) node
3711 v3s16 blockpos = getNodeBlockPos(p2);
3712 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3713 if(blockref == NULL)
3715 // Relative position of requested node
3716 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3717 if(blockref->setTempMod(relpos, mod))
3722 if(changed && affected_blocks!=NULL)
3724 for(u16 i=0; i<7; i++)
3726 v3s16 p2 = p + dirs[i];
3727 // Block position of neighbor (or requested) node
3728 v3s16 blockpos = getNodeBlockPos(p2);
3729 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3730 if(blockref == NULL)
3732 affected_blocks->insert(blockpos, blockref);
3738 bool ClientMap::clearTempMod(v3s16 p,
3739 core::map<v3s16, MapBlock*> *affected_blocks)
3741 bool changed = false;
3743 v3s16(0,0,0), // this
3744 v3s16(0,0,1), // back
3745 v3s16(0,1,0), // top
3746 v3s16(1,0,0), // right
3747 v3s16(0,0,-1), // front
3748 v3s16(0,-1,0), // bottom
3749 v3s16(-1,0,0), // left
3751 for(u16 i=0; i<7; i++)
3753 v3s16 p2 = p + dirs[i];
3754 // Block position of neighbor (or requested) node
3755 v3s16 blockpos = getNodeBlockPos(p2);
3756 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3757 if(blockref == NULL)
3759 // Relative position of requested node
3760 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3761 if(blockref->clearTempMod(relpos))
3766 if(changed && affected_blocks!=NULL)
3768 for(u16 i=0; i<7; i++)
3770 v3s16 p2 = p + dirs[i];
3771 // Block position of neighbor (or requested) node
3772 v3s16 blockpos = getNodeBlockPos(p2);
3773 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3774 if(blockref == NULL)
3776 affected_blocks->insert(blockpos, blockref);
3782 void ClientMap::expireMeshes(bool only_daynight_diffed)
3784 TimeTaker timer("expireMeshes()");
3786 core::map<v2s16, MapSector*>::Iterator si;
3787 si = m_sectors.getIterator();
3788 for(; si.atEnd() == false; si++)
3790 MapSector *sector = si.getNode()->getValue();
3792 core::list< MapBlock * > sectorblocks;
3793 sector->getBlocks(sectorblocks);
3795 core::list< MapBlock * >::Iterator i;
3796 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3798 MapBlock *block = *i;
3800 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3806 JMutexAutoLock lock(block->mesh_mutex);
3807 if(block->mesh != NULL)
3809 /*block->mesh->drop();
3810 block->mesh = NULL;*/
3811 block->setMeshExpired(true);
3818 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3820 assert(mapType() == MAPTYPE_CLIENT);
3823 v3s16 p = blockpos + v3s16(0,0,0);
3824 MapBlock *b = getBlockNoCreate(p);
3825 b->updateMesh(daynight_ratio);
3826 //b->setMeshExpired(true);
3828 catch(InvalidPositionException &e){}
3831 v3s16 p = blockpos + v3s16(-1,0,0);
3832 MapBlock *b = getBlockNoCreate(p);
3833 b->updateMesh(daynight_ratio);
3834 //b->setMeshExpired(true);
3836 catch(InvalidPositionException &e){}
3838 v3s16 p = blockpos + v3s16(0,-1,0);
3839 MapBlock *b = getBlockNoCreate(p);
3840 b->updateMesh(daynight_ratio);
3841 //b->setMeshExpired(true);
3843 catch(InvalidPositionException &e){}
3845 v3s16 p = blockpos + v3s16(0,0,-1);
3846 MapBlock *b = getBlockNoCreate(p);
3847 b->updateMesh(daynight_ratio);
3848 //b->setMeshExpired(true);
3850 catch(InvalidPositionException &e){}
3855 Update mesh of block in which the node is, and if the node is at the
3856 leading edge, update the appropriate leading blocks too.
3858 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3866 v3s16 blockposes[4];
3867 for(u32 i=0; i<4; i++)
3869 v3s16 np = nodepos + dirs[i];
3870 blockposes[i] = getNodeBlockPos(np);
3871 // Don't update mesh of block if it has been done already
3872 bool already_updated = false;
3873 for(u32 j=0; j<i; j++)
3875 if(blockposes[j] == blockposes[i])
3877 already_updated = true;
3884 MapBlock *b = getBlockNoCreate(blockposes[i]);
3885 b->updateMesh(daynight_ratio);
3890 void ClientMap::PrintInfo(std::ostream &out)
3901 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3906 MapVoxelManipulator::~MapVoxelManipulator()
3908 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3912 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3914 TimeTaker timer1("emerge", &emerge_time);
3916 // Units of these are MapBlocks
3917 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3918 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3920 VoxelArea block_area_nodes
3921 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3923 addArea(block_area_nodes);
3925 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3926 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3927 for(s32 x=p_min.X; x<=p_max.X; x++)
3930 core::map<v3s16, bool>::Node *n;
3931 n = m_loaded_blocks.find(p);
3935 bool block_data_inexistent = false;
3938 TimeTaker timer1("emerge load", &emerge_load_time);
3940 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3941 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3944 dstream<<std::endl;*/
3946 MapBlock *block = m_map->getBlockNoCreate(p);
3947 if(block->isDummy())
3948 block_data_inexistent = true;
3950 block->copyTo(*this);
3952 catch(InvalidPositionException &e)
3954 block_data_inexistent = true;
3957 if(block_data_inexistent)
3959 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3960 // Fill with VOXELFLAG_INEXISTENT
3961 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3962 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3964 s32 i = m_area.index(a.MinEdge.X,y,z);
3965 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3969 m_loaded_blocks.insert(p, !block_data_inexistent);
3972 //dstream<<"emerge done"<<std::endl;
3976 SUGG: Add an option to only update eg. water and air nodes.
3977 This will make it interfere less with important stuff if
3980 void MapVoxelManipulator::blitBack
3981 (core::map<v3s16, MapBlock*> & modified_blocks)
3983 if(m_area.getExtent() == v3s16(0,0,0))
3986 //TimeTaker timer1("blitBack");
3988 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3989 <<m_loaded_blocks.size()<<std::endl;*/
3992 Initialize block cache
3994 v3s16 blockpos_last;
3995 MapBlock *block = NULL;
3996 bool block_checked_in_modified = false;
3998 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3999 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4000 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4004 u8 f = m_flags[m_area.index(p)];
4005 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4008 MapNode &n = m_data[m_area.index(p)];
4010 v3s16 blockpos = getNodeBlockPos(p);
4015 if(block == NULL || blockpos != blockpos_last){
4016 block = m_map->getBlockNoCreate(blockpos);
4017 blockpos_last = blockpos;
4018 block_checked_in_modified = false;
4021 // Calculate relative position in block
4022 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4024 // Don't continue if nothing has changed here
4025 if(block->getNode(relpos) == n)
4028 //m_map->setNode(m_area.MinEdge + p, n);
4029 block->setNode(relpos, n);
4032 Make sure block is in modified_blocks
4034 if(block_checked_in_modified == false)
4036 modified_blocks[blockpos] = block;
4037 block_checked_in_modified = true;
4040 catch(InvalidPositionException &e)
4046 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4047 MapVoxelManipulator(map),
4048 m_create_area(false)
4052 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4056 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4058 // Just create the area so that it can be pointed to
4059 VoxelManipulator::emerge(a, caller_id);
4062 void ManualMapVoxelManipulator::initialEmerge(
4063 v3s16 blockpos_min, v3s16 blockpos_max)
4065 TimeTaker timer1("initialEmerge", &emerge_time);
4067 // Units of these are MapBlocks
4068 v3s16 p_min = blockpos_min;
4069 v3s16 p_max = blockpos_max;
4071 VoxelArea block_area_nodes
4072 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4074 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4077 dstream<<"initialEmerge: area: ";
4078 block_area_nodes.print(dstream);
4079 dstream<<" ("<<size_MB<<"MB)";
4083 addArea(block_area_nodes);
4085 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4086 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4087 for(s32 x=p_min.X; x<=p_max.X; x++)
4090 core::map<v3s16, bool>::Node *n;
4091 n = m_loaded_blocks.find(p);
4095 bool block_data_inexistent = false;
4098 TimeTaker timer1("emerge load", &emerge_load_time);
4100 MapBlock *block = m_map->getBlockNoCreate(p);
4101 if(block->isDummy())
4102 block_data_inexistent = true;
4104 block->copyTo(*this);
4106 catch(InvalidPositionException &e)
4108 block_data_inexistent = true;
4111 if(block_data_inexistent)
4114 Mark area inexistent
4116 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4117 // Fill with VOXELFLAG_INEXISTENT
4118 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4119 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4121 s32 i = m_area.index(a.MinEdge.X,y,z);
4122 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4126 m_loaded_blocks.insert(p, !block_data_inexistent);
4130 void ManualMapVoxelManipulator::blitBackAll(
4131 core::map<v3s16, MapBlock*> * modified_blocks)
4133 if(m_area.getExtent() == v3s16(0,0,0))
4137 Copy data of all blocks
4139 for(core::map<v3s16, bool>::Iterator
4140 i = m_loaded_blocks.getIterator();
4141 i.atEnd() == false; i++)
4143 v3s16 p = i.getNode()->getKey();
4144 bool existed = i.getNode()->getValue();
4145 if(existed == false)
4147 // The Great Bug was found using this
4148 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4149 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4153 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4156 dstream<<"WARNING: "<<__FUNCTION_NAME
4157 <<": got NULL block "
4158 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4163 block->copyFrom(*this);
4166 modified_blocks->insert(p, block);