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(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.setMap(this);
2029 //TimeTaker timer("initBlockMake() initialEmerge");
2030 data->vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2033 // Data is ready now.
2036 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
2037 core::map<v3s16, MapBlock*> &changed_blocks)
2039 v3s16 blockpos = data->blockpos;
2040 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2041 <<blockpos.Z<<")"<<std::endl;*/
2045 dstream<<"finishBlockMake(): no-op"<<std::endl;
2049 /*dstream<<"Resulting vmanip:"<<std::endl;
2050 data->vmanip.print(dstream);*/
2053 Blit generated stuff to map
2054 NOTE: blitBackAll adds nearly everything to changed_blocks
2058 //TimeTaker timer("finishBlockMake() blitBackAll");
2059 data->vmanip.blitBackAll(&changed_blocks);
2062 dstream<<"finishBlockMake: changed_blocks.size()="
2063 <<changed_blocks.size()<<std::endl;
2066 Copy transforming liquid information
2068 while(data->transforming_liquid.size() > 0)
2070 v3s16 p = data->transforming_liquid.pop_front();
2071 m_transforming_liquid.push_back(p);
2077 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2081 Set is_underground flag for lighting with sunlight
2084 block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2087 Add sunlight to central block.
2088 This makes in-dark-spawning monsters to not flood the whole thing.
2089 Do not spread the light, though.
2091 /*core::map<v3s16, bool> light_sources;
2092 bool black_air_left = false;
2093 block->propagateSunlight(light_sources, true, &black_air_left);*/
2096 NOTE: Lighting and object adding shouldn't really be here, but
2097 lighting is a bit tricky to move properly to makeBlock.
2098 TODO: Do this the right way anyway.
2105 core::map<v3s16, MapBlock*> lighting_update_blocks;
2107 lighting_update_blocks.insert(block->getPos(), block);
2109 // All modified blocks
2110 for(core::map<v3s16, MapBlock*>::Iterator
2111 i = changed_blocks.getIterator();
2112 i.atEnd() == false; i++)
2114 lighting_update_blocks.insert(i.getNode()->getKey(),
2115 i.getNode()->getValue());
2118 updateLighting(lighting_update_blocks, changed_blocks);
2121 Add random objects to block
2123 mapgen::add_random_objects(block);
2126 Go through changed blocks
2128 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2129 i.atEnd() == false; i++)
2131 MapBlock *block = i.getNode()->getValue();
2134 Update day/night difference cache of the MapBlocks
2136 block->updateDayNightDiff();
2138 Set block as modified
2140 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2144 Set central block as generated
2146 block->setGenerated(true);
2149 Save changed parts of map
2150 NOTE: Will be saved later.
2154 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2155 <<blockpos.Z<<")"<<std::endl;*/
2160 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2162 DSTACKF("%s: p2d=(%d,%d)",
2167 Check if it exists already in memory
2169 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2174 Try to load it from disk (with blocks)
2176 //if(loadSectorFull(p2d) == true)
2179 Try to load metadata from disk
2181 if(loadSectorMeta(p2d) == true)
2183 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2186 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2187 throw InvalidPositionException("");
2193 Do not create over-limit
2195 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2196 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2197 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2198 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2199 throw InvalidPositionException("createSector(): pos. over limit");
2202 Generate blank sector
2205 sector = new ServerMapSector(this, p2d);
2207 // Sector position on map in nodes
2208 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2213 m_sectors.insert(p2d, sector);
2219 This is a quick-hand function for calling makeBlock().
2221 MapBlock * ServerMap::generateBlock(
2223 core::map<v3s16, MapBlock*> &modified_blocks
2226 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2228 /*dstream<<"generateBlock(): "
2229 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2232 TimeTaker timer("generateBlock");
2234 //MapBlock *block = original_dummy;
2236 v2s16 p2d(p.X, p.Z);
2237 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2240 Do not generate over-limit
2242 if(blockpos_over_limit(p))
2244 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2245 throw InvalidPositionException("generateBlock(): pos. over limit");
2249 Create block make data
2252 initBlockMake(&data, p);
2258 TimeTaker t("mapgen::make_block()");
2259 mapgen::make_block(&data);
2263 Blit data back on map, update lighting, add mobs and whatever this does
2265 finishBlockMake(&data, modified_blocks);
2270 MapBlock *block = getBlockNoCreateNoEx(p);
2277 bool erroneus_content = false;
2278 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2279 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2280 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2283 MapNode n = block->getNode(p);
2284 if(n.d == CONTENT_IGNORE)
2286 dstream<<"CONTENT_IGNORE at "
2287 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2289 erroneus_content = true;
2293 if(erroneus_content)
2301 Generate a completely empty block
2303 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2304 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2306 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2312 n.d = CONTENT_STONE;
2313 block->setNode(v3s16(x0,y0,z0), n);
2321 MapBlock * ServerMap::createBlock(v3s16 p)
2323 DSTACKF("%s: p=(%d,%d,%d)",
2324 __FUNCTION_NAME, p.X, p.Y, p.Z);
2327 Do not create over-limit
2329 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2330 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2331 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2332 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2333 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2334 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2335 throw InvalidPositionException("createBlock(): pos. over limit");
2337 v2s16 p2d(p.X, p.Z);
2340 This will create or load a sector if not found in memory.
2341 If block exists on disk, it will be loaded.
2343 NOTE: On old save formats, this will be slow, as it generates
2344 lighting on blocks for them.
2346 ServerMapSector *sector;
2348 sector = (ServerMapSector*)createSector(p2d);
2349 assert(sector->getId() == MAPSECTOR_SERVER);
2351 catch(InvalidPositionException &e)
2353 dstream<<"createBlock: createSector() failed"<<std::endl;
2357 NOTE: This should not be done, or at least the exception
2358 should not be passed on as std::exception, because it
2359 won't be catched at all.
2361 /*catch(std::exception &e)
2363 dstream<<"createBlock: createSector() failed: "
2364 <<e.what()<<std::endl;
2369 Try to get a block from the sector
2372 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2375 if(block->isDummy())
2380 block = sector->createBlankBlock(block_y);
2385 MapBlock * ServerMap::emergeBlock(
2387 bool only_from_disk,
2388 core::map<v3s16, MapBlock*> &changed_blocks,
2389 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
2392 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
2394 p.X, p.Y, p.Z, only_from_disk);
2396 // This has to be redone or removed
2404 Do not generate over-limit
2406 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2407 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2408 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2409 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2410 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2411 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2412 throw InvalidPositionException("emergeBlock(): pos. over limit");
2414 v2s16 p2d(p.X, p.Z);
2417 This will create or load a sector if not found in memory.
2418 If block exists on disk, it will be loaded.
2420 ServerMapSector *sector;
2422 sector = createSector(p2d);
2423 //sector = emergeSector(p2d, changed_blocks);
2425 catch(InvalidPositionException &e)
2427 dstream<<"emergeBlock: createSector() failed: "
2428 <<e.what()<<std::endl;
2429 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2431 <<"You could try to delete it."<<std::endl;
2434 catch(VersionMismatchException &e)
2436 dstream<<"emergeBlock: createSector() failed: "
2437 <<e.what()<<std::endl;
2438 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2440 <<"You could try to delete it."<<std::endl;
2445 Try to get a block from the sector
2448 bool does_not_exist = false;
2449 bool lighting_expired = false;
2450 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2452 // If not found, try loading from disk
2455 block = loadBlock(p);
2461 does_not_exist = true;
2463 else if(block->isDummy() == true)
2465 does_not_exist = true;
2467 else if(block->getLightingExpired())
2469 lighting_expired = true;
2474 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2479 If block was not found on disk and not going to generate a
2480 new one, make sure there is a dummy block in place.
2482 if(only_from_disk && (does_not_exist || lighting_expired))
2484 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2488 // Create dummy block
2489 block = new MapBlock(this, p, true);
2491 // Add block to sector
2492 sector->insertBlock(block);
2498 //dstream<<"Not found on disk, generating."<<std::endl;
2500 //TimeTaker("emergeBlock() generate");
2502 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2505 If the block doesn't exist, generate the block.
2509 block = generateBlock(p, block, sector, changed_blocks,
2510 lighting_invalidated_blocks);
2513 if(lighting_expired)
2515 lighting_invalidated_blocks.insert(p, block);
2520 Initially update sunlight
2523 core::map<v3s16, bool> light_sources;
2524 bool black_air_left = false;
2525 bool bottom_invalid =
2526 block->propagateSunlight(light_sources, true,
2529 // If sunlight didn't reach everywhere and part of block is
2530 // above ground, lighting has to be properly updated
2531 //if(black_air_left && some_part_underground)
2534 lighting_invalidated_blocks[block->getPos()] = block;
2539 lighting_invalidated_blocks[block->getPos()] = block;
2548 s16 ServerMap::findGroundLevel(v2s16 p2d)
2552 Uh, just do something random...
2554 // Find existing map from top to down
2557 v3s16 p(p2d.X, max, p2d.Y);
2558 for(; p.Y>min; p.Y--)
2560 MapNode n = getNodeNoEx(p);
2561 if(n.d != CONTENT_IGNORE)
2566 // If this node is not air, go to plan b
2567 if(getNodeNoEx(p).d != CONTENT_AIR)
2569 // Search existing walkable and return it
2570 for(; p.Y>min; p.Y--)
2572 MapNode n = getNodeNoEx(p);
2573 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
2582 Determine from map generator noise functions
2585 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2588 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2589 //return (s16)level;
2592 void ServerMap::createDirs(std::string path)
2594 if(fs::CreateAllDirs(path) == false)
2596 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2597 <<"\""<<path<<"\""<<std::endl;
2598 throw BaseException("ServerMap failed to create directory");
2602 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2608 snprintf(cc, 9, "%.4x%.4x",
2609 (unsigned int)pos.X&0xffff,
2610 (unsigned int)pos.Y&0xffff);
2612 return m_savedir + "/sectors/" + cc;
2614 snprintf(cc, 9, "%.3x/%.3x",
2615 (unsigned int)pos.X&0xfff,
2616 (unsigned int)pos.Y&0xfff);
2618 return m_savedir + "/sectors2/" + cc;
2624 v2s16 ServerMap::getSectorPos(std::string dirname)
2628 size_t spos = dirname.rfind('/') + 1;
2629 assert(spos != std::string::npos);
2630 if(dirname.size() - spos == 8)
2633 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2635 else if(dirname.size() - spos == 3)
2638 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2639 // Sign-extend the 12 bit values up to 16 bits...
2640 if(x&0x800) x|=0xF000;
2641 if(y&0x800) y|=0xF000;
2648 v2s16 pos((s16)x, (s16)y);
2652 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2654 v2s16 p2d = getSectorPos(sectordir);
2656 if(blockfile.size() != 4){
2657 throw InvalidFilenameException("Invalid block filename");
2660 int r = sscanf(blockfile.c_str(), "%4x", &y);
2662 throw InvalidFilenameException("Invalid block filename");
2663 return v3s16(p2d.X, y, p2d.Y);
2666 std::string ServerMap::getBlockFilename(v3s16 p)
2669 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2673 void ServerMap::save(bool only_changed)
2675 DSTACK(__FUNCTION_NAME);
2676 if(m_map_saving_enabled == false)
2678 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2682 if(only_changed == false)
2683 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2686 if(only_changed == false || m_map_metadata_changed)
2691 u32 sector_meta_count = 0;
2692 u32 block_count = 0;
2693 u32 block_count_all = 0; // Number of blocks in memory
2695 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2696 for(; i.atEnd() == false; i++)
2698 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2699 assert(sector->getId() == MAPSECTOR_SERVER);
2701 if(sector->differs_from_disk || only_changed == false)
2703 saveSectorMeta(sector);
2704 sector_meta_count++;
2706 core::list<MapBlock*> blocks;
2707 sector->getBlocks(blocks);
2708 core::list<MapBlock*>::Iterator j;
2709 for(j=blocks.begin(); j!=blocks.end(); j++)
2711 MapBlock *block = *j;
2715 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2716 || only_changed == false)
2721 /*dstream<<"ServerMap: Written block ("
2722 <<block->getPos().X<<","
2723 <<block->getPos().Y<<","
2724 <<block->getPos().Z<<")"
2731 Only print if something happened or saved whole map
2733 if(only_changed == false || sector_meta_count != 0
2734 || block_count != 0)
2736 dstream<<DTIME<<"ServerMap: Written: "
2737 <<sector_meta_count<<" sector metadata files, "
2738 <<block_count<<" block files"
2739 <<", "<<block_count_all<<" blocks in memory."
2744 void ServerMap::saveMapMeta()
2746 DSTACK(__FUNCTION_NAME);
2748 dstream<<"INFO: ServerMap::saveMapMeta(): "
2752 createDirs(m_savedir);
2754 std::string fullpath = m_savedir + "/map_meta.txt";
2755 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2756 if(os.good() == false)
2758 dstream<<"ERROR: ServerMap::saveMapMeta(): "
2759 <<"could not open"<<fullpath<<std::endl;
2760 throw FileNotGoodException("Cannot open chunk metadata");
2764 params.setU64("seed", m_seed);
2766 params.writeLines(os);
2768 os<<"[end_of_params]\n";
2770 m_map_metadata_changed = false;
2773 void ServerMap::loadMapMeta()
2775 DSTACK(__FUNCTION_NAME);
2777 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
2780 std::string fullpath = m_savedir + "/map_meta.txt";
2781 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2782 if(is.good() == false)
2784 dstream<<"ERROR: ServerMap::loadMapMeta(): "
2785 <<"could not open"<<fullpath<<std::endl;
2786 throw FileNotGoodException("Cannot open map metadata");
2794 throw SerializationError
2795 ("ServerMap::loadMapMeta(): [end_of_params] not found");
2797 std::getline(is, line);
2798 std::string trimmedline = trim(line);
2799 if(trimmedline == "[end_of_params]")
2801 params.parseConfigLine(line);
2804 m_seed = params.getU64("seed");
2806 dstream<<"INFO: ServerMap::loadMapMeta(): "
2811 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2813 DSTACK(__FUNCTION_NAME);
2814 // Format used for writing
2815 u8 version = SER_FMT_VER_HIGHEST;
2817 v2s16 pos = sector->getPos();
2818 std::string dir = getSectorDir(pos);
2821 std::string fullpath = dir + "/meta";
2822 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2823 if(o.good() == false)
2824 throw FileNotGoodException("Cannot open sector metafile");
2826 sector->serialize(o, version);
2828 sector->differs_from_disk = false;
2831 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
2833 DSTACK(__FUNCTION_NAME);
2835 v2s16 p2d = getSectorPos(sectordir);
2837 ServerMapSector *sector = NULL;
2839 std::string fullpath = sectordir + "/meta";
2840 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2841 if(is.good() == false)
2843 // If the directory exists anyway, it probably is in some old
2844 // format. Just go ahead and create the sector.
2845 if(fs::PathExists(sectordir))
2847 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
2848 <<fullpath<<" doesn't exist but directory does."
2849 <<" Continuing with a sector with no metadata."
2851 sector = new ServerMapSector(this, p2d);
2852 m_sectors.insert(p2d, sector);
2856 throw FileNotGoodException("Cannot open sector metafile");
2861 sector = ServerMapSector::deSerialize
2862 (is, this, p2d, m_sectors);
2864 saveSectorMeta(sector);
2867 sector->differs_from_disk = false;
2872 bool ServerMap::loadSectorMeta(v2s16 p2d)
2874 DSTACK(__FUNCTION_NAME);
2876 MapSector *sector = NULL;
2878 // The directory layout we're going to load from.
2879 // 1 - original sectors/xxxxzzzz/
2880 // 2 - new sectors2/xxx/zzz/
2881 // If we load from anything but the latest structure, we will
2882 // immediately save to the new one, and remove the old.
2884 std::string sectordir1 = getSectorDir(p2d, 1);
2885 std::string sectordir;
2886 if(fs::PathExists(sectordir1))
2888 sectordir = sectordir1;
2893 sectordir = getSectorDir(p2d, 2);
2897 sector = loadSectorMeta(sectordir, loadlayout != 2);
2899 catch(InvalidFilenameException &e)
2903 catch(FileNotGoodException &e)
2907 catch(std::exception &e)
2916 bool ServerMap::loadSectorFull(v2s16 p2d)
2918 DSTACK(__FUNCTION_NAME);
2920 MapSector *sector = NULL;
2922 // The directory layout we're going to load from.
2923 // 1 - original sectors/xxxxzzzz/
2924 // 2 - new sectors2/xxx/zzz/
2925 // If we load from anything but the latest structure, we will
2926 // immediately save to the new one, and remove the old.
2928 std::string sectordir1 = getSectorDir(p2d, 1);
2929 std::string sectordir;
2930 if(fs::PathExists(sectordir1))
2932 sectordir = sectordir1;
2937 sectordir = getSectorDir(p2d, 2);
2941 sector = loadSectorMeta(sectordir, loadlayout != 2);
2943 catch(InvalidFilenameException &e)
2947 catch(FileNotGoodException &e)
2951 catch(std::exception &e)
2959 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2961 std::vector<fs::DirListNode>::iterator i2;
2962 for(i2=list2.begin(); i2!=list2.end(); i2++)
2968 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
2970 catch(InvalidFilenameException &e)
2972 // This catches unknown crap in directory
2978 dstream<<"Sector converted to new layout - deleting "<<
2979 sectordir1<<std::endl;
2980 fs::RecursiveDelete(sectordir1);
2987 void ServerMap::saveBlock(MapBlock *block)
2989 DSTACK(__FUNCTION_NAME);
2991 Dummy blocks are not written
2993 if(block->isDummy())
2995 /*v3s16 p = block->getPos();
2996 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2997 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3001 // Format used for writing
3002 u8 version = SER_FMT_VER_HIGHEST;
3004 v3s16 p3d = block->getPos();
3006 v2s16 p2d(p3d.X, p3d.Z);
3007 std::string sectordir = getSectorDir(p2d);
3009 createDirs(sectordir);
3011 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3012 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3013 if(o.good() == false)
3014 throw FileNotGoodException("Cannot open block data");
3017 [0] u8 serialization version
3020 o.write((char*)&version, 1);
3023 block->serialize(o, version);
3025 // Write extra data stored on disk
3026 block->serializeDiskExtra(o, version);
3028 // We just wrote it to the disk so clear modified flag
3029 block->resetModified();
3032 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3034 DSTACK(__FUNCTION_NAME);
3036 std::string fullpath = sectordir+"/"+blockfile;
3039 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3040 if(is.good() == false)
3041 throw FileNotGoodException("Cannot open block file");
3043 v3s16 p3d = getBlockPos(sectordir, blockfile);
3044 v2s16 p2d(p3d.X, p3d.Z);
3046 assert(sector->getPos() == p2d);
3048 u8 version = SER_FMT_VER_INVALID;
3049 is.read((char*)&version, 1);
3052 throw SerializationError("ServerMap::loadBlock(): Failed"
3053 " to read MapBlock version");
3055 /*u32 block_size = MapBlock::serializedLength(version);
3056 SharedBuffer<u8> data(block_size);
3057 is.read((char*)*data, block_size);*/
3059 // This will always return a sector because we're the server
3060 //MapSector *sector = emergeSector(p2d);
3062 MapBlock *block = NULL;
3063 bool created_new = false;
3065 block = sector->getBlockNoCreate(p3d.Y);
3067 catch(InvalidPositionException &e)
3069 block = sector->createBlankBlockNoInsert(p3d.Y);
3074 block->deSerialize(is, version);
3076 // Read extra data stored on disk
3077 block->deSerializeDiskExtra(is, version);
3079 // If it's a new block, insert it to the map
3081 sector->insertBlock(block);
3084 Save blocks loaded in old format in new format
3087 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3092 // We just loaded it from the disk, so it's up-to-date.
3093 block->resetModified();
3096 catch(SerializationError &e)
3098 dstream<<"WARNING: Invalid block data on disk "
3099 <<"fullpath="<<fullpath
3100 <<" (SerializationError). "
3101 <<"what()="<<e.what()
3103 //" Ignoring. A new one will be generated.
3106 // TODO: Backup file; name is in fullpath.
3110 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3112 DSTACK(__FUNCTION_NAME);
3114 v2s16 p2d(blockpos.X, blockpos.Z);
3116 // The directory layout we're going to load from.
3117 // 1 - original sectors/xxxxzzzz/
3118 // 2 - new sectors2/xxx/zzz/
3119 // If we load from anything but the latest structure, we will
3120 // immediately save to the new one, and remove the old.
3122 std::string sectordir1 = getSectorDir(p2d, 1);
3123 std::string sectordir;
3124 if(fs::PathExists(sectordir1))
3126 sectordir = sectordir1;
3131 sectordir = getSectorDir(p2d, 2);
3135 Make sure sector is loaded
3137 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3141 sector = loadSectorMeta(sectordir, loadlayout != 2);
3143 catch(InvalidFilenameException &e)
3147 catch(FileNotGoodException &e)
3151 catch(std::exception &e)
3158 Make sure file exists
3161 std::string blockfilename = getBlockFilename(blockpos);
3162 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3168 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
3169 return getBlockNoCreateNoEx(blockpos);
3172 void ServerMap::PrintInfo(std::ostream &out)
3183 ClientMap::ClientMap(
3185 MapDrawControl &control,
3186 scene::ISceneNode* parent,
3187 scene::ISceneManager* mgr,
3191 scene::ISceneNode(parent, mgr, id),
3194 m_camera_position(0,0,0),
3195 m_camera_direction(0,0,1)
3197 m_camera_mutex.Init();
3198 assert(m_camera_mutex.IsInitialized());
3200 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3201 BS*1000000,BS*1000000,BS*1000000);
3204 ClientMap::~ClientMap()
3206 /*JMutexAutoLock lock(mesh_mutex);
3215 MapSector * ClientMap::emergeSector(v2s16 p2d)
3217 DSTACK(__FUNCTION_NAME);
3218 // Check that it doesn't exist already
3220 return getSectorNoGenerate(p2d);
3222 catch(InvalidPositionException &e)
3227 ClientMapSector *sector = new ClientMapSector(this, p2d);
3230 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3231 m_sectors.insert(p2d, sector);
3237 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3239 DSTACK(__FUNCTION_NAME);
3240 ClientMapSector *sector = NULL;
3242 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3244 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3248 sector = (ClientMapSector*)n->getValue();
3249 assert(sector->getId() == MAPSECTOR_CLIENT);
3253 sector = new ClientMapSector(this, p2d);
3255 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3256 m_sectors.insert(p2d, sector);
3260 sector->deSerialize(is);
3263 void ClientMap::OnRegisterSceneNode()
3267 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3268 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3271 ISceneNode::OnRegisterSceneNode();
3274 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3276 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3277 DSTACK(__FUNCTION_NAME);
3279 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3282 This is called two times per frame, reset on the non-transparent one
3284 if(pass == scene::ESNRP_SOLID)
3286 m_last_drawn_sectors.clear();
3290 Get time for measuring timeout.
3292 Measuring time is very useful for long delays when the
3293 machine is swapping a lot.
3295 int time1 = time(0);
3297 //u32 daynight_ratio = m_client->getDayNightRatio();
3299 m_camera_mutex.Lock();
3300 v3f camera_position = m_camera_position;
3301 v3f camera_direction = m_camera_direction;
3302 m_camera_mutex.Unlock();
3305 Get all blocks and draw all visible ones
3308 v3s16 cam_pos_nodes(
3309 camera_position.X / BS,
3310 camera_position.Y / BS,
3311 camera_position.Z / BS);
3313 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3315 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3316 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3318 // Take a fair amount as we will be dropping more out later
3320 p_nodes_min.X / MAP_BLOCKSIZE - 1,
3321 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
3322 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
3324 p_nodes_max.X / MAP_BLOCKSIZE,
3325 p_nodes_max.Y / MAP_BLOCKSIZE,
3326 p_nodes_max.Z / MAP_BLOCKSIZE);
3328 u32 vertex_count = 0;
3330 // For limiting number of mesh updates per frame
3331 u32 mesh_update_count = 0;
3333 u32 blocks_would_have_drawn = 0;
3334 u32 blocks_drawn = 0;
3336 int timecheck_counter = 0;
3337 core::map<v2s16, MapSector*>::Iterator si;
3338 si = m_sectors.getIterator();
3339 for(; si.atEnd() == false; si++)
3342 timecheck_counter++;
3343 if(timecheck_counter > 50)
3345 timecheck_counter = 0;
3346 int time2 = time(0);
3347 if(time2 > time1 + 4)
3349 dstream<<"ClientMap::renderMap(): "
3350 "Rendering takes ages, returning."
3357 MapSector *sector = si.getNode()->getValue();
3358 v2s16 sp = sector->getPos();
3360 if(m_control.range_all == false)
3362 if(sp.X < p_blocks_min.X
3363 || sp.X > p_blocks_max.X
3364 || sp.Y < p_blocks_min.Z
3365 || sp.Y > p_blocks_max.Z)
3369 core::list< MapBlock * > sectorblocks;
3370 sector->getBlocks(sectorblocks);
3376 u32 sector_blocks_drawn = 0;
3378 core::list< MapBlock * >::Iterator i;
3379 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3381 MapBlock *block = *i;
3384 Compare block position to camera position, skip
3385 if not seen on display
3388 float range = 100000 * BS;
3389 if(m_control.range_all == false)
3390 range = m_control.wanted_range * BS;
3393 if(isBlockInSight(block->getPos(), camera_position,
3394 camera_direction, range, &d) == false)
3399 // This is ugly (spherical distance limit?)
3400 /*if(m_control.range_all == false &&
3401 d - 0.5*BS*MAP_BLOCKSIZE > range)
3406 Update expired mesh (used for day/night change)
3408 It doesn't work exactly like it should now with the
3409 tasked mesh update but whatever.
3412 bool mesh_expired = false;
3415 JMutexAutoLock lock(block->mesh_mutex);
3417 mesh_expired = block->getMeshExpired();
3419 // Mesh has not been expired and there is no mesh:
3420 // block has no content
3421 if(block->mesh == NULL && mesh_expired == false)
3425 f32 faraway = BS*50;
3426 //f32 faraway = m_control.wanted_range * BS;
3429 This has to be done with the mesh_mutex unlocked
3431 // Pretty random but this should work somewhat nicely
3432 if(mesh_expired && (
3433 (mesh_update_count < 3
3434 && (d < faraway || mesh_update_count < 2)
3437 (m_control.range_all && mesh_update_count < 20)
3440 /*if(mesh_expired && mesh_update_count < 6
3441 && (d < faraway || mesh_update_count < 3))*/
3443 mesh_update_count++;
3445 // Mesh has been expired: generate new mesh
3446 //block->updateMesh(daynight_ratio);
3447 m_client->addUpdateMeshTask(block->getPos());
3449 mesh_expired = false;
3454 Draw the faces of the block
3457 JMutexAutoLock lock(block->mesh_mutex);
3459 scene::SMesh *mesh = block->mesh;
3464 blocks_would_have_drawn++;
3465 if(blocks_drawn >= m_control.wanted_max_blocks
3466 && m_control.range_all == false
3467 && d > m_control.wanted_min_range * BS)
3471 sector_blocks_drawn++;
3473 u32 c = mesh->getMeshBufferCount();
3475 for(u32 i=0; i<c; i++)
3477 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3478 const video::SMaterial& material = buf->getMaterial();
3479 video::IMaterialRenderer* rnd =
3480 driver->getMaterialRenderer(material.MaterialType);
3481 bool transparent = (rnd && rnd->isTransparent());
3482 // Render transparent on transparent pass and likewise.
3483 if(transparent == is_transparent_pass)
3486 This *shouldn't* hurt too much because Irrlicht
3487 doesn't change opengl textures if the old
3488 material is set again.
3490 driver->setMaterial(buf->getMaterial());
3491 driver->drawMeshBuffer(buf);
3492 vertex_count += buf->getVertexCount();
3496 } // foreach sectorblocks
3498 if(sector_blocks_drawn != 0)
3500 m_last_drawn_sectors[sp] = true;
3504 m_control.blocks_drawn = blocks_drawn;
3505 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3507 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3508 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3511 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3512 core::map<v3s16, MapBlock*> *affected_blocks)
3514 bool changed = false;
3516 Add it to all blocks touching it
3519 v3s16(0,0,0), // this
3520 v3s16(0,0,1), // back
3521 v3s16(0,1,0), // top
3522 v3s16(1,0,0), // right
3523 v3s16(0,0,-1), // front
3524 v3s16(0,-1,0), // bottom
3525 v3s16(-1,0,0), // left
3527 for(u16 i=0; i<7; i++)
3529 v3s16 p2 = p + dirs[i];
3530 // Block position of neighbor (or requested) node
3531 v3s16 blockpos = getNodeBlockPos(p2);
3532 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3533 if(blockref == NULL)
3535 // Relative position of requested node
3536 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3537 if(blockref->setTempMod(relpos, mod))
3542 if(changed && affected_blocks!=NULL)
3544 for(u16 i=0; i<7; i++)
3546 v3s16 p2 = p + dirs[i];
3547 // Block position of neighbor (or requested) node
3548 v3s16 blockpos = getNodeBlockPos(p2);
3549 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3550 if(blockref == NULL)
3552 affected_blocks->insert(blockpos, blockref);
3558 bool ClientMap::clearTempMod(v3s16 p,
3559 core::map<v3s16, MapBlock*> *affected_blocks)
3561 bool changed = false;
3563 v3s16(0,0,0), // this
3564 v3s16(0,0,1), // back
3565 v3s16(0,1,0), // top
3566 v3s16(1,0,0), // right
3567 v3s16(0,0,-1), // front
3568 v3s16(0,-1,0), // bottom
3569 v3s16(-1,0,0), // left
3571 for(u16 i=0; i<7; i++)
3573 v3s16 p2 = p + dirs[i];
3574 // Block position of neighbor (or requested) node
3575 v3s16 blockpos = getNodeBlockPos(p2);
3576 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3577 if(blockref == NULL)
3579 // Relative position of requested node
3580 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3581 if(blockref->clearTempMod(relpos))
3586 if(changed && affected_blocks!=NULL)
3588 for(u16 i=0; i<7; i++)
3590 v3s16 p2 = p + dirs[i];
3591 // Block position of neighbor (or requested) node
3592 v3s16 blockpos = getNodeBlockPos(p2);
3593 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3594 if(blockref == NULL)
3596 affected_blocks->insert(blockpos, blockref);
3602 void ClientMap::expireMeshes(bool only_daynight_diffed)
3604 TimeTaker timer("expireMeshes()");
3606 core::map<v2s16, MapSector*>::Iterator si;
3607 si = m_sectors.getIterator();
3608 for(; si.atEnd() == false; si++)
3610 MapSector *sector = si.getNode()->getValue();
3612 core::list< MapBlock * > sectorblocks;
3613 sector->getBlocks(sectorblocks);
3615 core::list< MapBlock * >::Iterator i;
3616 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3618 MapBlock *block = *i;
3620 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
3626 JMutexAutoLock lock(block->mesh_mutex);
3627 if(block->mesh != NULL)
3629 /*block->mesh->drop();
3630 block->mesh = NULL;*/
3631 block->setMeshExpired(true);
3638 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
3640 assert(mapType() == MAPTYPE_CLIENT);
3643 v3s16 p = blockpos + v3s16(0,0,0);
3644 MapBlock *b = getBlockNoCreate(p);
3645 b->updateMesh(daynight_ratio);
3646 //b->setMeshExpired(true);
3648 catch(InvalidPositionException &e){}
3651 v3s16 p = blockpos + v3s16(-1,0,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,-1,0);
3659 MapBlock *b = getBlockNoCreate(p);
3660 b->updateMesh(daynight_ratio);
3661 //b->setMeshExpired(true);
3663 catch(InvalidPositionException &e){}
3665 v3s16 p = blockpos + v3s16(0,0,-1);
3666 MapBlock *b = getBlockNoCreate(p);
3667 b->updateMesh(daynight_ratio);
3668 //b->setMeshExpired(true);
3670 catch(InvalidPositionException &e){}
3675 Update mesh of block in which the node is, and if the node is at the
3676 leading edge, update the appropriate leading blocks too.
3678 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
3686 v3s16 blockposes[4];
3687 for(u32 i=0; i<4; i++)
3689 v3s16 np = nodepos + dirs[i];
3690 blockposes[i] = getNodeBlockPos(np);
3691 // Don't update mesh of block if it has been done already
3692 bool already_updated = false;
3693 for(u32 j=0; j<i; j++)
3695 if(blockposes[j] == blockposes[i])
3697 already_updated = true;
3704 MapBlock *b = getBlockNoCreate(blockposes[i]);
3705 b->updateMesh(daynight_ratio);
3710 void ClientMap::PrintInfo(std::ostream &out)
3721 MapVoxelManipulator::MapVoxelManipulator(Map *map)
3726 MapVoxelManipulator::~MapVoxelManipulator()
3728 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
3732 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3734 TimeTaker timer1("emerge", &emerge_time);
3736 // Units of these are MapBlocks
3737 v3s16 p_min = getNodeBlockPos(a.MinEdge);
3738 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
3740 VoxelArea block_area_nodes
3741 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3743 addArea(block_area_nodes);
3745 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3746 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3747 for(s32 x=p_min.X; x<=p_max.X; x++)
3750 core::map<v3s16, bool>::Node *n;
3751 n = m_loaded_blocks.find(p);
3755 bool block_data_inexistent = false;
3758 TimeTaker timer1("emerge load", &emerge_load_time);
3760 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
3761 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3764 dstream<<std::endl;*/
3766 MapBlock *block = m_map->getBlockNoCreate(p);
3767 if(block->isDummy())
3768 block_data_inexistent = true;
3770 block->copyTo(*this);
3772 catch(InvalidPositionException &e)
3774 block_data_inexistent = true;
3777 if(block_data_inexistent)
3779 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3780 // Fill with VOXELFLAG_INEXISTENT
3781 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3782 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3784 s32 i = m_area.index(a.MinEdge.X,y,z);
3785 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3789 m_loaded_blocks.insert(p, !block_data_inexistent);
3792 //dstream<<"emerge done"<<std::endl;
3796 SUGG: Add an option to only update eg. water and air nodes.
3797 This will make it interfere less with important stuff if
3800 void MapVoxelManipulator::blitBack
3801 (core::map<v3s16, MapBlock*> & modified_blocks)
3803 if(m_area.getExtent() == v3s16(0,0,0))
3806 //TimeTaker timer1("blitBack");
3808 /*dstream<<"blitBack(): m_loaded_blocks.size()="
3809 <<m_loaded_blocks.size()<<std::endl;*/
3812 Initialize block cache
3814 v3s16 blockpos_last;
3815 MapBlock *block = NULL;
3816 bool block_checked_in_modified = false;
3818 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
3819 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
3820 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
3824 u8 f = m_flags[m_area.index(p)];
3825 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
3828 MapNode &n = m_data[m_area.index(p)];
3830 v3s16 blockpos = getNodeBlockPos(p);
3835 if(block == NULL || blockpos != blockpos_last){
3836 block = m_map->getBlockNoCreate(blockpos);
3837 blockpos_last = blockpos;
3838 block_checked_in_modified = false;
3841 // Calculate relative position in block
3842 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
3844 // Don't continue if nothing has changed here
3845 if(block->getNode(relpos) == n)
3848 //m_map->setNode(m_area.MinEdge + p, n);
3849 block->setNode(relpos, n);
3852 Make sure block is in modified_blocks
3854 if(block_checked_in_modified == false)
3856 modified_blocks[blockpos] = block;
3857 block_checked_in_modified = true;
3860 catch(InvalidPositionException &e)
3866 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
3867 MapVoxelManipulator(map),
3868 m_create_area(false)
3872 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
3876 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
3878 // Just create the area so that it can be pointed to
3879 VoxelManipulator::emerge(a, caller_id);
3882 void ManualMapVoxelManipulator::initialEmerge(
3883 v3s16 blockpos_min, v3s16 blockpos_max)
3885 TimeTaker timer1("initialEmerge", &emerge_time);
3887 // Units of these are MapBlocks
3888 v3s16 p_min = blockpos_min;
3889 v3s16 p_max = blockpos_max;
3891 VoxelArea block_area_nodes
3892 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3894 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
3897 dstream<<"initialEmerge: area: ";
3898 block_area_nodes.print(dstream);
3899 dstream<<" ("<<size_MB<<"MB)";
3903 addArea(block_area_nodes);
3905 for(s32 z=p_min.Z; z<=p_max.Z; z++)
3906 for(s32 y=p_min.Y; y<=p_max.Y; y++)
3907 for(s32 x=p_min.X; x<=p_max.X; x++)
3910 core::map<v3s16, bool>::Node *n;
3911 n = m_loaded_blocks.find(p);
3915 bool block_data_inexistent = false;
3918 TimeTaker timer1("emerge load", &emerge_load_time);
3920 MapBlock *block = m_map->getBlockNoCreate(p);
3921 if(block->isDummy())
3922 block_data_inexistent = true;
3924 block->copyTo(*this);
3926 catch(InvalidPositionException &e)
3928 block_data_inexistent = true;
3931 if(block_data_inexistent)
3934 Mark area inexistent
3936 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
3937 // Fill with VOXELFLAG_INEXISTENT
3938 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
3939 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
3941 s32 i = m_area.index(a.MinEdge.X,y,z);
3942 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
3946 m_loaded_blocks.insert(p, !block_data_inexistent);
3950 void ManualMapVoxelManipulator::blitBackAll(
3951 core::map<v3s16, MapBlock*> * modified_blocks)
3953 if(m_area.getExtent() == v3s16(0,0,0))
3957 Copy data of all blocks
3959 for(core::map<v3s16, bool>::Iterator
3960 i = m_loaded_blocks.getIterator();
3961 i.atEnd() == false; i++)
3963 bool existed = i.getNode()->getValue();
3964 if(existed == false)
3966 v3s16 p = i.getNode()->getKey();
3967 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
3970 dstream<<"WARNING: "<<__FUNCTION_NAME
3971 <<": got NULL block "
3972 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3977 block->copyFrom(*this);
3980 modified_blocks->insert(p, block);