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"
31 #include "serverobject.h"
32 #include "content_mapnode.h"
39 SQLite format specification:
40 - Initially only replaces sectors/ and sectors2/
47 Map::Map(std::ostream &dout):
51 /*m_sector_mutex.Init();
52 assert(m_sector_mutex.IsInitialized());*/
60 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
61 for(; i.atEnd() == false; i++)
63 MapSector *sector = i.getNode()->getValue();
68 void Map::addEventReceiver(MapEventReceiver *event_receiver)
70 m_event_receivers.insert(event_receiver, false);
73 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
75 if(m_event_receivers.find(event_receiver) == NULL)
77 m_event_receivers.remove(event_receiver);
80 void Map::dispatchEvent(MapEditEvent *event)
82 for(core::map<MapEventReceiver*, bool>::Iterator
83 i = m_event_receivers.getIterator();
84 i.atEnd()==false; i++)
86 MapEventReceiver* event_receiver = i.getNode()->getKey();
87 event_receiver->onMapEditEvent(event);
91 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
93 if(m_sector_cache != NULL && p == m_sector_cache_p){
94 MapSector * sector = m_sector_cache;
98 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
103 MapSector *sector = n->getValue();
105 // Cache the last result
106 m_sector_cache_p = p;
107 m_sector_cache = sector;
112 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
114 return getSectorNoGenerateNoExNoLock(p);
117 MapSector * Map::getSectorNoGenerate(v2s16 p)
119 MapSector *sector = getSectorNoGenerateNoEx(p);
121 throw InvalidPositionException();
126 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
128 v2s16 p2d(p3d.X, p3d.Z);
129 MapSector * sector = getSectorNoGenerate(p2d);
130 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
134 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
136 MapBlock *block = getBlockNoCreateNoEx(p3d);
138 throw InvalidPositionException();
143 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
145 v2s16 p2d(p3d.X, p3d.Z);
146 MapSector * sector = getSectorCreate(p2d);
148 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
151 block = sector->createBlankBlock(p3d.Y);
155 bool Map::isNodeUnderground(v3s16 p)
157 v3s16 blockpos = getNodeBlockPos(p);
159 MapBlock * block = getBlockNoCreate(blockpos);
160 return block->getIsUnderground();
162 catch(InvalidPositionException &e)
169 Goes recursively through the neighbours of the node.
171 Alters only transparent nodes.
173 If the lighting of the neighbour is lower than the lighting of
174 the node was (before changing it to 0 at the step before), the
175 lighting of the neighbour is set to 0 and then the same stuff
176 repeats for the neighbour.
178 The ending nodes of the routine are stored in light_sources.
179 This is useful when a light is removed. In such case, this
180 routine can be called for the light node and then again for
181 light_sources to re-light the area without the removed light.
183 values of from_nodes are lighting values.
185 void Map::unspreadLight(enum LightBank bank,
186 core::map<v3s16, u8> & from_nodes,
187 core::map<v3s16, bool> & light_sources,
188 core::map<v3s16, MapBlock*> & modified_blocks)
191 v3s16(0,0,1), // back
193 v3s16(1,0,0), // right
194 v3s16(0,0,-1), // front
195 v3s16(0,-1,0), // bottom
196 v3s16(-1,0,0), // left
199 if(from_nodes.size() == 0)
202 u32 blockchangecount = 0;
204 core::map<v3s16, u8> unlighted_nodes;
205 core::map<v3s16, u8>::Iterator j;
206 j = from_nodes.getIterator();
209 Initialize block cache
212 MapBlock *block = NULL;
213 // Cache this a bit, too
214 bool block_checked_in_modified = false;
216 for(; j.atEnd() == false; j++)
218 v3s16 pos = j.getNode()->getKey();
219 v3s16 blockpos = getNodeBlockPos(pos);
221 // Only fetch a new block if the block position has changed
223 if(block == NULL || blockpos != blockpos_last){
224 block = getBlockNoCreate(blockpos);
225 blockpos_last = blockpos;
227 block_checked_in_modified = false;
231 catch(InvalidPositionException &e)
239 // Calculate relative position in block
240 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
242 // Get node straight from the block
243 MapNode n = block->getNode(relpos);
245 u8 oldlight = j.getNode()->getValue();
247 // Loop through 6 neighbors
248 for(u16 i=0; i<6; i++)
250 // Get the position of the neighbor node
251 v3s16 n2pos = pos + dirs[i];
253 // Get the block where the node is located
254 v3s16 blockpos = getNodeBlockPos(n2pos);
258 // Only fetch a new block if the block position has changed
260 if(block == NULL || blockpos != blockpos_last){
261 block = getBlockNoCreate(blockpos);
262 blockpos_last = blockpos;
264 block_checked_in_modified = false;
268 catch(InvalidPositionException &e)
273 // Calculate relative position in block
274 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
275 // Get node straight from the block
276 MapNode n2 = block->getNode(relpos);
278 bool changed = false;
280 //TODO: Optimize output by optimizing light_sources?
283 If the neighbor is dimmer than what was specified
284 as oldlight (the light of the previous node)
286 if(n2.getLight(bank) < oldlight)
289 And the neighbor is transparent and it has some light
291 if(n2.light_propagates() && n2.getLight(bank) != 0)
294 Set light to 0 and add to queue
297 u8 current_light = n2.getLight(bank);
298 n2.setLight(bank, 0);
299 block->setNode(relpos, n2);
301 unlighted_nodes.insert(n2pos, current_light);
305 Remove from light_sources if it is there
306 NOTE: This doesn't happen nearly at all
308 /*if(light_sources.find(n2pos))
310 std::cout<<"Removed from light_sources"<<std::endl;
311 light_sources.remove(n2pos);
316 if(light_sources.find(n2pos) != NULL)
317 light_sources.remove(n2pos);*/
320 light_sources.insert(n2pos, true);
323 // Add to modified_blocks
324 if(changed == true && block_checked_in_modified == false)
326 // If the block is not found in modified_blocks, add.
327 if(modified_blocks.find(blockpos) == NULL)
329 modified_blocks.insert(blockpos, block);
331 block_checked_in_modified = true;
334 catch(InvalidPositionException &e)
341 /*dstream<<"unspreadLight(): Changed block "
342 <<blockchangecount<<" times"
343 <<" for "<<from_nodes.size()<<" nodes"
346 if(unlighted_nodes.size() > 0)
347 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
351 A single-node wrapper of the above
353 void Map::unLightNeighbors(enum LightBank bank,
354 v3s16 pos, u8 lightwas,
355 core::map<v3s16, bool> & light_sources,
356 core::map<v3s16, MapBlock*> & modified_blocks)
358 core::map<v3s16, u8> from_nodes;
359 from_nodes.insert(pos, lightwas);
361 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
365 Lights neighbors of from_nodes, collects all them and then
368 void Map::spreadLight(enum LightBank bank,
369 core::map<v3s16, bool> & from_nodes,
370 core::map<v3s16, MapBlock*> & modified_blocks)
372 const v3s16 dirs[6] = {
373 v3s16(0,0,1), // back
375 v3s16(1,0,0), // right
376 v3s16(0,0,-1), // front
377 v3s16(0,-1,0), // bottom
378 v3s16(-1,0,0), // left
381 if(from_nodes.size() == 0)
384 u32 blockchangecount = 0;
386 core::map<v3s16, bool> lighted_nodes;
387 core::map<v3s16, bool>::Iterator j;
388 j = from_nodes.getIterator();
391 Initialize block cache
394 MapBlock *block = NULL;
395 // Cache this a bit, too
396 bool block_checked_in_modified = false;
398 for(; j.atEnd() == false; j++)
399 //for(; j != from_nodes.end(); j++)
401 v3s16 pos = j.getNode()->getKey();
403 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
404 v3s16 blockpos = getNodeBlockPos(pos);
406 // Only fetch a new block if the block position has changed
408 if(block == NULL || blockpos != blockpos_last){
409 block = getBlockNoCreate(blockpos);
410 blockpos_last = blockpos;
412 block_checked_in_modified = false;
416 catch(InvalidPositionException &e)
424 // Calculate relative position in block
425 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
427 // Get node straight from the block
428 MapNode n = block->getNode(relpos);
430 u8 oldlight = n.getLight(bank);
431 u8 newlight = diminish_light(oldlight);
433 // Loop through 6 neighbors
434 for(u16 i=0; i<6; i++){
435 // Get the position of the neighbor node
436 v3s16 n2pos = pos + dirs[i];
438 // Get the block where the node is located
439 v3s16 blockpos = getNodeBlockPos(n2pos);
443 // Only fetch a new block if the block position has changed
445 if(block == NULL || blockpos != blockpos_last){
446 block = getBlockNoCreate(blockpos);
447 blockpos_last = blockpos;
449 block_checked_in_modified = false;
453 catch(InvalidPositionException &e)
458 // Calculate relative position in block
459 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
460 // Get node straight from the block
461 MapNode n2 = block->getNode(relpos);
463 bool changed = false;
465 If the neighbor is brighter than the current node,
466 add to list (it will light up this node on its turn)
468 if(n2.getLight(bank) > undiminish_light(oldlight))
470 lighted_nodes.insert(n2pos, true);
471 //lighted_nodes.push_back(n2pos);
475 If the neighbor is dimmer than how much light this node
476 would spread on it, add to list
478 if(n2.getLight(bank) < newlight)
480 if(n2.light_propagates())
482 n2.setLight(bank, newlight);
483 block->setNode(relpos, n2);
484 lighted_nodes.insert(n2pos, true);
485 //lighted_nodes.push_back(n2pos);
490 // Add to modified_blocks
491 if(changed == true && block_checked_in_modified == false)
493 // If the block is not found in modified_blocks, add.
494 if(modified_blocks.find(blockpos) == NULL)
496 modified_blocks.insert(blockpos, block);
498 block_checked_in_modified = true;
501 catch(InvalidPositionException &e)
508 /*dstream<<"spreadLight(): Changed block "
509 <<blockchangecount<<" times"
510 <<" for "<<from_nodes.size()<<" nodes"
513 if(lighted_nodes.size() > 0)
514 spreadLight(bank, lighted_nodes, modified_blocks);
518 A single-node source variation of the above.
520 void Map::lightNeighbors(enum LightBank bank,
522 core::map<v3s16, MapBlock*> & modified_blocks)
524 core::map<v3s16, bool> from_nodes;
525 from_nodes.insert(pos, true);
526 spreadLight(bank, from_nodes, modified_blocks);
529 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
532 v3s16(0,0,1), // back
534 v3s16(1,0,0), // right
535 v3s16(0,0,-1), // front
536 v3s16(0,-1,0), // bottom
537 v3s16(-1,0,0), // left
540 u8 brightest_light = 0;
541 v3s16 brightest_pos(0,0,0);
542 bool found_something = false;
544 // Loop through 6 neighbors
545 for(u16 i=0; i<6; i++){
546 // Get the position of the neighbor node
547 v3s16 n2pos = p + dirs[i];
552 catch(InvalidPositionException &e)
556 if(n2.getLight(bank) > brightest_light || found_something == false){
557 brightest_light = n2.getLight(bank);
558 brightest_pos = n2pos;
559 found_something = true;
563 if(found_something == false)
564 throw InvalidPositionException();
566 return brightest_pos;
570 Propagates sunlight down from a node.
571 Starting point gets sunlight.
573 Returns the lowest y value of where the sunlight went.
575 Mud is turned into grass in where the sunlight stops.
577 s16 Map::propagateSunlight(v3s16 start,
578 core::map<v3s16, MapBlock*> & modified_blocks)
583 v3s16 pos(start.X, y, start.Z);
585 v3s16 blockpos = getNodeBlockPos(pos);
588 block = getBlockNoCreate(blockpos);
590 catch(InvalidPositionException &e)
595 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
596 MapNode n = block->getNode(relpos);
598 if(n.sunlight_propagates())
600 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
601 block->setNode(relpos, n);
603 modified_blocks.insert(blockpos, block);
607 /*// Turn mud into grass
608 if(n.d == CONTENT_MUD)
611 block->setNode(relpos, n);
612 modified_blocks.insert(blockpos, block);
615 // Sunlight goes no further
622 void Map::updateLighting(enum LightBank bank,
623 core::map<v3s16, MapBlock*> & a_blocks,
624 core::map<v3s16, MapBlock*> & modified_blocks)
626 /*m_dout<<DTIME<<"Map::updateLighting(): "
627 <<a_blocks.size()<<" blocks."<<std::endl;*/
629 //TimeTaker timer("updateLighting");
633 //u32 count_was = modified_blocks.size();
635 core::map<v3s16, MapBlock*> blocks_to_update;
637 core::map<v3s16, bool> light_sources;
639 core::map<v3s16, u8> unlight_from;
641 core::map<v3s16, MapBlock*>::Iterator i;
642 i = a_blocks.getIterator();
643 for(; i.atEnd() == false; i++)
645 MapBlock *block = i.getNode()->getValue();
649 // Don't bother with dummy blocks.
653 v3s16 pos = block->getPos();
654 modified_blocks.insert(pos, block);
656 blocks_to_update.insert(pos, block);
659 Clear all light from block
661 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
662 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
663 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
668 MapNode n = block->getNode(v3s16(x,y,z));
669 u8 oldlight = n.getLight(bank);
671 block->setNode(v3s16(x,y,z), n);
673 // Collect borders for unlighting
674 if(x==0 || x == MAP_BLOCKSIZE-1
675 || y==0 || y == MAP_BLOCKSIZE-1
676 || z==0 || z == MAP_BLOCKSIZE-1)
678 v3s16 p_map = p + v3s16(
681 MAP_BLOCKSIZE*pos.Z);
682 unlight_from.insert(p_map, oldlight);
685 catch(InvalidPositionException &e)
688 This would happen when dealing with a
692 dstream<<"updateLighting(): InvalidPositionException"
697 if(bank == LIGHTBANK_DAY)
699 bool bottom_valid = block->propagateSunlight(light_sources);
701 // If bottom is valid, we're done.
705 else if(bank == LIGHTBANK_NIGHT)
707 // For night lighting, sunlight is not propagated
712 // Invalid lighting bank
716 /*dstream<<"Bottom for sunlight-propagated block ("
717 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
720 // Bottom sunlight is not valid; get the block and loop to it
724 block = getBlockNoCreate(pos);
726 catch(InvalidPositionException &e)
736 TimeTaker timer("unspreadLight");
737 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
742 u32 diff = modified_blocks.size() - count_was;
743 count_was = modified_blocks.size();
744 dstream<<"unspreadLight modified "<<diff<<std::endl;
748 TimeTaker timer("spreadLight");
749 spreadLight(bank, light_sources, modified_blocks);
754 u32 diff = modified_blocks.size() - count_was;
755 count_was = modified_blocks.size();
756 dstream<<"spreadLight modified "<<diff<<std::endl;
761 //MapVoxelManipulator vmanip(this);
763 // Make a manual voxel manipulator and load all the blocks
764 // that touch the requested blocks
765 ManualMapVoxelManipulator vmanip(this);
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();
773 // Add all surrounding blocks
774 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
777 Add all surrounding blocks that have up-to-date lighting
778 NOTE: This doesn't quite do the job (not everything
779 appropriate is lighted)
781 /*for(s16 z=-1; z<=1; z++)
782 for(s16 y=-1; y<=1; y++)
783 for(s16 x=-1; x<=1; x++)
786 MapBlock *block = getBlockNoCreateNoEx(p);
791 if(block->getLightingExpired())
793 vmanip.initialEmerge(p, p);
796 // Lighting of block will be updated completely
797 block->setLightingExpired(false);
801 //TimeTaker timer("unSpreadLight");
802 vmanip.unspreadLight(bank, unlight_from, light_sources);
805 //TimeTaker timer("spreadLight");
806 vmanip.spreadLight(bank, light_sources);
809 //TimeTaker timer("blitBack");
810 vmanip.blitBack(modified_blocks);
812 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
816 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
819 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
820 core::map<v3s16, MapBlock*> & modified_blocks)
822 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
823 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
826 Update information about whether day and night light differ
828 for(core::map<v3s16, MapBlock*>::Iterator
829 i = modified_blocks.getIterator();
830 i.atEnd() == false; i++)
832 MapBlock *block = i.getNode()->getValue();
833 block->updateDayNightDiff();
839 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
840 core::map<v3s16, MapBlock*> &modified_blocks)
843 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
844 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
847 From this node to nodes underneath:
848 If lighting is sunlight (1.0), unlight neighbours and
853 v3s16 toppos = p + v3s16(0,1,0);
854 v3s16 bottompos = p + v3s16(0,-1,0);
856 bool node_under_sunlight = true;
857 core::map<v3s16, bool> light_sources;
860 If there is a node at top and it doesn't have sunlight,
861 there has not been any sunlight going down.
863 Otherwise there probably is.
866 MapNode topnode = getNode(toppos);
868 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
869 node_under_sunlight = false;
871 catch(InvalidPositionException &e)
877 If the new node is solid and there is grass below, change it to mud
879 if(content_features(n.d).walkable == true)
882 MapNode bottomnode = getNode(bottompos);
884 if(bottomnode.d == CONTENT_GRASS
885 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
887 bottomnode.d = CONTENT_MUD;
888 setNode(bottompos, bottomnode);
891 catch(InvalidPositionException &e)
899 If the new node is mud and it is under sunlight, change it
902 if(n.d == CONTENT_MUD && node_under_sunlight)
909 Remove all light that has come out of this node
912 enum LightBank banks[] =
917 for(s32 i=0; i<2; i++)
919 enum LightBank bank = banks[i];
921 u8 lightwas = getNode(p).getLight(bank);
923 // Add the block of the added node to modified_blocks
924 v3s16 blockpos = getNodeBlockPos(p);
925 MapBlock * block = getBlockNoCreate(blockpos);
926 assert(block != NULL);
927 modified_blocks.insert(blockpos, block);
929 assert(isValidPosition(p));
931 // Unlight neighbours of node.
932 // This means setting light of all consequent dimmer nodes
934 // This also collects the nodes at the border which will spread
935 // light again into this.
936 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
942 If node lets sunlight through and is under sunlight, it has
945 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
947 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
951 Set the node on the map
960 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
963 NodeMetadata *meta = meta_proto->clone();
964 setNodeMetadata(p, meta);
968 If node is under sunlight and doesn't let sunlight through,
969 take all sunlighted nodes under it and clear light from them
970 and from where the light has been spread.
971 TODO: This could be optimized by mass-unlighting instead
974 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
978 //m_dout<<DTIME<<"y="<<y<<std::endl;
979 v3s16 n2pos(p.X, y, p.Z);
985 catch(InvalidPositionException &e)
990 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
992 unLightNeighbors(LIGHTBANK_DAY,
993 n2pos, n2.getLight(LIGHTBANK_DAY),
994 light_sources, modified_blocks);
995 n2.setLight(LIGHTBANK_DAY, 0);
1003 for(s32 i=0; i<2; i++)
1005 enum LightBank bank = banks[i];
1008 Spread light from all nodes that might be capable of doing so
1010 spreadLight(bank, light_sources, modified_blocks);
1014 Update information about whether day and night light differ
1016 for(core::map<v3s16, MapBlock*>::Iterator
1017 i = modified_blocks.getIterator();
1018 i.atEnd() == false; i++)
1020 MapBlock *block = i.getNode()->getValue();
1021 block->updateDayNightDiff();
1025 Add neighboring liquid nodes and the node itself if it is
1026 liquid (=water node was added) to transform queue.
1029 v3s16(0,0,0), // self
1030 v3s16(0,0,1), // back
1031 v3s16(0,1,0), // top
1032 v3s16(1,0,0), // right
1033 v3s16(0,0,-1), // front
1034 v3s16(0,-1,0), // bottom
1035 v3s16(-1,0,0), // left
1037 for(u16 i=0; i<7; i++)
1042 v3s16 p2 = p + dirs[i];
1044 MapNode n2 = getNode(p2);
1045 if(content_liquid(n2.d))
1047 m_transforming_liquid.push_back(p2);
1050 }catch(InvalidPositionException &e)
1058 void Map::removeNodeAndUpdate(v3s16 p,
1059 core::map<v3s16, MapBlock*> &modified_blocks)
1061 /*PrintInfo(m_dout);
1062 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1063 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1065 bool node_under_sunlight = true;
1067 v3s16 toppos = p + v3s16(0,1,0);
1069 // Node will be replaced with this
1070 u8 replace_material = CONTENT_AIR;
1073 If there is a node at top and it doesn't have sunlight,
1074 there will be no sunlight going down.
1077 MapNode topnode = getNode(toppos);
1079 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1080 node_under_sunlight = false;
1082 catch(InvalidPositionException &e)
1086 core::map<v3s16, bool> light_sources;
1088 enum LightBank banks[] =
1093 for(s32 i=0; i<2; i++)
1095 enum LightBank bank = banks[i];
1098 Unlight neighbors (in case the node is a light source)
1100 unLightNeighbors(bank, p,
1101 getNode(p).getLight(bank),
1102 light_sources, modified_blocks);
1106 Remove node metadata
1109 removeNodeMetadata(p);
1113 This also clears the lighting.
1117 n.d = replace_material;
1120 for(s32 i=0; i<2; i++)
1122 enum LightBank bank = banks[i];
1125 Recalculate lighting
1127 spreadLight(bank, light_sources, modified_blocks);
1130 // Add the block of the removed node to modified_blocks
1131 v3s16 blockpos = getNodeBlockPos(p);
1132 MapBlock * block = getBlockNoCreate(blockpos);
1133 assert(block != NULL);
1134 modified_blocks.insert(blockpos, block);
1137 If the removed node was under sunlight, propagate the
1138 sunlight down from it and then light all neighbors
1139 of the propagated blocks.
1141 if(node_under_sunlight)
1143 s16 ybottom = propagateSunlight(p, modified_blocks);
1144 /*m_dout<<DTIME<<"Node was under sunlight. "
1145 "Propagating sunlight";
1146 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1148 for(; y >= ybottom; y--)
1150 v3s16 p2(p.X, y, p.Z);
1151 /*m_dout<<DTIME<<"lighting neighbors of node ("
1152 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1154 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1159 // Set the lighting of this node to 0
1160 // TODO: Is this needed? Lighting is cleared up there already.
1162 MapNode n = getNode(p);
1163 n.setLight(LIGHTBANK_DAY, 0);
1166 catch(InvalidPositionException &e)
1172 for(s32 i=0; i<2; i++)
1174 enum LightBank bank = banks[i];
1176 // Get the brightest neighbour node and propagate light from it
1177 v3s16 n2p = getBrightestNeighbour(bank, p);
1179 MapNode n2 = getNode(n2p);
1180 lightNeighbors(bank, n2p, modified_blocks);
1182 catch(InvalidPositionException &e)
1188 Update information about whether day and night light differ
1190 for(core::map<v3s16, MapBlock*>::Iterator
1191 i = modified_blocks.getIterator();
1192 i.atEnd() == false; i++)
1194 MapBlock *block = i.getNode()->getValue();
1195 block->updateDayNightDiff();
1199 Add neighboring liquid nodes to transform queue.
1202 v3s16(0,0,1), // back
1203 v3s16(0,1,0), // top
1204 v3s16(1,0,0), // right
1205 v3s16(0,0,-1), // front
1206 v3s16(0,-1,0), // bottom
1207 v3s16(-1,0,0), // left
1209 for(u16 i=0; i<6; i++)
1214 v3s16 p2 = p + dirs[i];
1216 MapNode n2 = getNode(p2);
1217 if(content_liquid(n2.d))
1219 m_transforming_liquid.push_back(p2);
1222 }catch(InvalidPositionException &e)
1228 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1231 event.type = MEET_ADDNODE;
1235 bool succeeded = true;
1237 core::map<v3s16, MapBlock*> modified_blocks;
1238 addNodeAndUpdate(p, n, modified_blocks);
1240 // Copy modified_blocks to event
1241 for(core::map<v3s16, MapBlock*>::Iterator
1242 i = modified_blocks.getIterator();
1243 i.atEnd()==false; i++)
1245 event.modified_blocks.insert(i.getNode()->getKey(), false);
1248 catch(InvalidPositionException &e){
1252 dispatchEvent(&event);
1257 bool Map::removeNodeWithEvent(v3s16 p)
1260 event.type = MEET_REMOVENODE;
1263 bool succeeded = true;
1265 core::map<v3s16, MapBlock*> modified_blocks;
1266 removeNodeAndUpdate(p, modified_blocks);
1268 // Copy modified_blocks to event
1269 for(core::map<v3s16, MapBlock*>::Iterator
1270 i = modified_blocks.getIterator();
1271 i.atEnd()==false; i++)
1273 event.modified_blocks.insert(i.getNode()->getKey(), false);
1276 catch(InvalidPositionException &e){
1280 dispatchEvent(&event);
1285 bool Map::dayNightDiffed(v3s16 blockpos)
1288 v3s16 p = blockpos + v3s16(0,0,0);
1289 MapBlock *b = getBlockNoCreate(p);
1290 if(b->dayNightDiffed())
1293 catch(InvalidPositionException &e){}
1296 v3s16 p = blockpos + v3s16(-1,0,0);
1297 MapBlock *b = getBlockNoCreate(p);
1298 if(b->dayNightDiffed())
1301 catch(InvalidPositionException &e){}
1303 v3s16 p = blockpos + v3s16(0,-1,0);
1304 MapBlock *b = getBlockNoCreate(p);
1305 if(b->dayNightDiffed())
1308 catch(InvalidPositionException &e){}
1310 v3s16 p = blockpos + v3s16(0,0,-1);
1311 MapBlock *b = getBlockNoCreate(p);
1312 if(b->dayNightDiffed())
1315 catch(InvalidPositionException &e){}
1318 v3s16 p = blockpos + v3s16(1,0,0);
1319 MapBlock *b = getBlockNoCreate(p);
1320 if(b->dayNightDiffed())
1323 catch(InvalidPositionException &e){}
1325 v3s16 p = blockpos + v3s16(0,1,0);
1326 MapBlock *b = getBlockNoCreate(p);
1327 if(b->dayNightDiffed())
1330 catch(InvalidPositionException &e){}
1332 v3s16 p = blockpos + v3s16(0,0,1);
1333 MapBlock *b = getBlockNoCreate(p);
1334 if(b->dayNightDiffed())
1337 catch(InvalidPositionException &e){}
1343 Updates usage timers
1345 void Map::timerUpdate(float dtime)
1347 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1349 core::map<v2s16, MapSector*>::Iterator si;
1351 si = m_sectors.getIterator();
1352 for(; si.atEnd() == false; si++)
1354 MapSector *sector = si.getNode()->getValue();
1356 core::list<MapBlock*> blocks;
1357 sector->getBlocks(blocks);
1358 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1359 i != blocks.end(); i++)
1361 (*i)->incrementUsageTimer(dtime);
1366 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1368 core::list<v2s16>::Iterator j;
1369 for(j=list.begin(); j!=list.end(); j++)
1371 MapSector *sector = m_sectors[*j];
1374 sector->deleteBlocks();
1379 If sector is in sector cache, remove it from there
1381 if(m_sector_cache == sector)
1383 m_sector_cache = NULL;
1386 Remove from map and delete
1388 m_sectors.remove(*j);
1394 u32 Map::unloadUnusedData(float timeout, bool only_blocks,
1395 core::list<v3s16> *deleted_blocks)
1397 core::list<v2s16> sector_deletion_queue;
1399 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1400 for(; si.atEnd() == false; si++)
1402 MapSector *sector = si.getNode()->getValue();
1404 bool all_blocks_deleted = true;
1406 core::list<MapBlock*> blocks;
1407 sector->getBlocks(blocks);
1408 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1409 i != blocks.end(); i++)
1411 MapBlock *block = (*i);
1413 if(block->getUsageTimer() > timeout)
1416 if(block->getModified() != MOD_STATE_CLEAN)
1418 // Delete from memory
1419 sector->deleteBlock(block);
1423 all_blocks_deleted = false;
1427 if(all_blocks_deleted)
1429 sector_deletion_queue.push_back(si.getNode()->getKey());
1434 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1435 for(; i.atEnd() == false; i++)
1437 MapSector *sector = i.getNode()->getValue();
1439 Delete sector from memory if it hasn't been used in a long time
1441 if(sector->usage_timer > timeout)
1443 sector_deletion_queue.push_back(i.getNode()->getKey());
1445 if(deleted_blocks != NULL)
1447 // Collect positions of blocks of sector
1448 MapSector *sector = i.getNode()->getValue();
1449 core::list<MapBlock*> blocks;
1450 sector->getBlocks(blocks);
1451 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1452 i != blocks.end(); i++)
1454 deleted_blocks->push_back((*i)->getPos());
1461 deleteSectors(sector_deletion_queue, only_blocks);
1462 return sector_deletion_queue.getSize();
1465 void Map::PrintInfo(std::ostream &out)
1470 #define WATER_DROP_BOOST 4
1472 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1474 DSTACK(__FUNCTION_NAME);
1475 //TimeTaker timer("transformLiquids()");
1478 u32 initial_size = m_transforming_liquid.size();
1480 /*if(initial_size != 0)
1481 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1483 while(m_transforming_liquid.size() != 0)
1486 Get a queued transforming liquid node
1488 v3s16 p0 = m_transforming_liquid.pop_front();
1490 MapNode n0 = getNode(p0);
1492 // Don't deal with non-liquids
1493 if(content_liquid(n0.d) == false)
1496 bool is_source = !content_flowing_liquid(n0.d);
1498 u8 liquid_level = 8;
1499 if(is_source == false)
1500 liquid_level = n0.param2 & 0x0f;
1502 // Turn possible source into non-source
1503 u8 nonsource_c = make_liquid_flowing(n0.d);
1506 If not source, check that some node flows into this one
1507 and what is the level of liquid in this one
1509 if(is_source == false)
1511 s8 new_liquid_level_max = -1;
1513 v3s16 dirs_from[5] = {
1514 v3s16(0,1,0), // top
1515 v3s16(0,0,1), // back
1516 v3s16(1,0,0), // right
1517 v3s16(0,0,-1), // front
1518 v3s16(-1,0,0), // left
1520 for(u16 i=0; i<5; i++)
1525 bool from_top = (i==0);
1527 v3s16 p2 = p0 + dirs_from[i];
1528 MapNode n2 = getNode(p2);
1530 if(content_liquid(n2.d))
1532 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1533 // Check that the liquids are the same type
1534 if(n2_nonsource_c != nonsource_c)
1536 dstream<<"WARNING: Not handling: different liquids"
1537 " collide"<<std::endl;
1540 bool n2_is_source = !content_flowing_liquid(n2.d);
1541 s8 n2_liquid_level = 8;
1542 if(n2_is_source == false)
1543 n2_liquid_level = n2.param2 & 0x07;
1545 s8 new_liquid_level = -1;
1548 //new_liquid_level = 7;
1549 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1550 new_liquid_level = 7;
1552 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1554 else if(n2_liquid_level > 0)
1556 new_liquid_level = n2_liquid_level - 1;
1559 if(new_liquid_level > new_liquid_level_max)
1560 new_liquid_level_max = new_liquid_level;
1563 }catch(InvalidPositionException &e)
1569 If liquid level should be something else, update it and
1570 add all the neighboring water nodes to the transform queue.
1572 if(new_liquid_level_max != liquid_level)
1574 if(new_liquid_level_max == -1)
1576 // Remove water alltoghether
1583 n0.param2 = new_liquid_level_max;
1587 // Block has been modified
1589 v3s16 blockpos = getNodeBlockPos(p0);
1590 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1592 modified_blocks.insert(blockpos, block);
1596 Add neighboring non-source liquid nodes to transform queue.
1599 v3s16(0,0,1), // back
1600 v3s16(0,1,0), // top
1601 v3s16(1,0,0), // right
1602 v3s16(0,0,-1), // front
1603 v3s16(0,-1,0), // bottom
1604 v3s16(-1,0,0), // left
1606 for(u16 i=0; i<6; i++)
1611 v3s16 p2 = p0 + dirs[i];
1613 MapNode n2 = getNode(p2);
1614 if(content_flowing_liquid(n2.d))
1616 m_transforming_liquid.push_back(p2);
1619 }catch(InvalidPositionException &e)
1626 // Get a new one from queue if the node has turned into non-water
1627 if(content_liquid(n0.d) == false)
1631 Flow water from this node
1633 v3s16 dirs_to[5] = {
1634 v3s16(0,-1,0), // bottom
1635 v3s16(0,0,1), // back
1636 v3s16(1,0,0), // right
1637 v3s16(0,0,-1), // front
1638 v3s16(-1,0,0), // left
1640 for(u16 i=0; i<5; i++)
1645 bool to_bottom = (i == 0);
1647 // If liquid is at lowest possible height, it's not going
1648 // anywhere except down
1649 if(liquid_level == 0 && to_bottom == false)
1652 u8 liquid_next_level = 0;
1653 // If going to bottom
1656 //liquid_next_level = 7;
1657 if(liquid_level >= 7 - WATER_DROP_BOOST)
1658 liquid_next_level = 7;
1660 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1663 liquid_next_level = liquid_level - 1;
1665 bool n2_changed = false;
1666 bool flowed = false;
1668 v3s16 p2 = p0 + dirs_to[i];
1670 MapNode n2 = getNode(p2);
1671 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1673 if(content_liquid(n2.d))
1675 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1676 // Check that the liquids are the same type
1677 if(n2_nonsource_c != nonsource_c)
1679 dstream<<"WARNING: Not handling: different liquids"
1680 " collide"<<std::endl;
1683 bool n2_is_source = !content_flowing_liquid(n2.d);
1684 u8 n2_liquid_level = 8;
1685 if(n2_is_source == false)
1686 n2_liquid_level = n2.param2 & 0x07;
1695 // Just flow into the source, nothing changes.
1696 // n2_changed is not set because destination didn't change
1701 if(liquid_next_level > liquid_level)
1703 n2.param2 = liquid_next_level;
1711 else if(n2.d == CONTENT_AIR)
1714 n2.param2 = liquid_next_level;
1721 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1725 m_transforming_liquid.push_back(p2);
1727 v3s16 blockpos = getNodeBlockPos(p2);
1728 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1730 modified_blocks.insert(blockpos, block);
1733 // If n2_changed to bottom, don't flow anywhere else
1734 if(to_bottom && flowed && !is_source)
1737 }catch(InvalidPositionException &e)
1743 //if(loopcount >= 100000)
1744 if(loopcount >= initial_size * 1)
1747 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1750 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1752 v3s16 blockpos = getNodeBlockPos(p);
1753 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1754 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1757 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1761 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1765 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1767 v3s16 blockpos = getNodeBlockPos(p);
1768 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1769 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1772 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1776 block->m_node_metadata.set(p_rel, meta);
1779 void Map::removeNodeMetadata(v3s16 p)
1781 v3s16 blockpos = getNodeBlockPos(p);
1782 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1783 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1786 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1790 block->m_node_metadata.remove(p_rel);
1793 void Map::nodeMetadataStep(float dtime,
1794 core::map<v3s16, MapBlock*> &changed_blocks)
1798 Currently there is no way to ensure that all the necessary
1799 blocks are loaded when this is run. (They might get unloaded)
1800 NOTE: ^- Actually, that might not be so. In a quick test it
1801 reloaded a block with a furnace when I walked back to it from
1804 core::map<v2s16, MapSector*>::Iterator si;
1805 si = m_sectors.getIterator();
1806 for(; si.atEnd() == false; si++)
1808 MapSector *sector = si.getNode()->getValue();
1809 core::list< MapBlock * > sectorblocks;
1810 sector->getBlocks(sectorblocks);
1811 core::list< MapBlock * >::Iterator i;
1812 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1814 MapBlock *block = *i;
1815 bool changed = block->m_node_metadata.step(dtime);
1817 changed_blocks[block->getPos()] = block;
1826 ServerMap::ServerMap(std::string savedir):
1829 m_map_metadata_changed(true)
1831 dstream<<__FUNCTION_NAME<<std::endl;
1833 //m_chunksize = 8; // Takes a few seconds
1835 m_seed = (((u64)(myrand()%0xffff)<<0)
1836 + ((u64)(myrand()%0xffff)<<16)
1837 + ((u64)(myrand()%0xffff)<<32)
1838 + ((u64)(myrand()%0xffff)<<48));
1841 Experimental and debug stuff
1848 Try to load map; if not found, create a new one.
1851 m_savedir = savedir;
1852 m_map_saving_enabled = false;
1856 // If directory exists, check contents and load if possible
1857 if(fs::PathExists(m_savedir))
1859 // If directory is empty, it is safe to save into it.
1860 if(fs::GetDirListing(m_savedir).size() == 0)
1862 dstream<<DTIME<<"Server: Empty save directory is valid."
1864 m_map_saving_enabled = true;
1869 // Load map metadata (seed, chunksize)
1872 catch(FileNotGoodException &e){
1873 dstream<<DTIME<<"WARNING: Could not load map metadata"
1874 //<<" Disabling chunk-based generator."
1880 // Load chunk metadata
1883 catch(FileNotGoodException &e){
1884 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1885 <<" Disabling chunk-based generator."
1890 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1891 "metadata and sector (0,0) from "<<savedir<<
1892 ", assuming valid save directory."
1895 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1896 <<"and chunk metadata from "<<savedir
1897 <<", assuming valid save directory."
1900 m_map_saving_enabled = true;
1901 // Map loaded, not creating new one
1905 // If directory doesn't exist, it is safe to save to it
1907 m_map_saving_enabled = true;
1910 catch(std::exception &e)
1912 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1913 <<", exception: "<<e.what()<<std::endl;
1914 dstream<<"Please remove the map or fix it."<<std::endl;
1915 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1918 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1920 // Create zero sector
1921 emergeSector(v2s16(0,0));
1923 // Initially write whole map
1927 ServerMap::~ServerMap()
1929 dstream<<__FUNCTION_NAME<<std::endl;
1933 if(m_map_saving_enabled)
1936 // Save only changed parts
1938 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1942 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1945 catch(std::exception &e)
1947 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1948 <<", exception: "<<e.what()<<std::endl;
1955 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1956 for(; i.atEnd() == false; i++)
1958 MapChunk *chunk = i.getNode()->getValue();
1964 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
1966 /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
1967 <<blockpos.Z<<")"<<std::endl;*/
1969 data->no_op = false;
1970 data->seed = m_seed;
1971 data->blockpos = blockpos;
1974 Create the whole area of this and the neighboring blocks
1977 //TimeTaker timer("initBlockMake() create area");
1979 for(s16 x=-1; x<=1; x++)
1980 for(s16 z=-1; z<=1; z++)
1982 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
1983 // Sector metadata is loaded from disk if not already loaded.
1984 ServerMapSector *sector = createSector(sectorpos);
1987 for(s16 y=-1; y<=1; y++)
1989 MapBlock *block = createBlock(blockpos);
1991 // Lighting won't be calculated
1992 block->setLightingExpired(true);
1993 // Lighting will be calculated
1994 //block->setLightingExpired(false);
1997 Block gets sunlight if this is true.
1999 This should be set to true when the top side of a block
2000 is completely exposed to the sky.
2002 block->setIsUnderground(false);
2008 Now we have a big empty area.
2010 Make a ManualMapVoxelManipulator that contains this and the
2014 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2015 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2017 data->vmanip = new ManualMapVoxelManipulator(this);
2018 //data->vmanip->setMap(this);
2022 //TimeTaker timer("initBlockMake() initialEmerge");
2023 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2026 // Data is ready now.
2029 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2030 core::map<v3s16, MapBlock*> &changed_blocks)
2032 v3s16 blockpos = data->blockpos;
2033 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2034 <<blockpos.Z<<")"<<std::endl;*/
2038 dstream<<"finishBlockMake(): no-op"<<std::endl;
2042 /*dstream<<"Resulting vmanip:"<<std::endl;
2043 data->vmanip.print(dstream);*/
2046 Blit generated stuff to map
2047 NOTE: blitBackAll adds nearly everything to changed_blocks
2051 //TimeTaker timer("finishBlockMake() blitBackAll");
2052 data->vmanip->blitBackAll(&changed_blocks);
2055 dstream<<"finishBlockMake: changed_blocks.size()="
2056 <<changed_blocks.size()<<std::endl;
2059 Copy transforming liquid information
2061 while(data->transforming_liquid.size() > 0)
2063 v3s16 p = data->transforming_liquid.pop_front();
2064 m_transforming_liquid.push_back(p);
2070 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2074 Set is_underground flag for lighting with sunlight
2077 block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2080 Add sunlight to central block.
2081 This makes in-dark-spawning monsters to not flood the whole thing.
2082 Do not spread the light, though.
2084 /*core::map<v3s16, bool> light_sources;
2085 bool black_air_left = false;
2086 block->propagateSunlight(light_sources, true, &black_air_left);*/
2089 NOTE: Lighting and object adding shouldn't really be here, but
2090 lighting is a bit tricky to move properly to makeBlock.
2091 TODO: Do this the right way anyway.
2098 core::map<v3s16, MapBlock*> lighting_update_blocks;
2100 lighting_update_blocks.insert(block->getPos(), block);
2102 // All modified blocks
2103 for(core::map<v3s16, MapBlock*>::Iterator
2104 i = changed_blocks.getIterator();
2105 i.atEnd() == false; i++)
2107 lighting_update_blocks.insert(i.getNode()->getKey(),
2108 i.getNode()->getValue());
2111 updateLighting(lighting_update_blocks, changed_blocks);
2114 Add random objects to block
2116 mapgen::add_random_objects(block);
2119 Go through changed blocks
2121 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2122 i.atEnd() == false; i++)
2124 MapBlock *block = i.getNode()->getValue();
2127 Update day/night difference cache of the MapBlocks
2129 block->updateDayNightDiff();
2131 Set block as modified
2133 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2137 Set central block as generated
2139 block->setGenerated(true);
2142 Save changed parts of map
2143 NOTE: Will be saved later.
2147 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2148 <<blockpos.Z<<")"<<std::endl;*/
2153 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2155 DSTACKF("%s: p2d=(%d,%d)",
2160 Check if it exists already in memory
2162 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2167 Try to load it from disk (with blocks)
2169 //if(loadSectorFull(p2d) == true)
2172 Try to load metadata from disk
2174 if(loadSectorMeta(p2d) == true)
2176 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2179 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2180 throw InvalidPositionException("");
2186 Do not create over-limit
2188 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2189 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2190 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2191 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2192 throw InvalidPositionException("createSector(): pos. over limit");
2195 Generate blank sector
2198 sector = new ServerMapSector(this, p2d);
2200 // Sector position on map in nodes
2201 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2206 m_sectors.insert(p2d, sector);
2212 This is a quick-hand function for calling makeBlock().
2214 MapBlock * ServerMap::generateBlock(
2216 core::map<v3s16, MapBlock*> &modified_blocks
2219 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2221 /*dstream<<"generateBlock(): "
2222 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2225 TimeTaker timer("generateBlock");
2227 //MapBlock *block = original_dummy;
2229 v2s16 p2d(p.X, p.Z);
2230 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2233 Do not generate over-limit
2235 if(blockpos_over_limit(p))
2237 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2238 throw InvalidPositionException("generateBlock(): pos. over limit");
2242 Create block make data
2244 mapgen::BlockMakeData data;
2245 initBlockMake(&data, p);
2251 TimeTaker t("mapgen::make_block()");
2252 mapgen::make_block(&data);
2256 Blit data back on map, update lighting, add mobs and whatever this does
2258 finishBlockMake(&data, modified_blocks);
2263 MapBlock *block = getBlockNoCreateNoEx(p);
2270 bool erroneus_content = false;
2271 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2272 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2273 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2276 MapNode n = block->getNode(p);
2277 if(n.d == CONTENT_IGNORE)
2279 dstream<<"CONTENT_IGNORE at "
2280 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2282 erroneus_content = true;
2286 if(erroneus_content)
2294 Generate a completely empty block
2296 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2297 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2299 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2305 n.d = CONTENT_STONE;
2306 block->setNode(v3s16(x0,y0,z0), n);
2314 MapBlock * ServerMap::createBlock(v3s16 p)
2316 DSTACKF("%s: p=(%d,%d,%d)",
2317 __FUNCTION_NAME, p.X, p.Y, p.Z);
2320 Do not create over-limit
2322 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2323 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2324 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2325 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2326 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2327 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2328 throw InvalidPositionException("createBlock(): pos. over limit");
2330 v2s16 p2d(p.X, p.Z);
2333 This will create or load a sector if not found in memory.
2334 If block exists on disk, it will be loaded.
2336 NOTE: On old save formats, this will be slow, as it generates
2337 lighting on blocks for them.
2339 ServerMapSector *sector;
2341 sector = (ServerMapSector*)createSector(p2d);
2342 assert(sector->getId() == MAPSECTOR_SERVER);
2344 catch(InvalidPositionException &e)
2346 dstream<<"createBlock: createSector() failed"<<std::endl;
2350 NOTE: This should not be done, or at least the exception
2351 should not be passed on as std::exception, because it
2352 won't be catched at all.
2354 /*catch(std::exception &e)
2356 dstream<<"createBlock: createSector() failed: "
2357 <<e.what()<<std::endl;
2362 Try to get a block from the sector
2365 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2368 if(block->isDummy())
2373 block = sector->createBlankBlock(block_y);
2378 MapBlock * ServerMap::emergeBlock(
2380 bool only_from_disk,
2381 core::map<v3s16, MapBlock*> &changed_blocks,
2382 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2385 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
2387 p.X, p.Y, p.Z, only_from_disk);
2389 // This has to be redone or removed
2397 Do not generate over-limit
2399 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2400 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2401 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2402 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2403 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2404 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2405 throw InvalidPositionException("emergeBlock(): pos. over limit");
2407 v2s16 p2d(p.X, p.Z);
2410 This will create or load a sector if not found in memory.
2411 If block exists on disk, it will be loaded.
2413 ServerMapSector *sector;
2415 sector = createSector(p2d);
2416 //sector = emergeSector(p2d, changed_blocks);
2418 catch(InvalidPositionException &e)
2420 dstream<<"emergeBlock: createSector() failed: "
2421 <<e.what()<<std::endl;
2422 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2424 <<"You could try to delete it."<<std::endl;
2427 catch(VersionMismatchException &e)
2429 dstream<<"emergeBlock: createSector() failed: "
2430 <<e.what()<<std::endl;
2431 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2433 <<"You could try to delete it."<<std::endl;
2438 Try to get a block from the sector
2441 bool does_not_exist = false;
2442 bool lighting_expired = false;
2443 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2445 // If not found, try loading from disk
2448 block = loadBlock(p);
2454 does_not_exist = true;
2456 else if(block->isDummy() == true)
2458 does_not_exist = true;
2460 else if(block->getLightingExpired())
2462 lighting_expired = true;
2467 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2472 If block was not found on disk and not going to generate a
2473 new one, make sure there is a dummy block in place.
2475 if(only_from_disk && (does_not_exist || lighting_expired))
2477 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2481 // Create dummy block
2482 block = new MapBlock(this, p, true);
2484 // Add block to sector
2485 sector->insertBlock(block);
2491 //dstream<<"Not found on disk, generating."<<std::endl;
2493 //TimeTaker("emergeBlock() generate");
2495 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2498 If the block doesn't exist, generate the block.
2502 block = generateBlock(p, block, sector, changed_blocks,
2503 lighting_invalidated_blocks);
2506 if(lighting_expired)
2508 lighting_invalidated_blocks.insert(p, block);
2513 Initially update sunlight
2516 core::map<v3s16, bool> light_sources;
2517 bool black_air_left = false;
2518 bool bottom_invalid =
2519 block->propagateSunlight(light_sources, true,
2522 // If sunlight didn't reach everywhere and part of block is
2523 // above ground, lighting has to be properly updated
2524 //if(black_air_left && some_part_underground)
2527 lighting_invalidated_blocks[block->getPos()] = block;
2532 lighting_invalidated_blocks[block->getPos()] = block;
2541 s16 ServerMap::findGroundLevel(v2s16 p2d)
2545 Uh, just do something random...
2547 // Find existing map from top to down
2550 v3s16 p(p2d.X, max, p2d.Y);
2551 for(; p.Y>min; p.Y--)
2553 MapNode n = getNodeNoEx(p);
2554 if(n.d != CONTENT_IGNORE)
2559 // If this node is not air, go to plan b
2560 if(getNodeNoEx(p).d != CONTENT_AIR)
2562 // Search existing walkable and return it
2563 for(; p.Y>min; p.Y--)
2565 MapNode n = getNodeNoEx(p);
2566 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
2575 Determine from map generator noise functions
2578 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2581 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2582 //return (s16)level;
2585 void ServerMap::createDirs(std::string path)
2587 if(fs::CreateAllDirs(path) == false)
2589 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2590 <<"\""<<path<<"\""<<std::endl;
2591 throw BaseException("ServerMap failed to create directory");
2595 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2601 snprintf(cc, 9, "%.4x%.4x",
2602 (unsigned int)pos.X&0xffff,
2603 (unsigned int)pos.Y&0xffff);
2605 return m_savedir + "/sectors/" + cc;
2607 snprintf(cc, 9, "%.3x/%.3x",
2608 (unsigned int)pos.X&0xfff,
2609 (unsigned int)pos.Y&0xfff);
2611 return m_savedir + "/sectors2/" + cc;
2617 v2s16 ServerMap::getSectorPos(std::string dirname)
2621 size_t spos = dirname.rfind('/') + 1;
2622 assert(spos != std::string::npos);
2623 if(dirname.size() - spos == 8)
2626 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2628 else if(dirname.size() - spos == 3)
2631 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2632 // Sign-extend the 12 bit values up to 16 bits...
2633 if(x&0x800) x|=0xF000;
2634 if(y&0x800) y|=0xF000;
2641 v2s16 pos((s16)x, (s16)y);
2645 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2647 v2s16 p2d = getSectorPos(sectordir);
2649 if(blockfile.size() != 4){
2650 throw InvalidFilenameException("Invalid block filename");
2653 int r = sscanf(blockfile.c_str(), "%4x", &y);
2655 throw InvalidFilenameException("Invalid block filename");
2656 return v3s16(p2d.X, y, p2d.Y);
2659 std::string ServerMap::getBlockFilename(v3s16 p)
2662 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2666 void ServerMap::save(bool only_changed)
2668 DSTACK(__FUNCTION_NAME);
2669 if(m_map_saving_enabled == false)
2671 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2675 if(only_changed == false)
2676 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2679 if(only_changed == false || m_map_metadata_changed)
2684 u32 sector_meta_count = 0;
2685 u32 block_count = 0;
2686 u32 block_count_all = 0; // Number of blocks in memory
2688 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2689 for(; i.atEnd() == false; i++)
2691 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2692 assert(sector->getId() == MAPSECTOR_SERVER);
2694 if(sector->differs_from_disk || only_changed == false)
2696 saveSectorMeta(sector);
2697 sector_meta_count++;
2699 core::list<MapBlock*> blocks;
2700 sector->getBlocks(blocks);
2701 core::list<MapBlock*>::Iterator j;
2702 for(j=blocks.begin(); j!=blocks.end(); j++)
2704 MapBlock *block = *j;
2708 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2709 || only_changed == false)
2714 /*dstream<<"ServerMap: Written block ("
2715 <<block->getPos().X<<","
2716 <<block->getPos().Y<<","
2717 <<block->getPos().Z<<")"
2724 Only print if something happened or saved whole map
2726 if(only_changed == false || sector_meta_count != 0
2727 || block_count != 0)
2729 dstream<<DTIME<<"ServerMap: Written: "
2730 <<sector_meta_count<<" sector metadata files, "
2731 <<block_count<<" block files"
2732 <<", "<<block_count_all<<" blocks in memory."
2737 void ServerMap::saveMapMeta()
2739 DSTACK(__FUNCTION_NAME);
2741 dstream<<"INFO: ServerMap::saveMapMeta(): "
2745 createDirs(m_savedir);
2747 std::string fullpath = m_savedir + "/map_meta.txt";
2748 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2749 if(os.good() == false)
2751 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2752 <<"could not open"<<fullpath<<std::endl;
2753 throw FileNotGoodException("Cannot open chunk metadata");
2757 params.setU64("seed", m_seed);
2759 params.writeLines(os);
2761 os<<"[end_of_params]\n";
2763 m_map_metadata_changed = false;
2766 void ServerMap::loadMapMeta()
2768 DSTACK(__FUNCTION_NAME);
2770 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2773 std::string fullpath = m_savedir + "/map_meta.txt";
2774 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2775 if(is.good() == false)
2777 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2778 <<"could not open"<<fullpath<<std::endl;
2779 throw FileNotGoodException("Cannot open map metadata");
2787 throw SerializationError
2788 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2790 std::getline(is, line);
2791 std::string trimmedline = trim(line);
2792 if(trimmedline == "[end_of_params]")
2794 params.parseConfigLine(line);
2797 m_seed = params.getU64("seed");
2799 dstream<<"INFO: ServerMap::loadMapMeta(): "
2804 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2806 DSTACK(__FUNCTION_NAME);
2807 // Format used for writing
2808 u8 version = SER_FMT_VER_HIGHEST;
2810 v2s16 pos = sector->getPos();
2811 std::string dir = getSectorDir(pos);
2814 std::string fullpath = dir + "/meta";
2815 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2816 if(o.good() == false)
2817 throw FileNotGoodException("Cannot open sector metafile");
2819 sector->serialize(o, version);
2821 sector->differs_from_disk = false;
2824 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2826 DSTACK(__FUNCTION_NAME);
2828 v2s16 p2d = getSectorPos(sectordir);
2830 ServerMapSector *sector = NULL;
2832 std::string fullpath = sectordir + "/meta";
2833 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2834 if(is.good() == false)
2836 // If the directory exists anyway, it probably is in some old
2837 // format. Just go ahead and create the sector.
2838 if(fs::PathExists(sectordir))
2840 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
2841 <<fullpath<<" doesn't exist but directory does."
2842 <<" Continuing with a sector with no metadata."
2844 sector = new ServerMapSector(this, p2d);
2845 m_sectors.insert(p2d, sector);
2849 throw FileNotGoodException("Cannot open sector metafile");
2854 sector = ServerMapSector::deSerialize
2855 (is, this, p2d, m_sectors);
2857 saveSectorMeta(sector);
2860 sector->differs_from_disk = false;
2865 bool ServerMap::loadSectorMeta(v2s16 p2d)
2867 DSTACK(__FUNCTION_NAME);
2869 MapSector *sector = NULL;
2871 // The directory layout we're going to load from.
2872 // 1 - original sectors/xxxxzzzz/
2873 // 2 - new sectors2/xxx/zzz/
2874 // If we load from anything but the latest structure, we will
2875 // immediately save to the new one, and remove the old.
2877 std::string sectordir1 = getSectorDir(p2d, 1);
2878 std::string sectordir;
2879 if(fs::PathExists(sectordir1))
2881 sectordir = sectordir1;
2886 sectordir = getSectorDir(p2d, 2);
2890 sector = loadSectorMeta(sectordir, loadlayout != 2);
2892 catch(InvalidFilenameException &e)
2896 catch(FileNotGoodException &e)
2900 catch(std::exception &e)
2909 bool ServerMap::loadSectorFull(v2s16 p2d)
2911 DSTACK(__FUNCTION_NAME);
2913 MapSector *sector = NULL;
2915 // The directory layout we're going to load from.
2916 // 1 - original sectors/xxxxzzzz/
2917 // 2 - new sectors2/xxx/zzz/
2918 // If we load from anything but the latest structure, we will
2919 // immediately save to the new one, and remove the old.
2921 std::string sectordir1 = getSectorDir(p2d, 1);
2922 std::string sectordir;
2923 if(fs::PathExists(sectordir1))
2925 sectordir = sectordir1;
2930 sectordir = getSectorDir(p2d, 2);
2934 sector = loadSectorMeta(sectordir, loadlayout != 2);
2936 catch(InvalidFilenameException &e)
2940 catch(FileNotGoodException &e)
2944 catch(std::exception &e)
2952 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2954 std::vector<fs::DirListNode>::iterator i2;
2955 for(i2=list2.begin(); i2!=list2.end(); i2++)
2961 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
2963 catch(InvalidFilenameException &e)
2965 // This catches unknown crap in directory
2971 dstream<<"Sector converted to new layout - deleting "<<
2972 sectordir1<<std::endl;
2973 fs::RecursiveDelete(sectordir1);
2980 void ServerMap::saveBlock(MapBlock *block)
2982 DSTACK(__FUNCTION_NAME);
2984 Dummy blocks are not written
2986 if(block->isDummy())
2988 /*v3s16 p = block->getPos();
2989 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2990 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2994 // Format used for writing
2995 u8 version = SER_FMT_VER_HIGHEST;
2997 v3s16 p3d = block->getPos();
2999 v2s16 p2d(p3d.X, p3d.Z);
3000 std::string sectordir = getSectorDir(p2d);
3002 createDirs(sectordir);
3004 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3005 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3006 if(o.good() == false)
3007 throw FileNotGoodException("Cannot open block data");
3010 [0] u8 serialization version
3013 o.write((char*)&version, 1);
3016 block->serialize(o, version);
3018 // Write extra data stored on disk
3019 block->serializeDiskExtra(o, version);
3021 // We just wrote it to the disk so clear modified flag
3022 block->resetModified();
3025 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3027 DSTACK(__FUNCTION_NAME);
3029 std::string fullpath = sectordir+"/"+blockfile;
3032 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3033 if(is.good() == false)
3034 throw FileNotGoodException("Cannot open block file");
3036 v3s16 p3d = getBlockPos(sectordir, blockfile);
3037 v2s16 p2d(p3d.X, p3d.Z);
3039 assert(sector->getPos() == p2d);
3041 u8 version = SER_FMT_VER_INVALID;
3042 is.read((char*)&version, 1);
3045 throw SerializationError("ServerMap::loadBlock(): Failed"
3046 " to read MapBlock version");
3048 /*u32 block_size = MapBlock::serializedLength(version);
3049 SharedBuffer<u8> data(block_size);
3050 is.read((char*)*data, block_size);*/
3052 // This will always return a sector because we're the server
3053 //MapSector *sector = emergeSector(p2d);
3055 MapBlock *block = NULL;
3056 bool created_new = false;
3057 block = sector->getBlockNoCreateNoEx(p3d.Y);
3060 block = sector->createBlankBlockNoInsert(p3d.Y);
3065 block->deSerialize(is, version);
3067 // Read extra data stored on disk
3068 block->deSerializeDiskExtra(is, version);
3070 // If it's a new block, insert it to the map
3072 sector->insertBlock(block);
3075 Save blocks loaded in old format in new format
3078 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3083 // We just loaded it from the disk, so it's up-to-date.
3084 block->resetModified();
3087 catch(SerializationError &e)
3089 dstream<<"WARNING: Invalid block data on disk "
3090 <<"fullpath="<<fullpath
3091 <<" (SerializationError). "
3092 <<"what()="<<e.what()
3094 //" Ignoring. A new one will be generated.
3097 // TODO: Backup file; name is in fullpath.
3101 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3103 DSTACK(__FUNCTION_NAME);
3105 v2s16 p2d(blockpos.X, blockpos.Z);
3107 // The directory layout we're going to load from.
3108 // 1 - original sectors/xxxxzzzz/
3109 // 2 - new sectors2/xxx/zzz/
3110 // If we load from anything but the latest structure, we will
3111 // immediately save to the new one, and remove the old.
3113 std::string sectordir1 = getSectorDir(p2d, 1);
3114 std::string sectordir;
3115 if(fs::PathExists(sectordir1))
3117 sectordir = sectordir1;
3122 sectordir = getSectorDir(p2d, 2);
3126 Make sure sector is loaded
3128 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3132 sector = loadSectorMeta(sectordir, loadlayout != 2);
3134 catch(InvalidFilenameException &e)
3138 catch(FileNotGoodException &e)
3142 catch(std::exception &e)
3149 Make sure file exists
3152 std::string blockfilename = getBlockFilename(blockpos);
3153 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3159 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3160 return getBlockNoCreateNoEx(blockpos);
3163 void ServerMap::PrintInfo(std::ostream &out)
3174 ClientMap::ClientMap(
3176 MapDrawControl &control,
3177 scene::ISceneNode* parent,
3178 scene::ISceneManager* mgr,
3182 scene::ISceneNode(parent, mgr, id),
3185 m_camera_position(0,0,0),
3186 m_camera_direction(0,0,1)
3188 m_camera_mutex.Init();
3189 assert(m_camera_mutex.IsInitialized());
3191 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3192 BS*1000000,BS*1000000,BS*1000000);
3195 ClientMap::~ClientMap()
3197 /*JMutexAutoLock lock(mesh_mutex);
3206 MapSector * ClientMap::emergeSector(v2s16 p2d)
3208 DSTACK(__FUNCTION_NAME);
3209 // Check that it doesn't exist already
3211 return getSectorNoGenerate(p2d);
3213 catch(InvalidPositionException &e)
3218 ClientMapSector *sector = new ClientMapSector(this, p2d);
3221 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3222 m_sectors.insert(p2d, sector);
3229 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3231 DSTACK(__FUNCTION_NAME);
3232 ClientMapSector *sector = NULL;
3234 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3236 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3240 sector = (ClientMapSector*)n->getValue();
3241 assert(sector->getId() == MAPSECTOR_CLIENT);
3245 sector = new ClientMapSector(this, p2d);
3247 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3248 m_sectors.insert(p2d, sector);
3252 sector->deSerialize(is);
3256 void ClientMap::OnRegisterSceneNode()
3260 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3261 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3264 ISceneNode::OnRegisterSceneNode();
3267 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3269 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3270 DSTACK(__FUNCTION_NAME);
3272 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3275 This is called two times per frame, reset on the non-transparent one
3277 if(pass == scene::ESNRP_SOLID)
3279 m_last_drawn_sectors.clear();
3283 Get time for measuring timeout.
3285 Measuring time is very useful for long delays when the
3286 machine is swapping a lot.
3288 int time1 = time(0);
3290 //u32 daynight_ratio = m_client->getDayNightRatio();
3292 m_camera_mutex.Lock();
3293 v3f camera_position = m_camera_position;
3294 v3f camera_direction = m_camera_direction;
3295 m_camera_mutex.Unlock();
3298 Get all blocks and draw all visible ones
3301 v3s16 cam_pos_nodes(
3302 camera_position.X / BS,
3303 camera_position.Y / BS,
3304 camera_position.Z / BS);
3306 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3308 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3309 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3311 // Take a fair amount as we will be dropping more out later
3313 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3314 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3315 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3317 p_nodes_max.X / MAP_BLOCKSIZE,
3318 p_nodes_max.Y / MAP_BLOCKSIZE,
3319 p_nodes_max.Z / MAP_BLOCKSIZE);
3321 u32 vertex_count = 0;
3323 // For limiting number of mesh updates per frame
3324 u32 mesh_update_count = 0;
3326 u32 blocks_would_have_drawn = 0;
3327 u32 blocks_drawn = 0;
3329 int timecheck_counter = 0;
3330 core::map<v2s16, MapSector*>::Iterator si;
3331 si = m_sectors.getIterator();
3332 for(; si.atEnd() == false; si++)
3335 timecheck_counter++;
3336 if(timecheck_counter > 50)
3338 timecheck_counter = 0;
3339 int time2 = time(0);
3340 if(time2 > time1 + 4)
3342 dstream<<"ClientMap::renderMap(): "
3343 "Rendering takes ages, returning."
3350 MapSector *sector = si.getNode()->getValue();
3351 v2s16 sp = sector->getPos();
3353 if(m_control.range_all == false)
3355 if(sp.X < p_blocks_min.X
3356 || sp.X > p_blocks_max.X
3357 || sp.Y < p_blocks_min.Z
3358 || sp.Y > p_blocks_max.Z)
3362 core::list< MapBlock * > sectorblocks;
3363 sector->getBlocks(sectorblocks);
3369 u32 sector_blocks_drawn = 0;
3371 core::list< MapBlock * >::Iterator i;
3372 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3374 MapBlock *block = *i;
3377 Compare block position to camera position, skip
3378 if not seen on display
3381 float range = 100000 * BS;
3382 if(m_control.range_all == false)
3383 range = m_control.wanted_range * BS;
3386 if(isBlockInSight(block->getPos(), camera_position,
3387 camera_direction, range, &d) == false)
3392 // This is ugly (spherical distance limit?)
3393 /*if(m_control.range_all == false &&
3394 d - 0.5*BS*MAP_BLOCKSIZE > range)
3399 Update expired mesh (used for day/night change)
3401 It doesn't work exactly like it should now with the
3402 tasked mesh update but whatever.
3405 bool mesh_expired = false;
3408 JMutexAutoLock lock(block->mesh_mutex);
3410 mesh_expired = block->getMeshExpired();
3412 // Mesh has not been expired and there is no mesh:
3413 // block has no content
3414 if(block->mesh == NULL && mesh_expired == false)
3418 f32 faraway = BS*50;
3419 //f32 faraway = m_control.wanted_range * BS;
3422 This has to be done with the mesh_mutex unlocked
3424 // Pretty random but this should work somewhat nicely
3425 if(mesh_expired && (
3426 (mesh_update_count < 3
3427 && (d < faraway || mesh_update_count < 2)
3430 (m_control.range_all && mesh_update_count < 20)
3433 /*if(mesh_expired && mesh_update_count < 6
3434 && (d < faraway || mesh_update_count < 3))*/
3436 mesh_update_count++;
3438 // Mesh has been expired: generate new mesh
3439 //block->updateMesh(daynight_ratio);
3440 m_client->addUpdateMeshTask(block->getPos());
3442 mesh_expired = false;
3447 Draw the faces of the block
3450 JMutexAutoLock lock(block->mesh_mutex);
3452 scene::SMesh *mesh = block->mesh;
3457 blocks_would_have_drawn++;
3458 if(blocks_drawn >= m_control.wanted_max_blocks
3459 && m_control.range_all == false
3460 && d > m_control.wanted_min_range * BS)
3464 sector_blocks_drawn++;
3466 u32 c = mesh->getMeshBufferCount();
3468 for(u32 i=0; i<c; i++)
3470 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3471 const video::SMaterial& material = buf->getMaterial();
3472 video::IMaterialRenderer* rnd =
3473 driver->getMaterialRenderer(material.MaterialType);
3474 bool transparent = (rnd && rnd->isTransparent());
3475 // Render transparent on transparent pass and likewise.
3476 if(transparent == is_transparent_pass)
3479 This *shouldn't* hurt too much because Irrlicht
3480 doesn't change opengl textures if the old
3481 material is set again.
3483 driver->setMaterial(buf->getMaterial());
3484 driver->drawMeshBuffer(buf);
3485 vertex_count += buf->getVertexCount();
3489 } // foreach sectorblocks
3491 if(sector_blocks_drawn != 0)
3493 m_last_drawn_sectors[sp] = true;
3497 m_control.blocks_drawn = blocks_drawn;
3498 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3500 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3501 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3504 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3505 core::map<v3s16, MapBlock*> *affected_blocks)
3507 bool changed = false;
3509 Add it to all blocks touching it
3512 v3s16(0,0,0), // this
3513 v3s16(0,0,1), // back
3514 v3s16(0,1,0), // top
3515 v3s16(1,0,0), // right
3516 v3s16(0,0,-1), // front
3517 v3s16(0,-1,0), // bottom
3518 v3s16(-1,0,0), // left
3520 for(u16 i=0; i<7; i++)
3522 v3s16 p2 = p + dirs[i];
3523 // Block position of neighbor (or requested) node
3524 v3s16 blockpos = getNodeBlockPos(p2);
3525 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3526 if(blockref == NULL)
3528 // Relative position of requested node
3529 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3530 if(blockref->setTempMod(relpos, mod))
3535 if(changed && affected_blocks!=NULL)
3537 for(u16 i=0; i<7; i++)
3539 v3s16 p2 = p + dirs[i];
3540 // Block position of neighbor (or requested) node
3541 v3s16 blockpos = getNodeBlockPos(p2);
3542 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3543 if(blockref == NULL)
3545 affected_blocks->insert(blockpos, blockref);
3551 bool ClientMap::clearTempMod(v3s16 p,
3552 core::map<v3s16, MapBlock*> *affected_blocks)
3554 bool changed = false;
3556 v3s16(0,0,0), // this
3557 v3s16(0,0,1), // back
3558 v3s16(0,1,0), // top
3559 v3s16(1,0,0), // right
3560 v3s16(0,0,-1), // front
3561 v3s16(0,-1,0), // bottom
3562 v3s16(-1,0,0), // left
3564 for(u16 i=0; i<7; i++)
3566 v3s16 p2 = p + dirs[i];
3567 // Block position of neighbor (or requested) node
3568 v3s16 blockpos = getNodeBlockPos(p2);
3569 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3570 if(blockref == NULL)
3572 // Relative position of requested node
3573 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3574 if(blockref->clearTempMod(relpos))
3579 if(changed && affected_blocks!=NULL)
3581 for(u16 i=0; i<7; i++)
3583 v3s16 p2 = p + dirs[i];
3584 // Block position of neighbor (or requested) node
3585 v3s16 blockpos = getNodeBlockPos(p2);
3586 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3587 if(blockref == NULL)
3589 affected_blocks->insert(blockpos, blockref);
3595 void ClientMap::expireMeshes(bool only_daynight_diffed)
3597 TimeTaker timer("expireMeshes()");
3599 core::map<v2s16, MapSector*>::Iterator si;
3600 si = m_sectors.getIterator();
3601 for(; si.atEnd() == false; si++)
3603 MapSector *sector = si.getNode()->getValue();
3605 core::list< MapBlock * > sectorblocks;
3606 sector->getBlocks(sectorblocks);
3608 core::list< MapBlock * >::Iterator i;
3609 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3611 MapBlock *block = *i;
3613 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3619 JMutexAutoLock lock(block->mesh_mutex);
3620 if(block->mesh != NULL)
3622 /*block->mesh->drop();
3623 block->mesh = NULL;*/
3624 block->setMeshExpired(true);
3631 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3633 assert(mapType() == MAPTYPE_CLIENT);
3636 v3s16 p = blockpos + v3s16(0,0,0);
3637 MapBlock *b = getBlockNoCreate(p);
3638 b->updateMesh(daynight_ratio);
3639 //b->setMeshExpired(true);
3641 catch(InvalidPositionException &e){}
3644 v3s16 p = blockpos + v3s16(-1,0,0);
3645 MapBlock *b = getBlockNoCreate(p);
3646 b->updateMesh(daynight_ratio);
3647 //b->setMeshExpired(true);
3649 catch(InvalidPositionException &e){}
3651 v3s16 p = blockpos + v3s16(0,-1,0);
3652 MapBlock *b = getBlockNoCreate(p);
3653 b->updateMesh(daynight_ratio);
3654 //b->setMeshExpired(true);
3656 catch(InvalidPositionException &e){}
3658 v3s16 p = blockpos + v3s16(0,0,-1);
3659 MapBlock *b = getBlockNoCreate(p);
3660 b->updateMesh(daynight_ratio);
3661 //b->setMeshExpired(true);
3663 catch(InvalidPositionException &e){}
3668 Update mesh of block in which the node is, and if the node is at the
3669 leading edge, update the appropriate leading blocks too.
3671 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3679 v3s16 blockposes[4];
3680 for(u32 i=0; i<4; i++)
3682 v3s16 np = nodepos + dirs[i];
3683 blockposes[i] = getNodeBlockPos(np);
3684 // Don't update mesh of block if it has been done already
3685 bool already_updated = false;
3686 for(u32 j=0; j<i; j++)
3688 if(blockposes[j] == blockposes[i])
3690 already_updated = true;
3697 MapBlock *b = getBlockNoCreate(blockposes[i]);
3698 b->updateMesh(daynight_ratio);
3703 void ClientMap::PrintInfo(std::ostream &out)
3714 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3719 MapVoxelManipulator::~MapVoxelManipulator()
3721 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3725 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3727 TimeTaker timer1("emerge", &emerge_time);
3729 // Units of these are MapBlocks
3730 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3731 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3733 VoxelArea block_area_nodes
3734 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3736 addArea(block_area_nodes);
3738 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3739 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3740 for(s32 x=p_min.X; x<=p_max.X; x++)
3743 core::map<v3s16, bool>::Node *n;
3744 n = m_loaded_blocks.find(p);
3748 bool block_data_inexistent = false;
3751 TimeTaker timer1("emerge load", &emerge_load_time);
3753 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3754 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3757 dstream<<std::endl;*/
3759 MapBlock *block = m_map->getBlockNoCreate(p);
3760 if(block->isDummy())
3761 block_data_inexistent = true;
3763 block->copyTo(*this);
3765 catch(InvalidPositionException &e)
3767 block_data_inexistent = true;
3770 if(block_data_inexistent)
3772 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3773 // Fill with VOXELFLAG_INEXISTENT
3774 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3775 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3777 s32 i = m_area.index(a.MinEdge.X,y,z);
3778 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3782 m_loaded_blocks.insert(p, !block_data_inexistent);
3785 //dstream<<"emerge done"<<std::endl;
3789 SUGG: Add an option to only update eg. water and air nodes.
3790 This will make it interfere less with important stuff if
3793 void MapVoxelManipulator::blitBack
3794 (core::map<v3s16, MapBlock*> & modified_blocks)
3796 if(m_area.getExtent() == v3s16(0,0,0))
3799 //TimeTaker timer1("blitBack");
3801 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3802 <<m_loaded_blocks.size()<<std::endl;*/
3805 Initialize block cache
3807 v3s16 blockpos_last;
3808 MapBlock *block = NULL;
3809 bool block_checked_in_modified = false;
3811 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3812 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3813 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3817 u8 f = m_flags[m_area.index(p)];
3818 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3821 MapNode &n = m_data[m_area.index(p)];
3823 v3s16 blockpos = getNodeBlockPos(p);
3828 if(block == NULL || blockpos != blockpos_last){
3829 block = m_map->getBlockNoCreate(blockpos);
3830 blockpos_last = blockpos;
3831 block_checked_in_modified = false;
3834 // Calculate relative position in block
3835 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3837 // Don't continue if nothing has changed here
3838 if(block->getNode(relpos) == n)
3841 //m_map->setNode(m_area.MinEdge + p, n);
3842 block->setNode(relpos, n);
3845 Make sure block is in modified_blocks
3847 if(block_checked_in_modified == false)
3849 modified_blocks[blockpos] = block;
3850 block_checked_in_modified = true;
3853 catch(InvalidPositionException &e)
3859 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3860 MapVoxelManipulator(map),
3861 m_create_area(false)
3865 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3869 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3871 // Just create the area so that it can be pointed to
3872 VoxelManipulator::emerge(a, caller_id);
3875 void ManualMapVoxelManipulator::initialEmerge(
3876 v3s16 blockpos_min, v3s16 blockpos_max)
3878 TimeTaker timer1("initialEmerge", &emerge_time);
3880 // Units of these are MapBlocks
3881 v3s16 p_min = blockpos_min;
3882 v3s16 p_max = blockpos_max;
3884 VoxelArea block_area_nodes
3885 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3887 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3890 dstream<<"initialEmerge: area: ";
3891 block_area_nodes.print(dstream);
3892 dstream<<" ("<<size_MB<<"MB)";
3896 addArea(block_area_nodes);
3898 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3899 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3900 for(s32 x=p_min.X; x<=p_max.X; x++)
3903 core::map<v3s16, bool>::Node *n;
3904 n = m_loaded_blocks.find(p);
3908 bool block_data_inexistent = false;
3911 TimeTaker timer1("emerge load", &emerge_load_time);
3913 MapBlock *block = m_map->getBlockNoCreate(p);
3914 if(block->isDummy())
3915 block_data_inexistent = true;
3917 block->copyTo(*this);
3919 catch(InvalidPositionException &e)
3921 block_data_inexistent = true;
3924 if(block_data_inexistent)
3927 Mark area inexistent
3929 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3930 // Fill with VOXELFLAG_INEXISTENT
3931 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3932 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3934 s32 i = m_area.index(a.MinEdge.X,y,z);
3935 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3939 m_loaded_blocks.insert(p, !block_data_inexistent);
3943 void ManualMapVoxelManipulator::blitBackAll(
3944 core::map<v3s16, MapBlock*> * modified_blocks)
3946 if(m_area.getExtent() == v3s16(0,0,0))
3950 Copy data of all blocks
3952 for(core::map<v3s16, bool>::Iterator
3953 i = m_loaded_blocks.getIterator();
3954 i.atEnd() == false; i++)
3956 bool existed = i.getNode()->getValue();
3957 if(existed == false)
3959 v3s16 p = i.getNode()->getKey();
3960 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3963 dstream<<"WARNING: "<<__FUNCTION_NAME
3964 <<": got NULL block "
3965 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3970 block->copyFrom(*this);
3973 modified_blocks->insert(p, block);