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.
22 #include "jmutexautolock.h"
30 #include "serverobject.h"
31 #include "content_mapnode.h"
38 SQLite format specification:
39 - Initially only replaces sectors/ and sectors2/
46 Map::Map(std::ostream &dout):
50 /*m_sector_mutex.Init();
51 assert(m_sector_mutex.IsInitialized());*/
59 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
60 for(; i.atEnd() == false; i++)
62 MapSector *sector = i.getNode()->getValue();
67 void Map::addEventReceiver(MapEventReceiver *event_receiver)
69 m_event_receivers.insert(event_receiver, false);
72 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
74 if(m_event_receivers.find(event_receiver) == NULL)
76 m_event_receivers.remove(event_receiver);
79 void Map::dispatchEvent(MapEditEvent *event)
81 for(core::map<MapEventReceiver*, bool>::Iterator
82 i = m_event_receivers.getIterator();
83 i.atEnd()==false; i++)
85 MapEventReceiver* event_receiver = i.getNode()->getKey();
86 event_receiver->onMapEditEvent(event);
90 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
92 if(m_sector_cache != NULL && p == m_sector_cache_p){
93 MapSector * sector = m_sector_cache;
97 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
102 MapSector *sector = n->getValue();
104 // Cache the last result
105 m_sector_cache_p = p;
106 m_sector_cache = sector;
111 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
113 return getSectorNoGenerateNoExNoLock(p);
116 MapSector * Map::getSectorNoGenerate(v2s16 p)
118 MapSector *sector = getSectorNoGenerateNoEx(p);
120 throw InvalidPositionException();
125 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
127 v2s16 p2d(p3d.X, p3d.Z);
128 MapSector * sector = getSectorNoGenerate(p2d);
130 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
135 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
139 v2s16 p2d(p3d.X, p3d.Z);
140 MapSector * sector = getSectorNoGenerate(p2d);
141 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
144 catch(InvalidPositionException &e)
150 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
152 v2s16 p2d(p3d.X, p3d.Z);
153 MapSector * sector = getSectorCreate(p2d);
155 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
158 block = sector->createBlankBlock(p3d.Y);
162 bool Map::isNodeUnderground(v3s16 p)
164 v3s16 blockpos = getNodeBlockPos(p);
166 MapBlock * block = getBlockNoCreate(blockpos);
167 return block->getIsUnderground();
169 catch(InvalidPositionException &e)
176 Goes recursively through the neighbours of the node.
178 Alters only transparent nodes.
180 If the lighting of the neighbour is lower than the lighting of
181 the node was (before changing it to 0 at the step before), the
182 lighting of the neighbour is set to 0 and then the same stuff
183 repeats for the neighbour.
185 The ending nodes of the routine are stored in light_sources.
186 This is useful when a light is removed. In such case, this
187 routine can be called for the light node and then again for
188 light_sources to re-light the area without the removed light.
190 values of from_nodes are lighting values.
192 void Map::unspreadLight(enum LightBank bank,
193 core::map<v3s16, u8> & from_nodes,
194 core::map<v3s16, bool> & light_sources,
195 core::map<v3s16, MapBlock*> & modified_blocks)
198 v3s16(0,0,1), // back
200 v3s16(1,0,0), // right
201 v3s16(0,0,-1), // front
202 v3s16(0,-1,0), // bottom
203 v3s16(-1,0,0), // left
206 if(from_nodes.size() == 0)
209 u32 blockchangecount = 0;
211 core::map<v3s16, u8> unlighted_nodes;
212 core::map<v3s16, u8>::Iterator j;
213 j = from_nodes.getIterator();
216 Initialize block cache
219 MapBlock *block = NULL;
220 // Cache this a bit, too
221 bool block_checked_in_modified = false;
223 for(; j.atEnd() == false; j++)
225 v3s16 pos = j.getNode()->getKey();
226 v3s16 blockpos = getNodeBlockPos(pos);
228 // Only fetch a new block if the block position has changed
230 if(block == NULL || blockpos != blockpos_last){
231 block = getBlockNoCreate(blockpos);
232 blockpos_last = blockpos;
234 block_checked_in_modified = false;
238 catch(InvalidPositionException &e)
246 // Calculate relative position in block
247 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
249 // Get node straight from the block
250 MapNode n = block->getNode(relpos);
252 u8 oldlight = j.getNode()->getValue();
254 // Loop through 6 neighbors
255 for(u16 i=0; i<6; i++)
257 // Get the position of the neighbor node
258 v3s16 n2pos = pos + dirs[i];
260 // Get the block where the node is located
261 v3s16 blockpos = getNodeBlockPos(n2pos);
265 // Only fetch a new block if the block position has changed
267 if(block == NULL || blockpos != blockpos_last){
268 block = getBlockNoCreate(blockpos);
269 blockpos_last = blockpos;
271 block_checked_in_modified = false;
275 catch(InvalidPositionException &e)
280 // Calculate relative position in block
281 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
282 // Get node straight from the block
283 MapNode n2 = block->getNode(relpos);
285 bool changed = false;
287 //TODO: Optimize output by optimizing light_sources?
290 If the neighbor is dimmer than what was specified
291 as oldlight (the light of the previous node)
293 if(n2.getLight(bank) < oldlight)
296 And the neighbor is transparent and it has some light
298 if(n2.light_propagates() && n2.getLight(bank) != 0)
301 Set light to 0 and add to queue
304 u8 current_light = n2.getLight(bank);
305 n2.setLight(bank, 0);
306 block->setNode(relpos, n2);
308 unlighted_nodes.insert(n2pos, current_light);
312 Remove from light_sources if it is there
313 NOTE: This doesn't happen nearly at all
315 /*if(light_sources.find(n2pos))
317 std::cout<<"Removed from light_sources"<<std::endl;
318 light_sources.remove(n2pos);
323 if(light_sources.find(n2pos) != NULL)
324 light_sources.remove(n2pos);*/
327 light_sources.insert(n2pos, true);
330 // Add to modified_blocks
331 if(changed == true && block_checked_in_modified == false)
333 // If the block is not found in modified_blocks, add.
334 if(modified_blocks.find(blockpos) == NULL)
336 modified_blocks.insert(blockpos, block);
338 block_checked_in_modified = true;
341 catch(InvalidPositionException &e)
348 /*dstream<<"unspreadLight(): Changed block "
349 <<blockchangecount<<" times"
350 <<" for "<<from_nodes.size()<<" nodes"
353 if(unlighted_nodes.size() > 0)
354 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
358 A single-node wrapper of the above
360 void Map::unLightNeighbors(enum LightBank bank,
361 v3s16 pos, u8 lightwas,
362 core::map<v3s16, bool> & light_sources,
363 core::map<v3s16, MapBlock*> & modified_blocks)
365 core::map<v3s16, u8> from_nodes;
366 from_nodes.insert(pos, lightwas);
368 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
372 Lights neighbors of from_nodes, collects all them and then
375 void Map::spreadLight(enum LightBank bank,
376 core::map<v3s16, bool> & from_nodes,
377 core::map<v3s16, MapBlock*> & modified_blocks)
379 const v3s16 dirs[6] = {
380 v3s16(0,0,1), // back
382 v3s16(1,0,0), // right
383 v3s16(0,0,-1), // front
384 v3s16(0,-1,0), // bottom
385 v3s16(-1,0,0), // left
388 if(from_nodes.size() == 0)
391 u32 blockchangecount = 0;
393 core::map<v3s16, bool> lighted_nodes;
394 core::map<v3s16, bool>::Iterator j;
395 j = from_nodes.getIterator();
398 Initialize block cache
401 MapBlock *block = NULL;
402 // Cache this a bit, too
403 bool block_checked_in_modified = false;
405 for(; j.atEnd() == false; j++)
406 //for(; j != from_nodes.end(); j++)
408 v3s16 pos = j.getNode()->getKey();
410 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
411 v3s16 blockpos = getNodeBlockPos(pos);
413 // Only fetch a new block if the block position has changed
415 if(block == NULL || blockpos != blockpos_last){
416 block = getBlockNoCreate(blockpos);
417 blockpos_last = blockpos;
419 block_checked_in_modified = false;
423 catch(InvalidPositionException &e)
431 // Calculate relative position in block
432 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
434 // Get node straight from the block
435 MapNode n = block->getNode(relpos);
437 u8 oldlight = n.getLight(bank);
438 u8 newlight = diminish_light(oldlight);
440 // Loop through 6 neighbors
441 for(u16 i=0; i<6; i++){
442 // Get the position of the neighbor node
443 v3s16 n2pos = pos + dirs[i];
445 // Get the block where the node is located
446 v3s16 blockpos = getNodeBlockPos(n2pos);
450 // Only fetch a new block if the block position has changed
452 if(block == NULL || blockpos != blockpos_last){
453 block = getBlockNoCreate(blockpos);
454 blockpos_last = blockpos;
456 block_checked_in_modified = false;
460 catch(InvalidPositionException &e)
465 // Calculate relative position in block
466 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
467 // Get node straight from the block
468 MapNode n2 = block->getNode(relpos);
470 bool changed = false;
472 If the neighbor is brighter than the current node,
473 add to list (it will light up this node on its turn)
475 if(n2.getLight(bank) > undiminish_light(oldlight))
477 lighted_nodes.insert(n2pos, true);
478 //lighted_nodes.push_back(n2pos);
482 If the neighbor is dimmer than how much light this node
483 would spread on it, add to list
485 if(n2.getLight(bank) < newlight)
487 if(n2.light_propagates())
489 n2.setLight(bank, newlight);
490 block->setNode(relpos, n2);
491 lighted_nodes.insert(n2pos, true);
492 //lighted_nodes.push_back(n2pos);
497 // Add to modified_blocks
498 if(changed == true && block_checked_in_modified == false)
500 // If the block is not found in modified_blocks, add.
501 if(modified_blocks.find(blockpos) == NULL)
503 modified_blocks.insert(blockpos, block);
505 block_checked_in_modified = true;
508 catch(InvalidPositionException &e)
515 /*dstream<<"spreadLight(): Changed block "
516 <<blockchangecount<<" times"
517 <<" for "<<from_nodes.size()<<" nodes"
520 if(lighted_nodes.size() > 0)
521 spreadLight(bank, lighted_nodes, modified_blocks);
525 A single-node source variation of the above.
527 void Map::lightNeighbors(enum LightBank bank,
529 core::map<v3s16, MapBlock*> & modified_blocks)
531 core::map<v3s16, bool> from_nodes;
532 from_nodes.insert(pos, true);
533 spreadLight(bank, from_nodes, modified_blocks);
536 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
539 v3s16(0,0,1), // back
541 v3s16(1,0,0), // right
542 v3s16(0,0,-1), // front
543 v3s16(0,-1,0), // bottom
544 v3s16(-1,0,0), // left
547 u8 brightest_light = 0;
548 v3s16 brightest_pos(0,0,0);
549 bool found_something = false;
551 // Loop through 6 neighbors
552 for(u16 i=0; i<6; i++){
553 // Get the position of the neighbor node
554 v3s16 n2pos = p + dirs[i];
559 catch(InvalidPositionException &e)
563 if(n2.getLight(bank) > brightest_light || found_something == false){
564 brightest_light = n2.getLight(bank);
565 brightest_pos = n2pos;
566 found_something = true;
570 if(found_something == false)
571 throw InvalidPositionException();
573 return brightest_pos;
577 Propagates sunlight down from a node.
578 Starting point gets sunlight.
580 Returns the lowest y value of where the sunlight went.
582 Mud is turned into grass in where the sunlight stops.
584 s16 Map::propagateSunlight(v3s16 start,
585 core::map<v3s16, MapBlock*> & modified_blocks)
590 v3s16 pos(start.X, y, start.Z);
592 v3s16 blockpos = getNodeBlockPos(pos);
595 block = getBlockNoCreate(blockpos);
597 catch(InvalidPositionException &e)
602 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
603 MapNode n = block->getNode(relpos);
605 if(n.sunlight_propagates())
607 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
608 block->setNode(relpos, n);
610 modified_blocks.insert(blockpos, block);
614 /*// Turn mud into grass
615 if(n.d == CONTENT_MUD)
618 block->setNode(relpos, n);
619 modified_blocks.insert(blockpos, block);
622 // Sunlight goes no further
629 void Map::updateLighting(enum LightBank bank,
630 core::map<v3s16, MapBlock*> & a_blocks,
631 core::map<v3s16, MapBlock*> & modified_blocks)
633 /*m_dout<<DTIME<<"Map::updateLighting(): "
634 <<a_blocks.size()<<" blocks."<<std::endl;*/
636 //TimeTaker timer("updateLighting");
640 //u32 count_was = modified_blocks.size();
642 core::map<v3s16, MapBlock*> blocks_to_update;
644 core::map<v3s16, bool> light_sources;
646 core::map<v3s16, u8> unlight_from;
648 core::map<v3s16, MapBlock*>::Iterator i;
649 i = a_blocks.getIterator();
650 for(; i.atEnd() == false; i++)
652 MapBlock *block = i.getNode()->getValue();
656 // Don't bother with dummy blocks.
660 v3s16 pos = block->getPos();
661 modified_blocks.insert(pos, block);
663 blocks_to_update.insert(pos, block);
666 Clear all light from block
668 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
669 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
670 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
675 MapNode n = block->getNode(v3s16(x,y,z));
676 u8 oldlight = n.getLight(bank);
678 block->setNode(v3s16(x,y,z), n);
680 // Collect borders for unlighting
681 if(x==0 || x == MAP_BLOCKSIZE-1
682 || y==0 || y == MAP_BLOCKSIZE-1
683 || z==0 || z == MAP_BLOCKSIZE-1)
685 v3s16 p_map = p + v3s16(
688 MAP_BLOCKSIZE*pos.Z);
689 unlight_from.insert(p_map, oldlight);
692 catch(InvalidPositionException &e)
695 This would happen when dealing with a
699 dstream<<"updateLighting(): InvalidPositionException"
704 if(bank == LIGHTBANK_DAY)
706 bool bottom_valid = block->propagateSunlight(light_sources);
708 // If bottom is valid, we're done.
712 else if(bank == LIGHTBANK_NIGHT)
714 // For night lighting, sunlight is not propagated
719 // Invalid lighting bank
723 /*dstream<<"Bottom for sunlight-propagated block ("
724 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
727 // Bottom sunlight is not valid; get the block and loop to it
731 block = getBlockNoCreate(pos);
733 catch(InvalidPositionException &e)
743 TimeTaker timer("unspreadLight");
744 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
749 u32 diff = modified_blocks.size() - count_was;
750 count_was = modified_blocks.size();
751 dstream<<"unspreadLight modified "<<diff<<std::endl;
755 TimeTaker timer("spreadLight");
756 spreadLight(bank, light_sources, modified_blocks);
761 u32 diff = modified_blocks.size() - count_was;
762 count_was = modified_blocks.size();
763 dstream<<"spreadLight modified "<<diff<<std::endl;
768 //MapVoxelManipulator vmanip(this);
770 // Make a manual voxel manipulator and load all the blocks
771 // that touch the requested blocks
772 ManualMapVoxelManipulator vmanip(this);
773 core::map<v3s16, MapBlock*>::Iterator i;
774 i = blocks_to_update.getIterator();
775 for(; i.atEnd() == false; i++)
777 MapBlock *block = i.getNode()->getValue();
778 v3s16 p = block->getPos();
780 // Add all surrounding blocks
781 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
784 Add all surrounding blocks that have up-to-date lighting
785 NOTE: This doesn't quite do the job (not everything
786 appropriate is lighted)
788 /*for(s16 z=-1; z<=1; z++)
789 for(s16 y=-1; y<=1; y++)
790 for(s16 x=-1; x<=1; x++)
793 MapBlock *block = getBlockNoCreateNoEx(p);
798 if(block->getLightingExpired())
800 vmanip.initialEmerge(p, p);
803 // Lighting of block will be updated completely
804 block->setLightingExpired(false);
808 //TimeTaker timer("unSpreadLight");
809 vmanip.unspreadLight(bank, unlight_from, light_sources);
812 //TimeTaker timer("spreadLight");
813 vmanip.spreadLight(bank, light_sources);
816 //TimeTaker timer("blitBack");
817 vmanip.blitBack(modified_blocks);
819 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
823 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
826 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
827 core::map<v3s16, MapBlock*> & modified_blocks)
829 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
830 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
833 Update information about whether day and night light differ
835 for(core::map<v3s16, MapBlock*>::Iterator
836 i = modified_blocks.getIterator();
837 i.atEnd() == false; i++)
839 MapBlock *block = i.getNode()->getValue();
840 block->updateDayNightDiff();
846 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
847 core::map<v3s16, MapBlock*> &modified_blocks)
850 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
851 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
854 From this node to nodes underneath:
855 If lighting is sunlight (1.0), unlight neighbours and
860 v3s16 toppos = p + v3s16(0,1,0);
861 v3s16 bottompos = p + v3s16(0,-1,0);
863 bool node_under_sunlight = true;
864 core::map<v3s16, bool> light_sources;
867 If there is a node at top and it doesn't have sunlight,
868 there has not been any sunlight going down.
870 Otherwise there probably is.
873 MapNode topnode = getNode(toppos);
875 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
876 node_under_sunlight = false;
878 catch(InvalidPositionException &e)
884 If the new node is solid and there is grass below, change it to mud
886 if(content_features(n.d).walkable == true)
889 MapNode bottomnode = getNode(bottompos);
891 if(bottomnode.d == CONTENT_GRASS
892 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
894 bottomnode.d = CONTENT_MUD;
895 setNode(bottompos, bottomnode);
898 catch(InvalidPositionException &e)
906 If the new node is mud and it is under sunlight, change it
909 if(n.d == CONTENT_MUD && node_under_sunlight)
916 Remove all light that has come out of this node
919 enum LightBank banks[] =
924 for(s32 i=0; i<2; i++)
926 enum LightBank bank = banks[i];
928 u8 lightwas = getNode(p).getLight(bank);
930 // Add the block of the added node to modified_blocks
931 v3s16 blockpos = getNodeBlockPos(p);
932 MapBlock * block = getBlockNoCreate(blockpos);
933 assert(block != NULL);
934 modified_blocks.insert(blockpos, block);
936 assert(isValidPosition(p));
938 // Unlight neighbours of node.
939 // This means setting light of all consequent dimmer nodes
941 // This also collects the nodes at the border which will spread
942 // light again into this.
943 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
949 If node lets sunlight through and is under sunlight, it has
952 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
954 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
958 Set the node on the map
967 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
970 NodeMetadata *meta = meta_proto->clone();
971 setNodeMetadata(p, meta);
975 If node is under sunlight and doesn't let sunlight through,
976 take all sunlighted nodes under it and clear light from them
977 and from where the light has been spread.
978 TODO: This could be optimized by mass-unlighting instead
981 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
985 //m_dout<<DTIME<<"y="<<y<<std::endl;
986 v3s16 n2pos(p.X, y, p.Z);
992 catch(InvalidPositionException &e)
997 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
999 unLightNeighbors(LIGHTBANK_DAY,
1000 n2pos, n2.getLight(LIGHTBANK_DAY),
1001 light_sources, modified_blocks);
1002 n2.setLight(LIGHTBANK_DAY, 0);
1010 for(s32 i=0; i<2; i++)
1012 enum LightBank bank = banks[i];
1015 Spread light from all nodes that might be capable of doing so
1017 spreadLight(bank, light_sources, modified_blocks);
1021 Update information about whether day and night light differ
1023 for(core::map<v3s16, MapBlock*>::Iterator
1024 i = modified_blocks.getIterator();
1025 i.atEnd() == false; i++)
1027 MapBlock *block = i.getNode()->getValue();
1028 block->updateDayNightDiff();
1032 Add neighboring liquid nodes and the node itself if it is
1033 liquid (=water node was added) to transform queue.
1036 v3s16(0,0,0), // self
1037 v3s16(0,0,1), // back
1038 v3s16(0,1,0), // top
1039 v3s16(1,0,0), // right
1040 v3s16(0,0,-1), // front
1041 v3s16(0,-1,0), // bottom
1042 v3s16(-1,0,0), // left
1044 for(u16 i=0; i<7; i++)
1049 v3s16 p2 = p + dirs[i];
1051 MapNode n2 = getNode(p2);
1052 if(content_liquid(n2.d))
1054 m_transforming_liquid.push_back(p2);
1057 }catch(InvalidPositionException &e)
1065 void Map::removeNodeAndUpdate(v3s16 p,
1066 core::map<v3s16, MapBlock*> &modified_blocks)
1068 /*PrintInfo(m_dout);
1069 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1070 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1072 bool node_under_sunlight = true;
1074 v3s16 toppos = p + v3s16(0,1,0);
1076 // Node will be replaced with this
1077 u8 replace_material = CONTENT_AIR;
1080 If there is a node at top and it doesn't have sunlight,
1081 there will be no sunlight going down.
1084 MapNode topnode = getNode(toppos);
1086 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1087 node_under_sunlight = false;
1089 catch(InvalidPositionException &e)
1093 core::map<v3s16, bool> light_sources;
1095 enum LightBank banks[] =
1100 for(s32 i=0; i<2; i++)
1102 enum LightBank bank = banks[i];
1105 Unlight neighbors (in case the node is a light source)
1107 unLightNeighbors(bank, p,
1108 getNode(p).getLight(bank),
1109 light_sources, modified_blocks);
1113 Remove node metadata
1116 removeNodeMetadata(p);
1120 This also clears the lighting.
1124 n.d = replace_material;
1127 for(s32 i=0; i<2; i++)
1129 enum LightBank bank = banks[i];
1132 Recalculate lighting
1134 spreadLight(bank, light_sources, modified_blocks);
1137 // Add the block of the removed node to modified_blocks
1138 v3s16 blockpos = getNodeBlockPos(p);
1139 MapBlock * block = getBlockNoCreate(blockpos);
1140 assert(block != NULL);
1141 modified_blocks.insert(blockpos, block);
1144 If the removed node was under sunlight, propagate the
1145 sunlight down from it and then light all neighbors
1146 of the propagated blocks.
1148 if(node_under_sunlight)
1150 s16 ybottom = propagateSunlight(p, modified_blocks);
1151 /*m_dout<<DTIME<<"Node was under sunlight. "
1152 "Propagating sunlight";
1153 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1155 for(; y >= ybottom; y--)
1157 v3s16 p2(p.X, y, p.Z);
1158 /*m_dout<<DTIME<<"lighting neighbors of node ("
1159 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1161 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1166 // Set the lighting of this node to 0
1167 // TODO: Is this needed? Lighting is cleared up there already.
1169 MapNode n = getNode(p);
1170 n.setLight(LIGHTBANK_DAY, 0);
1173 catch(InvalidPositionException &e)
1179 for(s32 i=0; i<2; i++)
1181 enum LightBank bank = banks[i];
1183 // Get the brightest neighbour node and propagate light from it
1184 v3s16 n2p = getBrightestNeighbour(bank, p);
1186 MapNode n2 = getNode(n2p);
1187 lightNeighbors(bank, n2p, modified_blocks);
1189 catch(InvalidPositionException &e)
1195 Update information about whether day and night light differ
1197 for(core::map<v3s16, MapBlock*>::Iterator
1198 i = modified_blocks.getIterator();
1199 i.atEnd() == false; i++)
1201 MapBlock *block = i.getNode()->getValue();
1202 block->updateDayNightDiff();
1206 Add neighboring liquid nodes to transform queue.
1209 v3s16(0,0,1), // back
1210 v3s16(0,1,0), // top
1211 v3s16(1,0,0), // right
1212 v3s16(0,0,-1), // front
1213 v3s16(0,-1,0), // bottom
1214 v3s16(-1,0,0), // left
1216 for(u16 i=0; i<6; i++)
1221 v3s16 p2 = p + dirs[i];
1223 MapNode n2 = getNode(p2);
1224 if(content_liquid(n2.d))
1226 m_transforming_liquid.push_back(p2);
1229 }catch(InvalidPositionException &e)
1235 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1238 event.type = MEET_ADDNODE;
1242 bool succeeded = true;
1244 core::map<v3s16, MapBlock*> modified_blocks;
1245 addNodeAndUpdate(p, n, modified_blocks);
1247 // Copy modified_blocks to event
1248 for(core::map<v3s16, MapBlock*>::Iterator
1249 i = modified_blocks.getIterator();
1250 i.atEnd()==false; i++)
1252 event.modified_blocks.insert(i.getNode()->getKey(), false);
1255 catch(InvalidPositionException &e){
1259 dispatchEvent(&event);
1264 bool Map::removeNodeWithEvent(v3s16 p)
1267 event.type = MEET_REMOVENODE;
1270 bool succeeded = true;
1272 core::map<v3s16, MapBlock*> modified_blocks;
1273 removeNodeAndUpdate(p, modified_blocks);
1275 // Copy modified_blocks to event
1276 for(core::map<v3s16, MapBlock*>::Iterator
1277 i = modified_blocks.getIterator();
1278 i.atEnd()==false; i++)
1280 event.modified_blocks.insert(i.getNode()->getKey(), false);
1283 catch(InvalidPositionException &e){
1287 dispatchEvent(&event);
1292 bool Map::dayNightDiffed(v3s16 blockpos)
1295 v3s16 p = blockpos + v3s16(0,0,0);
1296 MapBlock *b = getBlockNoCreate(p);
1297 if(b->dayNightDiffed())
1300 catch(InvalidPositionException &e){}
1303 v3s16 p = blockpos + v3s16(-1,0,0);
1304 MapBlock *b = getBlockNoCreate(p);
1305 if(b->dayNightDiffed())
1308 catch(InvalidPositionException &e){}
1310 v3s16 p = blockpos + v3s16(0,-1,0);
1311 MapBlock *b = getBlockNoCreate(p);
1312 if(b->dayNightDiffed())
1315 catch(InvalidPositionException &e){}
1317 v3s16 p = blockpos + v3s16(0,0,-1);
1318 MapBlock *b = getBlockNoCreate(p);
1319 if(b->dayNightDiffed())
1322 catch(InvalidPositionException &e){}
1325 v3s16 p = blockpos + v3s16(1,0,0);
1326 MapBlock *b = getBlockNoCreate(p);
1327 if(b->dayNightDiffed())
1330 catch(InvalidPositionException &e){}
1332 v3s16 p = blockpos + v3s16(0,1,0);
1333 MapBlock *b = getBlockNoCreate(p);
1334 if(b->dayNightDiffed())
1337 catch(InvalidPositionException &e){}
1339 v3s16 p = blockpos + v3s16(0,0,1);
1340 MapBlock *b = getBlockNoCreate(p);
1341 if(b->dayNightDiffed())
1344 catch(InvalidPositionException &e){}
1350 Updates usage timers
1352 void Map::timerUpdate(float dtime)
1354 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1356 core::map<v2s16, MapSector*>::Iterator si;
1358 si = m_sectors.getIterator();
1359 for(; si.atEnd() == false; si++)
1361 MapSector *sector = si.getNode()->getValue();
1363 core::list<MapBlock*> blocks;
1364 sector->getBlocks(blocks);
1365 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1366 i != blocks.end(); i++)
1368 (*i)->incrementUsageTimer(dtime);
1373 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1375 core::list<v2s16>::Iterator j;
1376 for(j=list.begin(); j!=list.end(); j++)
1378 MapSector *sector = m_sectors[*j];
1381 sector->deleteBlocks();
1386 If sector is in sector cache, remove it from there
1388 if(m_sector_cache == sector)
1390 m_sector_cache = NULL;
1393 Remove from map and delete
1395 m_sectors.remove(*j);
1401 u32 Map::unloadUnusedData(float timeout, bool only_blocks,
1402 core::list<v3s16> *deleted_blocks)
1404 core::list<v2s16> sector_deletion_queue;
1406 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1407 for(; si.atEnd() == false; si++)
1409 MapSector *sector = si.getNode()->getValue();
1411 bool all_blocks_deleted = true;
1413 core::list<MapBlock*> blocks;
1414 sector->getBlocks(blocks);
1415 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1416 i != blocks.end(); i++)
1418 MapBlock *block = (*i);
1420 if(block->getUsageTimer() > timeout)
1423 if(block->getModified() != MOD_STATE_CLEAN)
1426 sector->removeBlock(block);
1431 all_blocks_deleted = false;
1435 if(all_blocks_deleted)
1437 sector_deletion_queue.push_back(si.getNode()->getKey());
1442 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1443 for(; i.atEnd() == false; i++)
1445 MapSector *sector = i.getNode()->getValue();
1447 Delete sector from memory if it hasn't been used in a long time
1449 if(sector->usage_timer > timeout)
1451 sector_deletion_queue.push_back(i.getNode()->getKey());
1453 if(deleted_blocks != NULL)
1455 // Collect positions of blocks of sector
1456 MapSector *sector = i.getNode()->getValue();
1457 core::list<MapBlock*> blocks;
1458 sector->getBlocks(blocks);
1459 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1460 i != blocks.end(); i++)
1462 deleted_blocks->push_back((*i)->getPos());
1469 deleteSectors(sector_deletion_queue, only_blocks);
1470 return sector_deletion_queue.getSize();
1473 void Map::PrintInfo(std::ostream &out)
1478 #define WATER_DROP_BOOST 4
1480 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1482 DSTACK(__FUNCTION_NAME);
1483 //TimeTaker timer("transformLiquids()");
1486 u32 initial_size = m_transforming_liquid.size();
1488 /*if(initial_size != 0)
1489 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1491 while(m_transforming_liquid.size() != 0)
1494 Get a queued transforming liquid node
1496 v3s16 p0 = m_transforming_liquid.pop_front();
1498 MapNode n0 = getNode(p0);
1500 // Don't deal with non-liquids
1501 if(content_liquid(n0.d) == false)
1504 bool is_source = !content_flowing_liquid(n0.d);
1506 u8 liquid_level = 8;
1507 if(is_source == false)
1508 liquid_level = n0.param2 & 0x0f;
1510 // Turn possible source into non-source
1511 u8 nonsource_c = make_liquid_flowing(n0.d);
1514 If not source, check that some node flows into this one
1515 and what is the level of liquid in this one
1517 if(is_source == false)
1519 s8 new_liquid_level_max = -1;
1521 v3s16 dirs_from[5] = {
1522 v3s16(0,1,0), // top
1523 v3s16(0,0,1), // back
1524 v3s16(1,0,0), // right
1525 v3s16(0,0,-1), // front
1526 v3s16(-1,0,0), // left
1528 for(u16 i=0; i<5; i++)
1533 bool from_top = (i==0);
1535 v3s16 p2 = p0 + dirs_from[i];
1536 MapNode n2 = getNode(p2);
1538 if(content_liquid(n2.d))
1540 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1541 // Check that the liquids are the same type
1542 if(n2_nonsource_c != nonsource_c)
1544 dstream<<"WARNING: Not handling: different liquids"
1545 " collide"<<std::endl;
1548 bool n2_is_source = !content_flowing_liquid(n2.d);
1549 s8 n2_liquid_level = 8;
1550 if(n2_is_source == false)
1551 n2_liquid_level = n2.param2 & 0x07;
1553 s8 new_liquid_level = -1;
1556 //new_liquid_level = 7;
1557 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1558 new_liquid_level = 7;
1560 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1562 else if(n2_liquid_level > 0)
1564 new_liquid_level = n2_liquid_level - 1;
1567 if(new_liquid_level > new_liquid_level_max)
1568 new_liquid_level_max = new_liquid_level;
1571 }catch(InvalidPositionException &e)
1577 If liquid level should be something else, update it and
1578 add all the neighboring water nodes to the transform queue.
1580 if(new_liquid_level_max != liquid_level)
1582 if(new_liquid_level_max == -1)
1584 // Remove water alltoghether
1591 n0.param2 = new_liquid_level_max;
1595 // Block has been modified
1597 v3s16 blockpos = getNodeBlockPos(p0);
1598 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1600 modified_blocks.insert(blockpos, block);
1604 Add neighboring non-source liquid nodes to transform queue.
1607 v3s16(0,0,1), // back
1608 v3s16(0,1,0), // top
1609 v3s16(1,0,0), // right
1610 v3s16(0,0,-1), // front
1611 v3s16(0,-1,0), // bottom
1612 v3s16(-1,0,0), // left
1614 for(u16 i=0; i<6; i++)
1619 v3s16 p2 = p0 + dirs[i];
1621 MapNode n2 = getNode(p2);
1622 if(content_flowing_liquid(n2.d))
1624 m_transforming_liquid.push_back(p2);
1627 }catch(InvalidPositionException &e)
1634 // Get a new one from queue if the node has turned into non-water
1635 if(content_liquid(n0.d) == false)
1639 Flow water from this node
1641 v3s16 dirs_to[5] = {
1642 v3s16(0,-1,0), // bottom
1643 v3s16(0,0,1), // back
1644 v3s16(1,0,0), // right
1645 v3s16(0,0,-1), // front
1646 v3s16(-1,0,0), // left
1648 for(u16 i=0; i<5; i++)
1653 bool to_bottom = (i == 0);
1655 // If liquid is at lowest possible height, it's not going
1656 // anywhere except down
1657 if(liquid_level == 0 && to_bottom == false)
1660 u8 liquid_next_level = 0;
1661 // If going to bottom
1664 //liquid_next_level = 7;
1665 if(liquid_level >= 7 - WATER_DROP_BOOST)
1666 liquid_next_level = 7;
1668 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1671 liquid_next_level = liquid_level - 1;
1673 bool n2_changed = false;
1674 bool flowed = false;
1676 v3s16 p2 = p0 + dirs_to[i];
1678 MapNode n2 = getNode(p2);
1679 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1681 if(content_liquid(n2.d))
1683 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1684 // Check that the liquids are the same type
1685 if(n2_nonsource_c != nonsource_c)
1687 dstream<<"WARNING: Not handling: different liquids"
1688 " collide"<<std::endl;
1691 bool n2_is_source = !content_flowing_liquid(n2.d);
1692 u8 n2_liquid_level = 8;
1693 if(n2_is_source == false)
1694 n2_liquid_level = n2.param2 & 0x07;
1703 // Just flow into the source, nothing changes.
1704 // n2_changed is not set because destination didn't change
1709 if(liquid_next_level > liquid_level)
1711 n2.param2 = liquid_next_level;
1719 else if(n2.d == CONTENT_AIR)
1722 n2.param2 = liquid_next_level;
1729 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1733 m_transforming_liquid.push_back(p2);
1735 v3s16 blockpos = getNodeBlockPos(p2);
1736 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1738 modified_blocks.insert(blockpos, block);
1741 // If n2_changed to bottom, don't flow anywhere else
1742 if(to_bottom && flowed && !is_source)
1745 }catch(InvalidPositionException &e)
1751 //if(loopcount >= 100000)
1752 if(loopcount >= initial_size * 1)
1755 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1758 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1760 v3s16 blockpos = getNodeBlockPos(p);
1761 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1762 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1765 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1769 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1773 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1775 v3s16 blockpos = getNodeBlockPos(p);
1776 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1777 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1780 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1784 block->m_node_metadata.set(p_rel, meta);
1787 void Map::removeNodeMetadata(v3s16 p)
1789 v3s16 blockpos = getNodeBlockPos(p);
1790 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1791 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1794 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1798 block->m_node_metadata.remove(p_rel);
1801 void Map::nodeMetadataStep(float dtime,
1802 core::map<v3s16, MapBlock*> &changed_blocks)
1806 Currently there is no way to ensure that all the necessary
1807 blocks are loaded when this is run. (They might get unloaded)
1808 NOTE: ^- Actually, that might not be so. In a quick test it
1809 reloaded a block with a furnace when I walked back to it from
1812 core::map<v2s16, MapSector*>::Iterator si;
1813 si = m_sectors.getIterator();
1814 for(; si.atEnd() == false; si++)
1816 MapSector *sector = si.getNode()->getValue();
1817 core::list< MapBlock * > sectorblocks;
1818 sector->getBlocks(sectorblocks);
1819 core::list< MapBlock * >::Iterator i;
1820 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1822 MapBlock *block = *i;
1823 bool changed = block->m_node_metadata.step(dtime);
1825 changed_blocks[block->getPos()] = block;
1834 ServerMap::ServerMap(std::string savedir):
1837 m_map_metadata_changed(true)
1839 dstream<<__FUNCTION_NAME<<std::endl;
1841 //m_chunksize = 8; // Takes a few seconds
1843 m_seed = (((u64)(myrand()%0xffff)<<0)
1844 + ((u64)(myrand()%0xffff)<<16)
1845 + ((u64)(myrand()%0xffff)<<32)
1846 + ((u64)(myrand()%0xffff)<<48));
1849 Experimental and debug stuff
1856 Try to load map; if not found, create a new one.
1859 m_savedir = savedir;
1860 m_map_saving_enabled = false;
1864 // If directory exists, check contents and load if possible
1865 if(fs::PathExists(m_savedir))
1867 // If directory is empty, it is safe to save into it.
1868 if(fs::GetDirListing(m_savedir).size() == 0)
1870 dstream<<DTIME<<"Server: Empty save directory is valid."
1872 m_map_saving_enabled = true;
1877 // Load map metadata (seed, chunksize)
1880 catch(FileNotGoodException &e){
1881 dstream<<DTIME<<"WARNING: Could not load map metadata"
1882 //<<" Disabling chunk-based generator."
1888 // Load chunk metadata
1891 catch(FileNotGoodException &e){
1892 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1893 <<" Disabling chunk-based generator."
1898 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1899 "metadata and sector (0,0) from "<<savedir<<
1900 ", assuming valid save directory."
1903 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1904 <<"and chunk metadata from "<<savedir
1905 <<", assuming valid save directory."
1908 m_map_saving_enabled = true;
1909 // Map loaded, not creating new one
1913 // If directory doesn't exist, it is safe to save to it
1915 m_map_saving_enabled = true;
1918 catch(std::exception &e)
1920 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1921 <<", exception: "<<e.what()<<std::endl;
1922 dstream<<"Please remove the map or fix it."<<std::endl;
1923 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1926 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1928 // Create zero sector
1929 emergeSector(v2s16(0,0));
1931 // Initially write whole map
1935 ServerMap::~ServerMap()
1937 dstream<<__FUNCTION_NAME<<std::endl;
1941 if(m_map_saving_enabled)
1944 // Save only changed parts
1946 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1950 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1953 catch(std::exception &e)
1955 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1956 <<", exception: "<<e.what()<<std::endl;
1963 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1964 for(; i.atEnd() == false; i++)
1966 MapChunk *chunk = i.getNode()->getValue();
1972 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
1974 /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
1975 <<blockpos.Z<<")"<<std::endl;*/
1977 data->no_op = false;
1978 data->seed = m_seed;
1979 data->blockpos = blockpos;
1982 Create the whole area of this and the neighboring blocks
1985 //TimeTaker timer("initBlockMake() create area");
1987 for(s16 x=-1; x<=1; x++)
1988 for(s16 z=-1; z<=1; z++)
1990 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
1991 // Sector metadata is loaded from disk if not already loaded.
1992 ServerMapSector *sector = createSector(sectorpos);
1995 for(s16 y=-1; y<=1; y++)
1997 MapBlock *block = createBlock(blockpos);
1999 // Lighting won't be calculated
2000 block->setLightingExpired(true);
2001 // Lighting will be calculated
2002 //block->setLightingExpired(false);
2005 Block gets sunlight if this is true.
2007 This should be set to true when the top side of a block
2008 is completely exposed to the sky.
2010 block->setIsUnderground(false);
2016 Now we have a big empty area.
2018 Make a ManualMapVoxelManipulator that contains this and the
2022 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2023 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2025 data->vmanip = new ManualMapVoxelManipulator(this);
2026 //data->vmanip->setMap(this);
2030 //TimeTaker timer("initBlockMake() initialEmerge");
2031 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2034 // Data is ready now.
2037 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2038 core::map<v3s16, MapBlock*> &changed_blocks)
2040 v3s16 blockpos = data->blockpos;
2041 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2042 <<blockpos.Z<<")"<<std::endl;*/
2046 dstream<<"finishBlockMake(): no-op"<<std::endl;
2050 /*dstream<<"Resulting vmanip:"<<std::endl;
2051 data->vmanip.print(dstream);*/
2054 Blit generated stuff to map
2055 NOTE: blitBackAll adds nearly everything to changed_blocks
2059 //TimeTaker timer("finishBlockMake() blitBackAll");
2060 data->vmanip->blitBackAll(&changed_blocks);
2063 dstream<<"finishBlockMake: changed_blocks.size()="
2064 <<changed_blocks.size()<<std::endl;
2067 Copy transforming liquid information
2069 while(data->transforming_liquid.size() > 0)
2071 v3s16 p = data->transforming_liquid.pop_front();
2072 m_transforming_liquid.push_back(p);
2078 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2082 Set is_underground flag for lighting with sunlight
2085 block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2088 Add sunlight to central block.
2089 This makes in-dark-spawning monsters to not flood the whole thing.
2090 Do not spread the light, though.
2092 /*core::map<v3s16, bool> light_sources;
2093 bool black_air_left = false;
2094 block->propagateSunlight(light_sources, true, &black_air_left);*/
2097 NOTE: Lighting and object adding shouldn't really be here, but
2098 lighting is a bit tricky to move properly to makeBlock.
2099 TODO: Do this the right way anyway.
2106 core::map<v3s16, MapBlock*> lighting_update_blocks;
2108 lighting_update_blocks.insert(block->getPos(), block);
2110 // All modified blocks
2111 for(core::map<v3s16, MapBlock*>::Iterator
2112 i = changed_blocks.getIterator();
2113 i.atEnd() == false; i++)
2115 lighting_update_blocks.insert(i.getNode()->getKey(),
2116 i.getNode()->getValue());
2119 updateLighting(lighting_update_blocks, changed_blocks);
2122 Add random objects to block
2124 mapgen::add_random_objects(block);
2127 Go through changed blocks
2129 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2130 i.atEnd() == false; i++)
2132 MapBlock *block = i.getNode()->getValue();
2135 Update day/night difference cache of the MapBlocks
2137 block->updateDayNightDiff();
2139 Set block as modified
2141 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2145 Set central block as generated
2147 block->setGenerated(true);
2150 Save changed parts of map
2151 NOTE: Will be saved later.
2155 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2156 <<blockpos.Z<<")"<<std::endl;*/
2161 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2163 DSTACKF("%s: p2d=(%d,%d)",
2168 Check if it exists already in memory
2170 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2175 Try to load it from disk (with blocks)
2177 //if(loadSectorFull(p2d) == true)
2180 Try to load metadata from disk
2182 if(loadSectorMeta(p2d) == true)
2184 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2187 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2188 throw InvalidPositionException("");
2194 Do not create over-limit
2196 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2197 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2198 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2199 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2200 throw InvalidPositionException("createSector(): pos. over limit");
2203 Generate blank sector
2206 sector = new ServerMapSector(this, p2d);
2208 // Sector position on map in nodes
2209 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2214 m_sectors.insert(p2d, sector);
2220 This is a quick-hand function for calling makeBlock().
2222 MapBlock * ServerMap::generateBlock(
2224 core::map<v3s16, MapBlock*> &modified_blocks
2227 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2229 /*dstream<<"generateBlock(): "
2230 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2233 TimeTaker timer("generateBlock");
2235 //MapBlock *block = original_dummy;
2237 v2s16 p2d(p.X, p.Z);
2238 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2241 Do not generate over-limit
2243 if(blockpos_over_limit(p))
2245 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2246 throw InvalidPositionException("generateBlock(): pos. over limit");
2250 Create block make data
2252 mapgen::BlockMakeData data;
2253 initBlockMake(&data, p);
2259 TimeTaker t("mapgen::make_block()");
2260 mapgen::make_block(&data);
2264 Blit data back on map, update lighting, add mobs and whatever this does
2266 finishBlockMake(&data, modified_blocks);
2271 MapBlock *block = getBlockNoCreateNoEx(p);
2278 bool erroneus_content = false;
2279 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2280 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2281 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2284 MapNode n = block->getNode(p);
2285 if(n.d == CONTENT_IGNORE)
2287 dstream<<"CONTENT_IGNORE at "
2288 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2290 erroneus_content = true;
2294 if(erroneus_content)
2302 Generate a completely empty block
2304 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2305 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2307 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2313 n.d = CONTENT_STONE;
2314 block->setNode(v3s16(x0,y0,z0), n);
2322 MapBlock * ServerMap::createBlock(v3s16 p)
2324 DSTACKF("%s: p=(%d,%d,%d)",
2325 __FUNCTION_NAME, p.X, p.Y, p.Z);
2328 Do not create over-limit
2330 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2331 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2332 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2333 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2334 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2335 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2336 throw InvalidPositionException("createBlock(): pos. over limit");
2338 v2s16 p2d(p.X, p.Z);
2341 This will create or load a sector if not found in memory.
2342 If block exists on disk, it will be loaded.
2344 NOTE: On old save formats, this will be slow, as it generates
2345 lighting on blocks for them.
2347 ServerMapSector *sector;
2349 sector = (ServerMapSector*)createSector(p2d);
2350 assert(sector->getId() == MAPSECTOR_SERVER);
2352 catch(InvalidPositionException &e)
2354 dstream<<"createBlock: createSector() failed"<<std::endl;
2358 NOTE: This should not be done, or at least the exception
2359 should not be passed on as std::exception, because it
2360 won't be catched at all.
2362 /*catch(std::exception &e)
2364 dstream<<"createBlock: createSector() failed: "
2365 <<e.what()<<std::endl;
2370 Try to get a block from the sector
2373 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2376 if(block->isDummy())
2381 block = sector->createBlankBlock(block_y);
2386 MapBlock * ServerMap::emergeBlock(
2388 bool only_from_disk,
2389 core::map<v3s16, MapBlock*> &changed_blocks,
2390 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2393 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
2395 p.X, p.Y, p.Z, only_from_disk);
2397 // This has to be redone or removed
2405 Do not generate over-limit
2407 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2408 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2409 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2410 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2411 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2412 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2413 throw InvalidPositionException("emergeBlock(): pos. over limit");
2415 v2s16 p2d(p.X, p.Z);
2418 This will create or load a sector if not found in memory.
2419 If block exists on disk, it will be loaded.
2421 ServerMapSector *sector;
2423 sector = createSector(p2d);
2424 //sector = emergeSector(p2d, changed_blocks);
2426 catch(InvalidPositionException &e)
2428 dstream<<"emergeBlock: createSector() failed: "
2429 <<e.what()<<std::endl;
2430 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2432 <<"You could try to delete it."<<std::endl;
2435 catch(VersionMismatchException &e)
2437 dstream<<"emergeBlock: createSector() failed: "
2438 <<e.what()<<std::endl;
2439 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2441 <<"You could try to delete it."<<std::endl;
2446 Try to get a block from the sector
2449 bool does_not_exist = false;
2450 bool lighting_expired = false;
2451 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2453 // If not found, try loading from disk
2456 block = loadBlock(p);
2462 does_not_exist = true;
2464 else if(block->isDummy() == true)
2466 does_not_exist = true;
2468 else if(block->getLightingExpired())
2470 lighting_expired = true;
2475 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2480 If block was not found on disk and not going to generate a
2481 new one, make sure there is a dummy block in place.
2483 if(only_from_disk && (does_not_exist || lighting_expired))
2485 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2489 // Create dummy block
2490 block = new MapBlock(this, p, true);
2492 // Add block to sector
2493 sector->insertBlock(block);
2499 //dstream<<"Not found on disk, generating."<<std::endl;
2501 //TimeTaker("emergeBlock() generate");
2503 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2506 If the block doesn't exist, generate the block.
2510 block = generateBlock(p, block, sector, changed_blocks,
2511 lighting_invalidated_blocks);
2514 if(lighting_expired)
2516 lighting_invalidated_blocks.insert(p, block);
2521 Initially update sunlight
2524 core::map<v3s16, bool> light_sources;
2525 bool black_air_left = false;
2526 bool bottom_invalid =
2527 block->propagateSunlight(light_sources, true,
2530 // If sunlight didn't reach everywhere and part of block is
2531 // above ground, lighting has to be properly updated
2532 //if(black_air_left && some_part_underground)
2535 lighting_invalidated_blocks[block->getPos()] = block;
2540 lighting_invalidated_blocks[block->getPos()] = block;
2549 s16 ServerMap::findGroundLevel(v2s16 p2d)
2553 Uh, just do something random...
2555 // Find existing map from top to down
2558 v3s16 p(p2d.X, max, p2d.Y);
2559 for(; p.Y>min; p.Y--)
2561 MapNode n = getNodeNoEx(p);
2562 if(n.d != CONTENT_IGNORE)
2567 // If this node is not air, go to plan b
2568 if(getNodeNoEx(p).d != CONTENT_AIR)
2570 // Search existing walkable and return it
2571 for(; p.Y>min; p.Y--)
2573 MapNode n = getNodeNoEx(p);
2574 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
2583 Determine from map generator noise functions
2586 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2589 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2590 //return (s16)level;
2593 void ServerMap::createDirs(std::string path)
2595 if(fs::CreateAllDirs(path) == false)
2597 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2598 <<"\""<<path<<"\""<<std::endl;
2599 throw BaseException("ServerMap failed to create directory");
2603 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2609 snprintf(cc, 9, "%.4x%.4x",
2610 (unsigned int)pos.X&0xffff,
2611 (unsigned int)pos.Y&0xffff);
2613 return m_savedir + "/sectors/" + cc;
2615 snprintf(cc, 9, "%.3x/%.3x",
2616 (unsigned int)pos.X&0xfff,
2617 (unsigned int)pos.Y&0xfff);
2619 return m_savedir + "/sectors2/" + cc;
2625 v2s16 ServerMap::getSectorPos(std::string dirname)
2629 size_t spos = dirname.rfind('/') + 1;
2630 assert(spos != std::string::npos);
2631 if(dirname.size() - spos == 8)
2634 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2636 else if(dirname.size() - spos == 3)
2639 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2640 // Sign-extend the 12 bit values up to 16 bits...
2641 if(x&0x800) x|=0xF000;
2642 if(y&0x800) y|=0xF000;
2649 v2s16 pos((s16)x, (s16)y);
2653 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2655 v2s16 p2d = getSectorPos(sectordir);
2657 if(blockfile.size() != 4){
2658 throw InvalidFilenameException("Invalid block filename");
2661 int r = sscanf(blockfile.c_str(), "%4x", &y);
2663 throw InvalidFilenameException("Invalid block filename");
2664 return v3s16(p2d.X, y, p2d.Y);
2667 std::string ServerMap::getBlockFilename(v3s16 p)
2670 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2674 void ServerMap::save(bool only_changed)
2676 DSTACK(__FUNCTION_NAME);
2677 if(m_map_saving_enabled == false)
2679 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2683 if(only_changed == false)
2684 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2687 if(only_changed == false || m_map_metadata_changed)
2692 u32 sector_meta_count = 0;
2693 u32 block_count = 0;
2694 u32 block_count_all = 0; // Number of blocks in memory
2696 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2697 for(; i.atEnd() == false; i++)
2699 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2700 assert(sector->getId() == MAPSECTOR_SERVER);
2702 if(sector->differs_from_disk || only_changed == false)
2704 saveSectorMeta(sector);
2705 sector_meta_count++;
2707 core::list<MapBlock*> blocks;
2708 sector->getBlocks(blocks);
2709 core::list<MapBlock*>::Iterator j;
2710 for(j=blocks.begin(); j!=blocks.end(); j++)
2712 MapBlock *block = *j;
2716 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2717 || only_changed == false)
2722 /*dstream<<"ServerMap: Written block ("
2723 <<block->getPos().X<<","
2724 <<block->getPos().Y<<","
2725 <<block->getPos().Z<<")"
2732 Only print if something happened or saved whole map
2734 if(only_changed == false || sector_meta_count != 0
2735 || block_count != 0)
2737 dstream<<DTIME<<"ServerMap: Written: "
2738 <<sector_meta_count<<" sector metadata files, "
2739 <<block_count<<" block files"
2740 <<", "<<block_count_all<<" blocks in memory."
2745 void ServerMap::saveMapMeta()
2747 DSTACK(__FUNCTION_NAME);
2749 dstream<<"INFO: ServerMap::saveMapMeta(): "
2753 createDirs(m_savedir);
2755 std::string fullpath = m_savedir + "/map_meta.txt";
2756 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2757 if(os.good() == false)
2759 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2760 <<"could not open"<<fullpath<<std::endl;
2761 throw FileNotGoodException("Cannot open chunk metadata");
2765 params.setU64("seed", m_seed);
2767 params.writeLines(os);
2769 os<<"[end_of_params]\n";
2771 m_map_metadata_changed = false;
2774 void ServerMap::loadMapMeta()
2776 DSTACK(__FUNCTION_NAME);
2778 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2781 std::string fullpath = m_savedir + "/map_meta.txt";
2782 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2783 if(is.good() == false)
2785 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2786 <<"could not open"<<fullpath<<std::endl;
2787 throw FileNotGoodException("Cannot open map metadata");
2795 throw SerializationError
2796 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2798 std::getline(is, line);
2799 std::string trimmedline = trim(line);
2800 if(trimmedline == "[end_of_params]")
2802 params.parseConfigLine(line);
2805 m_seed = params.getU64("seed");
2807 dstream<<"INFO: ServerMap::loadMapMeta(): "
2812 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2814 DSTACK(__FUNCTION_NAME);
2815 // Format used for writing
2816 u8 version = SER_FMT_VER_HIGHEST;
2818 v2s16 pos = sector->getPos();
2819 std::string dir = getSectorDir(pos);
2822 std::string fullpath = dir + "/meta";
2823 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2824 if(o.good() == false)
2825 throw FileNotGoodException("Cannot open sector metafile");
2827 sector->serialize(o, version);
2829 sector->differs_from_disk = false;
2832 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2834 DSTACK(__FUNCTION_NAME);
2836 v2s16 p2d = getSectorPos(sectordir);
2838 ServerMapSector *sector = NULL;
2840 std::string fullpath = sectordir + "/meta";
2841 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2842 if(is.good() == false)
2844 // If the directory exists anyway, it probably is in some old
2845 // format. Just go ahead and create the sector.
2846 if(fs::PathExists(sectordir))
2848 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
2849 <<fullpath<<" doesn't exist but directory does."
2850 <<" Continuing with a sector with no metadata."
2852 sector = new ServerMapSector(this, p2d);
2853 m_sectors.insert(p2d, sector);
2857 throw FileNotGoodException("Cannot open sector metafile");
2862 sector = ServerMapSector::deSerialize
2863 (is, this, p2d, m_sectors);
2865 saveSectorMeta(sector);
2868 sector->differs_from_disk = false;
2873 bool ServerMap::loadSectorMeta(v2s16 p2d)
2875 DSTACK(__FUNCTION_NAME);
2877 MapSector *sector = NULL;
2879 // The directory layout we're going to load from.
2880 // 1 - original sectors/xxxxzzzz/
2881 // 2 - new sectors2/xxx/zzz/
2882 // If we load from anything but the latest structure, we will
2883 // immediately save to the new one, and remove the old.
2885 std::string sectordir1 = getSectorDir(p2d, 1);
2886 std::string sectordir;
2887 if(fs::PathExists(sectordir1))
2889 sectordir = sectordir1;
2894 sectordir = getSectorDir(p2d, 2);
2898 sector = loadSectorMeta(sectordir, loadlayout != 2);
2900 catch(InvalidFilenameException &e)
2904 catch(FileNotGoodException &e)
2908 catch(std::exception &e)
2917 bool ServerMap::loadSectorFull(v2s16 p2d)
2919 DSTACK(__FUNCTION_NAME);
2921 MapSector *sector = NULL;
2923 // The directory layout we're going to load from.
2924 // 1 - original sectors/xxxxzzzz/
2925 // 2 - new sectors2/xxx/zzz/
2926 // If we load from anything but the latest structure, we will
2927 // immediately save to the new one, and remove the old.
2929 std::string sectordir1 = getSectorDir(p2d, 1);
2930 std::string sectordir;
2931 if(fs::PathExists(sectordir1))
2933 sectordir = sectordir1;
2938 sectordir = getSectorDir(p2d, 2);
2942 sector = loadSectorMeta(sectordir, loadlayout != 2);
2944 catch(InvalidFilenameException &e)
2948 catch(FileNotGoodException &e)
2952 catch(std::exception &e)
2960 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2962 std::vector<fs::DirListNode>::iterator i2;
2963 for(i2=list2.begin(); i2!=list2.end(); i2++)
2969 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
2971 catch(InvalidFilenameException &e)
2973 // This catches unknown crap in directory
2979 dstream<<"Sector converted to new layout - deleting "<<
2980 sectordir1<<std::endl;
2981 fs::RecursiveDelete(sectordir1);
2988 void ServerMap::saveBlock(MapBlock *block)
2990 DSTACK(__FUNCTION_NAME);
2992 Dummy blocks are not written
2994 if(block->isDummy())
2996 /*v3s16 p = block->getPos();
2997 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2998 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3002 // Format used for writing
3003 u8 version = SER_FMT_VER_HIGHEST;
3005 v3s16 p3d = block->getPos();
3007 v2s16 p2d(p3d.X, p3d.Z);
3008 std::string sectordir = getSectorDir(p2d);
3010 createDirs(sectordir);
3012 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3013 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3014 if(o.good() == false)
3015 throw FileNotGoodException("Cannot open block data");
3018 [0] u8 serialization version
3021 o.write((char*)&version, 1);
3024 block->serialize(o, version);
3026 // Write extra data stored on disk
3027 block->serializeDiskExtra(o, version);
3029 // We just wrote it to the disk so clear modified flag
3030 block->resetModified();
3033 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3035 DSTACK(__FUNCTION_NAME);
3037 std::string fullpath = sectordir+"/"+blockfile;
3040 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3041 if(is.good() == false)
3042 throw FileNotGoodException("Cannot open block file");
3044 v3s16 p3d = getBlockPos(sectordir, blockfile);
3045 v2s16 p2d(p3d.X, p3d.Z);
3047 assert(sector->getPos() == p2d);
3049 u8 version = SER_FMT_VER_INVALID;
3050 is.read((char*)&version, 1);
3053 throw SerializationError("ServerMap::loadBlock(): Failed"
3054 " to read MapBlock version");
3056 /*u32 block_size = MapBlock::serializedLength(version);
3057 SharedBuffer<u8> data(block_size);
3058 is.read((char*)*data, block_size);*/
3060 // This will always return a sector because we're the server
3061 //MapSector *sector = emergeSector(p2d);
3063 MapBlock *block = NULL;
3064 bool created_new = false;
3066 block = sector->getBlockNoCreate(p3d.Y);
3068 catch(InvalidPositionException &e)
3070 block = sector->createBlankBlockNoInsert(p3d.Y);
3075 block->deSerialize(is, version);
3077 // Read extra data stored on disk
3078 block->deSerializeDiskExtra(is, version);
3080 // If it's a new block, insert it to the map
3082 sector->insertBlock(block);
3085 Save blocks loaded in old format in new format
3088 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3093 // We just loaded it from the disk, so it's up-to-date.
3094 block->resetModified();
3097 catch(SerializationError &e)
3099 dstream<<"WARNING: Invalid block data on disk "
3100 <<"fullpath="<<fullpath
3101 <<" (SerializationError). "
3102 <<"what()="<<e.what()
3104 //" Ignoring. A new one will be generated.
3107 // TODO: Backup file; name is in fullpath.
3111 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3113 DSTACK(__FUNCTION_NAME);
3115 v2s16 p2d(blockpos.X, blockpos.Z);
3117 // The directory layout we're going to load from.
3118 // 1 - original sectors/xxxxzzzz/
3119 // 2 - new sectors2/xxx/zzz/
3120 // If we load from anything but the latest structure, we will
3121 // immediately save to the new one, and remove the old.
3123 std::string sectordir1 = getSectorDir(p2d, 1);
3124 std::string sectordir;
3125 if(fs::PathExists(sectordir1))
3127 sectordir = sectordir1;
3132 sectordir = getSectorDir(p2d, 2);
3136 Make sure sector is loaded
3138 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3142 sector = loadSectorMeta(sectordir, loadlayout != 2);
3144 catch(InvalidFilenameException &e)
3148 catch(FileNotGoodException &e)
3152 catch(std::exception &e)
3159 Make sure file exists
3162 std::string blockfilename = getBlockFilename(blockpos);
3163 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3169 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3170 return getBlockNoCreateNoEx(blockpos);
3173 void ServerMap::PrintInfo(std::ostream &out)
3184 ClientMap::ClientMap(
3186 MapDrawControl &control,
3187 scene::ISceneNode* parent,
3188 scene::ISceneManager* mgr,
3192 scene::ISceneNode(parent, mgr, id),
3195 m_camera_position(0,0,0),
3196 m_camera_direction(0,0,1)
3198 m_camera_mutex.Init();
3199 assert(m_camera_mutex.IsInitialized());
3201 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3202 BS*1000000,BS*1000000,BS*1000000);
3205 ClientMap::~ClientMap()
3207 /*JMutexAutoLock lock(mesh_mutex);
3216 MapSector * ClientMap::emergeSector(v2s16 p2d)
3218 DSTACK(__FUNCTION_NAME);
3219 // Check that it doesn't exist already
3221 return getSectorNoGenerate(p2d);
3223 catch(InvalidPositionException &e)
3228 ClientMapSector *sector = new ClientMapSector(this, p2d);
3231 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3232 m_sectors.insert(p2d, sector);
3238 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3240 DSTACK(__FUNCTION_NAME);
3241 ClientMapSector *sector = NULL;
3243 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3245 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3249 sector = (ClientMapSector*)n->getValue();
3250 assert(sector->getId() == MAPSECTOR_CLIENT);
3254 sector = new ClientMapSector(this, p2d);
3256 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3257 m_sectors.insert(p2d, sector);
3261 sector->deSerialize(is);
3264 void ClientMap::OnRegisterSceneNode()
3268 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3269 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3272 ISceneNode::OnRegisterSceneNode();
3275 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3277 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3278 DSTACK(__FUNCTION_NAME);
3280 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3283 This is called two times per frame, reset on the non-transparent one
3285 if(pass == scene::ESNRP_SOLID)
3287 m_last_drawn_sectors.clear();
3291 Get time for measuring timeout.
3293 Measuring time is very useful for long delays when the
3294 machine is swapping a lot.
3296 int time1 = time(0);
3298 //u32 daynight_ratio = m_client->getDayNightRatio();
3300 m_camera_mutex.Lock();
3301 v3f camera_position = m_camera_position;
3302 v3f camera_direction = m_camera_direction;
3303 m_camera_mutex.Unlock();
3306 Get all blocks and draw all visible ones
3309 v3s16 cam_pos_nodes(
3310 camera_position.X / BS,
3311 camera_position.Y / BS,
3312 camera_position.Z / BS);
3314 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3316 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3317 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3319 // Take a fair amount as we will be dropping more out later
3321 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3322 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3323 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3325 p_nodes_max.X / MAP_BLOCKSIZE,
3326 p_nodes_max.Y / MAP_BLOCKSIZE,
3327 p_nodes_max.Z / MAP_BLOCKSIZE);
3329 u32 vertex_count = 0;
3331 // For limiting number of mesh updates per frame
3332 u32 mesh_update_count = 0;
3334 u32 blocks_would_have_drawn = 0;
3335 u32 blocks_drawn = 0;
3337 int timecheck_counter = 0;
3338 core::map<v2s16, MapSector*>::Iterator si;
3339 si = m_sectors.getIterator();
3340 for(; si.atEnd() == false; si++)
3343 timecheck_counter++;
3344 if(timecheck_counter > 50)
3346 timecheck_counter = 0;
3347 int time2 = time(0);
3348 if(time2 > time1 + 4)
3350 dstream<<"ClientMap::renderMap(): "
3351 "Rendering takes ages, returning."
3358 MapSector *sector = si.getNode()->getValue();
3359 v2s16 sp = sector->getPos();
3361 if(m_control.range_all == false)
3363 if(sp.X < p_blocks_min.X
3364 || sp.X > p_blocks_max.X
3365 || sp.Y < p_blocks_min.Z
3366 || sp.Y > p_blocks_max.Z)
3370 core::list< MapBlock * > sectorblocks;
3371 sector->getBlocks(sectorblocks);
3377 u32 sector_blocks_drawn = 0;
3379 core::list< MapBlock * >::Iterator i;
3380 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3382 MapBlock *block = *i;
3385 Compare block position to camera position, skip
3386 if not seen on display
3389 float range = 100000 * BS;
3390 if(m_control.range_all == false)
3391 range = m_control.wanted_range * BS;
3394 if(isBlockInSight(block->getPos(), camera_position,
3395 camera_direction, range, &d) == false)
3400 // This is ugly (spherical distance limit?)
3401 /*if(m_control.range_all == false &&
3402 d - 0.5*BS*MAP_BLOCKSIZE > range)
3407 Update expired mesh (used for day/night change)
3409 It doesn't work exactly like it should now with the
3410 tasked mesh update but whatever.
3413 bool mesh_expired = false;
3416 JMutexAutoLock lock(block->mesh_mutex);
3418 mesh_expired = block->getMeshExpired();
3420 // Mesh has not been expired and there is no mesh:
3421 // block has no content
3422 if(block->mesh == NULL && mesh_expired == false)
3426 f32 faraway = BS*50;
3427 //f32 faraway = m_control.wanted_range * BS;
3430 This has to be done with the mesh_mutex unlocked
3432 // Pretty random but this should work somewhat nicely
3433 if(mesh_expired && (
3434 (mesh_update_count < 3
3435 && (d < faraway || mesh_update_count < 2)
3438 (m_control.range_all && mesh_update_count < 20)
3441 /*if(mesh_expired && mesh_update_count < 6
3442 && (d < faraway || mesh_update_count < 3))*/
3444 mesh_update_count++;
3446 // Mesh has been expired: generate new mesh
3447 //block->updateMesh(daynight_ratio);
3448 m_client->addUpdateMeshTask(block->getPos());
3450 mesh_expired = false;
3455 Draw the faces of the block
3458 JMutexAutoLock lock(block->mesh_mutex);
3460 scene::SMesh *mesh = block->mesh;
3465 blocks_would_have_drawn++;
3466 if(blocks_drawn >= m_control.wanted_max_blocks
3467 && m_control.range_all == false
3468 && d > m_control.wanted_min_range * BS)
3472 sector_blocks_drawn++;
3474 u32 c = mesh->getMeshBufferCount();
3476 for(u32 i=0; i<c; i++)
3478 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3479 const video::SMaterial& material = buf->getMaterial();
3480 video::IMaterialRenderer* rnd =
3481 driver->getMaterialRenderer(material.MaterialType);
3482 bool transparent = (rnd && rnd->isTransparent());
3483 // Render transparent on transparent pass and likewise.
3484 if(transparent == is_transparent_pass)
3487 This *shouldn't* hurt too much because Irrlicht
3488 doesn't change opengl textures if the old
3489 material is set again.
3491 driver->setMaterial(buf->getMaterial());
3492 driver->drawMeshBuffer(buf);
3493 vertex_count += buf->getVertexCount();
3497 } // foreach sectorblocks
3499 if(sector_blocks_drawn != 0)
3501 m_last_drawn_sectors[sp] = true;
3505 m_control.blocks_drawn = blocks_drawn;
3506 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3508 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3509 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3512 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3513 core::map<v3s16, MapBlock*> *affected_blocks)
3515 bool changed = false;
3517 Add it to all blocks touching it
3520 v3s16(0,0,0), // this
3521 v3s16(0,0,1), // back
3522 v3s16(0,1,0), // top
3523 v3s16(1,0,0), // right
3524 v3s16(0,0,-1), // front
3525 v3s16(0,-1,0), // bottom
3526 v3s16(-1,0,0), // left
3528 for(u16 i=0; i<7; i++)
3530 v3s16 p2 = p + dirs[i];
3531 // Block position of neighbor (or requested) node
3532 v3s16 blockpos = getNodeBlockPos(p2);
3533 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3534 if(blockref == NULL)
3536 // Relative position of requested node
3537 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3538 if(blockref->setTempMod(relpos, mod))
3543 if(changed && affected_blocks!=NULL)
3545 for(u16 i=0; i<7; i++)
3547 v3s16 p2 = p + dirs[i];
3548 // Block position of neighbor (or requested) node
3549 v3s16 blockpos = getNodeBlockPos(p2);
3550 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3551 if(blockref == NULL)
3553 affected_blocks->insert(blockpos, blockref);
3559 bool ClientMap::clearTempMod(v3s16 p,
3560 core::map<v3s16, MapBlock*> *affected_blocks)
3562 bool changed = false;
3564 v3s16(0,0,0), // this
3565 v3s16(0,0,1), // back
3566 v3s16(0,1,0), // top
3567 v3s16(1,0,0), // right
3568 v3s16(0,0,-1), // front
3569 v3s16(0,-1,0), // bottom
3570 v3s16(-1,0,0), // left
3572 for(u16 i=0; i<7; i++)
3574 v3s16 p2 = p + dirs[i];
3575 // Block position of neighbor (or requested) node
3576 v3s16 blockpos = getNodeBlockPos(p2);
3577 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3578 if(blockref == NULL)
3580 // Relative position of requested node
3581 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3582 if(blockref->clearTempMod(relpos))
3587 if(changed && affected_blocks!=NULL)
3589 for(u16 i=0; i<7; i++)
3591 v3s16 p2 = p + dirs[i];
3592 // Block position of neighbor (or requested) node
3593 v3s16 blockpos = getNodeBlockPos(p2);
3594 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3595 if(blockref == NULL)
3597 affected_blocks->insert(blockpos, blockref);
3603 void ClientMap::expireMeshes(bool only_daynight_diffed)
3605 TimeTaker timer("expireMeshes()");
3607 core::map<v2s16, MapSector*>::Iterator si;
3608 si = m_sectors.getIterator();
3609 for(; si.atEnd() == false; si++)
3611 MapSector *sector = si.getNode()->getValue();
3613 core::list< MapBlock * > sectorblocks;
3614 sector->getBlocks(sectorblocks);
3616 core::list< MapBlock * >::Iterator i;
3617 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3619 MapBlock *block = *i;
3621 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3627 JMutexAutoLock lock(block->mesh_mutex);
3628 if(block->mesh != NULL)
3630 /*block->mesh->drop();
3631 block->mesh = NULL;*/
3632 block->setMeshExpired(true);
3639 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3641 assert(mapType() == MAPTYPE_CLIENT);
3644 v3s16 p = blockpos + v3s16(0,0,0);
3645 MapBlock *b = getBlockNoCreate(p);
3646 b->updateMesh(daynight_ratio);
3647 //b->setMeshExpired(true);
3649 catch(InvalidPositionException &e){}
3652 v3s16 p = blockpos + v3s16(-1,0,0);
3653 MapBlock *b = getBlockNoCreate(p);
3654 b->updateMesh(daynight_ratio);
3655 //b->setMeshExpired(true);
3657 catch(InvalidPositionException &e){}
3659 v3s16 p = blockpos + v3s16(0,-1,0);
3660 MapBlock *b = getBlockNoCreate(p);
3661 b->updateMesh(daynight_ratio);
3662 //b->setMeshExpired(true);
3664 catch(InvalidPositionException &e){}
3666 v3s16 p = blockpos + v3s16(0,0,-1);
3667 MapBlock *b = getBlockNoCreate(p);
3668 b->updateMesh(daynight_ratio);
3669 //b->setMeshExpired(true);
3671 catch(InvalidPositionException &e){}
3676 Update mesh of block in which the node is, and if the node is at the
3677 leading edge, update the appropriate leading blocks too.
3679 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3687 v3s16 blockposes[4];
3688 for(u32 i=0; i<4; i++)
3690 v3s16 np = nodepos + dirs[i];
3691 blockposes[i] = getNodeBlockPos(np);
3692 // Don't update mesh of block if it has been done already
3693 bool already_updated = false;
3694 for(u32 j=0; j<i; j++)
3696 if(blockposes[j] == blockposes[i])
3698 already_updated = true;
3705 MapBlock *b = getBlockNoCreate(blockposes[i]);
3706 b->updateMesh(daynight_ratio);
3711 void ClientMap::PrintInfo(std::ostream &out)
3722 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3727 MapVoxelManipulator::~MapVoxelManipulator()
3729 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3733 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3735 TimeTaker timer1("emerge", &emerge_time);
3737 // Units of these are MapBlocks
3738 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3739 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3741 VoxelArea block_area_nodes
3742 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3744 addArea(block_area_nodes);
3746 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3747 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3748 for(s32 x=p_min.X; x<=p_max.X; x++)
3751 core::map<v3s16, bool>::Node *n;
3752 n = m_loaded_blocks.find(p);
3756 bool block_data_inexistent = false;
3759 TimeTaker timer1("emerge load", &emerge_load_time);
3761 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3762 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3765 dstream<<std::endl;*/
3767 MapBlock *block = m_map->getBlockNoCreate(p);
3768 if(block->isDummy())
3769 block_data_inexistent = true;
3771 block->copyTo(*this);
3773 catch(InvalidPositionException &e)
3775 block_data_inexistent = true;
3778 if(block_data_inexistent)
3780 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3781 // Fill with VOXELFLAG_INEXISTENT
3782 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3783 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3785 s32 i = m_area.index(a.MinEdge.X,y,z);
3786 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3790 m_loaded_blocks.insert(p, !block_data_inexistent);
3793 //dstream<<"emerge done"<<std::endl;
3797 SUGG: Add an option to only update eg. water and air nodes.
3798 This will make it interfere less with important stuff if
3801 void MapVoxelManipulator::blitBack
3802 (core::map<v3s16, MapBlock*> & modified_blocks)
3804 if(m_area.getExtent() == v3s16(0,0,0))
3807 //TimeTaker timer1("blitBack");
3809 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3810 <<m_loaded_blocks.size()<<std::endl;*/
3813 Initialize block cache
3815 v3s16 blockpos_last;
3816 MapBlock *block = NULL;
3817 bool block_checked_in_modified = false;
3819 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3820 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3821 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3825 u8 f = m_flags[m_area.index(p)];
3826 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3829 MapNode &n = m_data[m_area.index(p)];
3831 v3s16 blockpos = getNodeBlockPos(p);
3836 if(block == NULL || blockpos != blockpos_last){
3837 block = m_map->getBlockNoCreate(blockpos);
3838 blockpos_last = blockpos;
3839 block_checked_in_modified = false;
3842 // Calculate relative position in block
3843 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3845 // Don't continue if nothing has changed here
3846 if(block->getNode(relpos) == n)
3849 //m_map->setNode(m_area.MinEdge + p, n);
3850 block->setNode(relpos, n);
3853 Make sure block is in modified_blocks
3855 if(block_checked_in_modified == false)
3857 modified_blocks[blockpos] = block;
3858 block_checked_in_modified = true;
3861 catch(InvalidPositionException &e)
3867 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3868 MapVoxelManipulator(map),
3869 m_create_area(false)
3873 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3877 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3879 // Just create the area so that it can be pointed to
3880 VoxelManipulator::emerge(a, caller_id);
3883 void ManualMapVoxelManipulator::initialEmerge(
3884 v3s16 blockpos_min, v3s16 blockpos_max)
3886 TimeTaker timer1("initialEmerge", &emerge_time);
3888 // Units of these are MapBlocks
3889 v3s16 p_min = blockpos_min;
3890 v3s16 p_max = blockpos_max;
3892 VoxelArea block_area_nodes
3893 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3895 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3898 dstream<<"initialEmerge: area: ";
3899 block_area_nodes.print(dstream);
3900 dstream<<" ("<<size_MB<<"MB)";
3904 addArea(block_area_nodes);
3906 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3907 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3908 for(s32 x=p_min.X; x<=p_max.X; x++)
3911 core::map<v3s16, bool>::Node *n;
3912 n = m_loaded_blocks.find(p);
3916 bool block_data_inexistent = false;
3919 TimeTaker timer1("emerge load", &emerge_load_time);
3921 MapBlock *block = m_map->getBlockNoCreate(p);
3922 if(block->isDummy())
3923 block_data_inexistent = true;
3925 block->copyTo(*this);
3927 catch(InvalidPositionException &e)
3929 block_data_inexistent = true;
3932 if(block_data_inexistent)
3935 Mark area inexistent
3937 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3938 // Fill with VOXELFLAG_INEXISTENT
3939 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3940 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3942 s32 i = m_area.index(a.MinEdge.X,y,z);
3943 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3947 m_loaded_blocks.insert(p, !block_data_inexistent);
3951 void ManualMapVoxelManipulator::blitBackAll(
3952 core::map<v3s16, MapBlock*> * modified_blocks)
3954 if(m_area.getExtent() == v3s16(0,0,0))
3958 Copy data of all blocks
3960 for(core::map<v3s16, bool>::Iterator
3961 i = m_loaded_blocks.getIterator();
3962 i.atEnd() == false; i++)
3964 bool existed = i.getNode()->getValue();
3965 if(existed == false)
3967 v3s16 p = i.getNode()->getKey();
3968 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3971 dstream<<"WARNING: "<<__FUNCTION_NAME
3972 <<": got NULL block "
3973 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3978 block->copyFrom(*this);
3981 modified_blocks->insert(p, block);