3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
32 #include "nodemetadata.h"
33 #include "content_mapnode.h"
35 #include <IMaterialRenderer.h>
41 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
44 SQLite format specification:
45 - Initially only replaces sectors/ and sectors2/
47 If map.sqlite does not exist in the save dir
48 or the block was not found in the database
49 the map will try to load from sectors folder.
50 In either case, map.sqlite will be created
51 and all future saves will save there.
53 Structure of map.sqlite:
64 Map::Map(std::ostream &dout):
68 /*m_sector_mutex.Init();
69 assert(m_sector_mutex.IsInitialized());*/
77 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
78 for(; i.atEnd() == false; i++)
80 MapSector *sector = i.getNode()->getValue();
85 void Map::addEventReceiver(MapEventReceiver *event_receiver)
87 m_event_receivers.insert(event_receiver, false);
90 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
92 if(m_event_receivers.find(event_receiver) == NULL)
94 m_event_receivers.remove(event_receiver);
97 void Map::dispatchEvent(MapEditEvent *event)
99 for(core::map<MapEventReceiver*, bool>::Iterator
100 i = m_event_receivers.getIterator();
101 i.atEnd()==false; i++)
103 MapEventReceiver* event_receiver = i.getNode()->getKey();
104 event_receiver->onMapEditEvent(event);
108 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
110 if(m_sector_cache != NULL && p == m_sector_cache_p){
111 MapSector * sector = m_sector_cache;
115 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
120 MapSector *sector = n->getValue();
122 // Cache the last result
123 m_sector_cache_p = p;
124 m_sector_cache = sector;
129 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
131 return getSectorNoGenerateNoExNoLock(p);
134 MapSector * Map::getSectorNoGenerate(v2s16 p)
136 MapSector *sector = getSectorNoGenerateNoEx(p);
138 throw InvalidPositionException();
143 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
145 v2s16 p2d(p3d.X, p3d.Z);
146 MapSector * sector = getSectorNoGenerateNoEx(p2d);
149 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
153 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
155 MapBlock *block = getBlockNoCreateNoEx(p3d);
157 throw InvalidPositionException();
161 bool Map::isNodeUnderground(v3s16 p)
163 v3s16 blockpos = getNodeBlockPos(p);
165 MapBlock * block = getBlockNoCreate(blockpos);
166 return block->getIsUnderground();
168 catch(InvalidPositionException &e)
174 bool Map::isValidPosition(v3s16 p)
176 v3s16 blockpos = getNodeBlockPos(p);
177 MapBlock *block = getBlockNoCreate(blockpos);
178 return (block != NULL);
181 // Returns a CONTENT_IGNORE node if not found
182 MapNode Map::getNodeNoEx(v3s16 p)
184 v3s16 blockpos = getNodeBlockPos(p);
185 MapBlock *block = getBlockNoCreateNoEx(blockpos);
187 return MapNode(CONTENT_IGNORE);
188 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
189 return block->getNodeNoCheck(relpos);
192 // throws InvalidPositionException if not found
193 MapNode Map::getNode(v3s16 p)
195 v3s16 blockpos = getNodeBlockPos(p);
196 MapBlock *block = getBlockNoCreateNoEx(blockpos);
198 throw InvalidPositionException();
199 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
200 return block->getNodeNoCheck(relpos);
203 // throws InvalidPositionException if not found
204 void Map::setNode(v3s16 p, MapNode & n)
206 v3s16 blockpos = getNodeBlockPos(p);
207 MapBlock *block = getBlockNoCreate(blockpos);
208 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
209 block->setNodeNoCheck(relpos, n);
214 Goes recursively through the neighbours of the node.
216 Alters only transparent nodes.
218 If the lighting of the neighbour is lower than the lighting of
219 the node was (before changing it to 0 at the step before), the
220 lighting of the neighbour is set to 0 and then the same stuff
221 repeats for the neighbour.
223 The ending nodes of the routine are stored in light_sources.
224 This is useful when a light is removed. In such case, this
225 routine can be called for the light node and then again for
226 light_sources to re-light the area without the removed light.
228 values of from_nodes are lighting values.
230 void Map::unspreadLight(enum LightBank bank,
231 core::map<v3s16, u8> & from_nodes,
232 core::map<v3s16, bool> & light_sources,
233 core::map<v3s16, MapBlock*> & modified_blocks)
236 v3s16(0,0,1), // back
238 v3s16(1,0,0), // right
239 v3s16(0,0,-1), // front
240 v3s16(0,-1,0), // bottom
241 v3s16(-1,0,0), // left
244 if(from_nodes.size() == 0)
247 u32 blockchangecount = 0;
249 core::map<v3s16, u8> unlighted_nodes;
250 core::map<v3s16, u8>::Iterator j;
251 j = from_nodes.getIterator();
254 Initialize block cache
257 MapBlock *block = NULL;
258 // Cache this a bit, too
259 bool block_checked_in_modified = false;
261 for(; j.atEnd() == false; j++)
263 v3s16 pos = j.getNode()->getKey();
264 v3s16 blockpos = getNodeBlockPos(pos);
266 // Only fetch a new block if the block position has changed
268 if(block == NULL || blockpos != blockpos_last){
269 block = getBlockNoCreate(blockpos);
270 blockpos_last = blockpos;
272 block_checked_in_modified = false;
276 catch(InvalidPositionException &e)
284 // Calculate relative position in block
285 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
287 // Get node straight from the block
288 MapNode n = block->getNode(relpos);
290 u8 oldlight = j.getNode()->getValue();
292 // Loop through 6 neighbors
293 for(u16 i=0; i<6; i++)
295 // Get the position of the neighbor node
296 v3s16 n2pos = pos + dirs[i];
298 // Get the block where the node is located
299 v3s16 blockpos = getNodeBlockPos(n2pos);
303 // Only fetch a new block if the block position has changed
305 if(block == NULL || blockpos != blockpos_last){
306 block = getBlockNoCreate(blockpos);
307 blockpos_last = blockpos;
309 block_checked_in_modified = false;
313 catch(InvalidPositionException &e)
318 // Calculate relative position in block
319 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
320 // Get node straight from the block
321 MapNode n2 = block->getNode(relpos);
323 bool changed = false;
325 //TODO: Optimize output by optimizing light_sources?
328 If the neighbor is dimmer than what was specified
329 as oldlight (the light of the previous node)
331 if(n2.getLight(bank) < oldlight)
334 And the neighbor is transparent and it has some light
336 if(n2.light_propagates() && n2.getLight(bank) != 0)
339 Set light to 0 and add to queue
342 u8 current_light = n2.getLight(bank);
343 n2.setLight(bank, 0);
344 block->setNode(relpos, n2);
346 unlighted_nodes.insert(n2pos, current_light);
350 Remove from light_sources if it is there
351 NOTE: This doesn't happen nearly at all
353 /*if(light_sources.find(n2pos))
355 infostream<<"Removed from light_sources"<<std::endl;
356 light_sources.remove(n2pos);
361 if(light_sources.find(n2pos) != NULL)
362 light_sources.remove(n2pos);*/
365 light_sources.insert(n2pos, true);
368 // Add to modified_blocks
369 if(changed == true && block_checked_in_modified == false)
371 // If the block is not found in modified_blocks, add.
372 if(modified_blocks.find(blockpos) == NULL)
374 modified_blocks.insert(blockpos, block);
376 block_checked_in_modified = true;
379 catch(InvalidPositionException &e)
386 /*infostream<<"unspreadLight(): Changed block "
387 <<blockchangecount<<" times"
388 <<" for "<<from_nodes.size()<<" nodes"
391 if(unlighted_nodes.size() > 0)
392 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
396 A single-node wrapper of the above
398 void Map::unLightNeighbors(enum LightBank bank,
399 v3s16 pos, u8 lightwas,
400 core::map<v3s16, bool> & light_sources,
401 core::map<v3s16, MapBlock*> & modified_blocks)
403 core::map<v3s16, u8> from_nodes;
404 from_nodes.insert(pos, lightwas);
406 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
410 Lights neighbors of from_nodes, collects all them and then
413 void Map::spreadLight(enum LightBank bank,
414 core::map<v3s16, bool> & from_nodes,
415 core::map<v3s16, MapBlock*> & modified_blocks)
417 const v3s16 dirs[6] = {
418 v3s16(0,0,1), // back
420 v3s16(1,0,0), // right
421 v3s16(0,0,-1), // front
422 v3s16(0,-1,0), // bottom
423 v3s16(-1,0,0), // left
426 if(from_nodes.size() == 0)
429 u32 blockchangecount = 0;
431 core::map<v3s16, bool> lighted_nodes;
432 core::map<v3s16, bool>::Iterator j;
433 j = from_nodes.getIterator();
436 Initialize block cache
439 MapBlock *block = NULL;
440 // Cache this a bit, too
441 bool block_checked_in_modified = false;
443 for(; j.atEnd() == false; j++)
444 //for(; j != from_nodes.end(); j++)
446 v3s16 pos = j.getNode()->getKey();
448 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
449 v3s16 blockpos = getNodeBlockPos(pos);
451 // Only fetch a new block if the block position has changed
453 if(block == NULL || blockpos != blockpos_last){
454 block = getBlockNoCreate(blockpos);
455 blockpos_last = blockpos;
457 block_checked_in_modified = false;
461 catch(InvalidPositionException &e)
469 // Calculate relative position in block
470 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
472 // Get node straight from the block
473 MapNode n = block->getNode(relpos);
475 u8 oldlight = n.getLight(bank);
476 u8 newlight = diminish_light(oldlight);
478 // Loop through 6 neighbors
479 for(u16 i=0; i<6; i++){
480 // Get the position of the neighbor node
481 v3s16 n2pos = pos + dirs[i];
483 // Get the block where the node is located
484 v3s16 blockpos = getNodeBlockPos(n2pos);
488 // Only fetch a new block if the block position has changed
490 if(block == NULL || blockpos != blockpos_last){
491 block = getBlockNoCreate(blockpos);
492 blockpos_last = blockpos;
494 block_checked_in_modified = false;
498 catch(InvalidPositionException &e)
503 // Calculate relative position in block
504 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
505 // Get node straight from the block
506 MapNode n2 = block->getNode(relpos);
508 bool changed = false;
510 If the neighbor is brighter than the current node,
511 add to list (it will light up this node on its turn)
513 if(n2.getLight(bank) > undiminish_light(oldlight))
515 lighted_nodes.insert(n2pos, true);
516 //lighted_nodes.push_back(n2pos);
520 If the neighbor is dimmer than how much light this node
521 would spread on it, add to list
523 if(n2.getLight(bank) < newlight)
525 if(n2.light_propagates())
527 n2.setLight(bank, newlight);
528 block->setNode(relpos, n2);
529 lighted_nodes.insert(n2pos, true);
530 //lighted_nodes.push_back(n2pos);
535 // Add to modified_blocks
536 if(changed == true && block_checked_in_modified == false)
538 // If the block is not found in modified_blocks, add.
539 if(modified_blocks.find(blockpos) == NULL)
541 modified_blocks.insert(blockpos, block);
543 block_checked_in_modified = true;
546 catch(InvalidPositionException &e)
553 /*infostream<<"spreadLight(): Changed block "
554 <<blockchangecount<<" times"
555 <<" for "<<from_nodes.size()<<" nodes"
558 if(lighted_nodes.size() > 0)
559 spreadLight(bank, lighted_nodes, modified_blocks);
563 A single-node source variation of the above.
565 void Map::lightNeighbors(enum LightBank bank,
567 core::map<v3s16, MapBlock*> & modified_blocks)
569 core::map<v3s16, bool> from_nodes;
570 from_nodes.insert(pos, true);
571 spreadLight(bank, from_nodes, modified_blocks);
574 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
577 v3s16(0,0,1), // back
579 v3s16(1,0,0), // right
580 v3s16(0,0,-1), // front
581 v3s16(0,-1,0), // bottom
582 v3s16(-1,0,0), // left
585 u8 brightest_light = 0;
586 v3s16 brightest_pos(0,0,0);
587 bool found_something = false;
589 // Loop through 6 neighbors
590 for(u16 i=0; i<6; i++){
591 // Get the position of the neighbor node
592 v3s16 n2pos = p + dirs[i];
597 catch(InvalidPositionException &e)
601 if(n2.getLight(bank) > brightest_light || found_something == false){
602 brightest_light = n2.getLight(bank);
603 brightest_pos = n2pos;
604 found_something = true;
608 if(found_something == false)
609 throw InvalidPositionException();
611 return brightest_pos;
615 Propagates sunlight down from a node.
616 Starting point gets sunlight.
618 Returns the lowest y value of where the sunlight went.
620 Mud is turned into grass in where the sunlight stops.
622 s16 Map::propagateSunlight(v3s16 start,
623 core::map<v3s16, MapBlock*> & modified_blocks)
628 v3s16 pos(start.X, y, start.Z);
630 v3s16 blockpos = getNodeBlockPos(pos);
633 block = getBlockNoCreate(blockpos);
635 catch(InvalidPositionException &e)
640 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
641 MapNode n = block->getNode(relpos);
643 if(n.sunlight_propagates())
645 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
646 block->setNode(relpos, n);
648 modified_blocks.insert(blockpos, block);
652 /*// Turn mud into grass
653 if(n.getContent() == CONTENT_MUD)
655 n.setContent(CONTENT_GRASS);
656 block->setNode(relpos, n);
657 modified_blocks.insert(blockpos, block);
660 // Sunlight goes no further
667 void Map::updateLighting(enum LightBank bank,
668 core::map<v3s16, MapBlock*> & a_blocks,
669 core::map<v3s16, MapBlock*> & modified_blocks)
671 /*m_dout<<DTIME<<"Map::updateLighting(): "
672 <<a_blocks.size()<<" blocks."<<std::endl;*/
674 //TimeTaker timer("updateLighting");
678 //u32 count_was = modified_blocks.size();
680 core::map<v3s16, MapBlock*> blocks_to_update;
682 core::map<v3s16, bool> light_sources;
684 core::map<v3s16, u8> unlight_from;
686 core::map<v3s16, MapBlock*>::Iterator i;
687 i = a_blocks.getIterator();
688 for(; i.atEnd() == false; i++)
690 MapBlock *block = i.getNode()->getValue();
694 // Don't bother with dummy blocks.
698 v3s16 pos = block->getPos();
699 modified_blocks.insert(pos, block);
701 blocks_to_update.insert(pos, block);
704 Clear all light from block
706 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
707 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
708 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
713 MapNode n = block->getNode(v3s16(x,y,z));
714 u8 oldlight = n.getLight(bank);
716 block->setNode(v3s16(x,y,z), n);
718 // Collect borders for unlighting
719 if(x==0 || x == MAP_BLOCKSIZE-1
720 || y==0 || y == MAP_BLOCKSIZE-1
721 || z==0 || z == MAP_BLOCKSIZE-1)
723 v3s16 p_map = p + v3s16(
726 MAP_BLOCKSIZE*pos.Z);
727 unlight_from.insert(p_map, oldlight);
730 catch(InvalidPositionException &e)
733 This would happen when dealing with a
737 infostream<<"updateLighting(): InvalidPositionException"
742 if(bank == LIGHTBANK_DAY)
744 bool bottom_valid = block->propagateSunlight(light_sources);
746 // If bottom is valid, we're done.
750 else if(bank == LIGHTBANK_NIGHT)
752 // For night lighting, sunlight is not propagated
757 // Invalid lighting bank
761 /*infostream<<"Bottom for sunlight-propagated block ("
762 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
765 // Bottom sunlight is not valid; get the block and loop to it
769 block = getBlockNoCreate(pos);
771 catch(InvalidPositionException &e)
780 Enable this to disable proper lighting for speeding up map
781 generation for testing or whatever
784 //if(g_settings->get(""))
786 core::map<v3s16, MapBlock*>::Iterator i;
787 i = blocks_to_update.getIterator();
788 for(; i.atEnd() == false; i++)
790 MapBlock *block = i.getNode()->getValue();
791 v3s16 p = block->getPos();
792 block->setLightingExpired(false);
800 TimeTaker timer("unspreadLight");
801 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
806 u32 diff = modified_blocks.size() - count_was;
807 count_was = modified_blocks.size();
808 infostream<<"unspreadLight modified "<<diff<<std::endl;
812 TimeTaker timer("spreadLight");
813 spreadLight(bank, light_sources, modified_blocks);
818 u32 diff = modified_blocks.size() - count_was;
819 count_was = modified_blocks.size();
820 infostream<<"spreadLight modified "<<diff<<std::endl;
825 //MapVoxelManipulator vmanip(this);
827 // Make a manual voxel manipulator and load all the blocks
828 // that touch the requested blocks
829 ManualMapVoxelManipulator vmanip(this);
830 core::map<v3s16, MapBlock*>::Iterator i;
831 i = blocks_to_update.getIterator();
832 for(; i.atEnd() == false; i++)
834 MapBlock *block = i.getNode()->getValue();
835 v3s16 p = block->getPos();
837 // Add all surrounding blocks
838 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
841 Add all surrounding blocks that have up-to-date lighting
842 NOTE: This doesn't quite do the job (not everything
843 appropriate is lighted)
845 /*for(s16 z=-1; z<=1; z++)
846 for(s16 y=-1; y<=1; y++)
847 for(s16 x=-1; x<=1; x++)
850 MapBlock *block = getBlockNoCreateNoEx(p);
855 if(block->getLightingExpired())
857 vmanip.initialEmerge(p, p);
860 // Lighting of block will be updated completely
861 block->setLightingExpired(false);
865 //TimeTaker timer("unSpreadLight");
866 vmanip.unspreadLight(bank, unlight_from, light_sources);
869 //TimeTaker timer("spreadLight");
870 vmanip.spreadLight(bank, light_sources);
873 //TimeTaker timer("blitBack");
874 vmanip.blitBack(modified_blocks);
876 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
880 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
883 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
884 core::map<v3s16, MapBlock*> & modified_blocks)
886 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
887 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
890 Update information about whether day and night light differ
892 for(core::map<v3s16, MapBlock*>::Iterator
893 i = modified_blocks.getIterator();
894 i.atEnd() == false; i++)
896 MapBlock *block = i.getNode()->getValue();
897 block->updateDayNightDiff();
903 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
904 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
907 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
908 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
911 From this node to nodes underneath:
912 If lighting is sunlight (1.0), unlight neighbours and
917 v3s16 toppos = p + v3s16(0,1,0);
918 v3s16 bottompos = p + v3s16(0,-1,0);
920 bool node_under_sunlight = true;
921 core::map<v3s16, bool> light_sources;
924 If there is a node at top and it doesn't have sunlight,
925 there has not been any sunlight going down.
927 Otherwise there probably is.
930 MapNode topnode = getNode(toppos);
932 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
933 node_under_sunlight = false;
935 catch(InvalidPositionException &e)
941 If the new node is solid and there is grass below, change it to mud
943 if(content_features(n).walkable == true)
946 MapNode bottomnode = getNode(bottompos);
948 if(bottomnode.getContent() == CONTENT_GRASS
949 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
951 bottomnode.setContent(CONTENT_MUD);
952 setNode(bottompos, bottomnode);
955 catch(InvalidPositionException &e)
963 If the new node is mud and it is under sunlight, change it
966 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
968 n.setContent(CONTENT_GRASS);
973 Remove all light that has come out of this node
976 enum LightBank banks[] =
981 for(s32 i=0; i<2; i++)
983 enum LightBank bank = banks[i];
985 u8 lightwas = getNode(p).getLight(bank);
987 // Add the block of the added node to modified_blocks
988 v3s16 blockpos = getNodeBlockPos(p);
989 MapBlock * block = getBlockNoCreate(blockpos);
990 assert(block != NULL);
991 modified_blocks.insert(blockpos, block);
993 assert(isValidPosition(p));
995 // Unlight neighbours of node.
996 // This means setting light of all consequent dimmer nodes
998 // This also collects the nodes at the border which will spread
999 // light again into this.
1000 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1002 n.setLight(bank, 0);
1006 If node lets sunlight through and is under sunlight, it has
1009 if(node_under_sunlight && content_features(n).sunlight_propagates)
1011 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1015 Set the node on the map
1024 NodeMetadata *meta_proto = content_features(n).initial_metadata;
1027 NodeMetadata *meta = meta_proto->clone();
1028 meta->setOwner(player_name);
1029 setNodeMetadata(p, meta);
1033 If node is under sunlight and doesn't let sunlight through,
1034 take all sunlighted nodes under it and clear light from them
1035 and from where the light has been spread.
1036 TODO: This could be optimized by mass-unlighting instead
1039 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1043 //m_dout<<DTIME<<"y="<<y<<std::endl;
1044 v3s16 n2pos(p.X, y, p.Z);
1048 n2 = getNode(n2pos);
1050 catch(InvalidPositionException &e)
1055 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1057 unLightNeighbors(LIGHTBANK_DAY,
1058 n2pos, n2.getLight(LIGHTBANK_DAY),
1059 light_sources, modified_blocks);
1060 n2.setLight(LIGHTBANK_DAY, 0);
1068 for(s32 i=0; i<2; i++)
1070 enum LightBank bank = banks[i];
1073 Spread light from all nodes that might be capable of doing so
1075 spreadLight(bank, light_sources, modified_blocks);
1079 Update information about whether day and night light differ
1081 for(core::map<v3s16, MapBlock*>::Iterator
1082 i = modified_blocks.getIterator();
1083 i.atEnd() == false; i++)
1085 MapBlock *block = i.getNode()->getValue();
1086 block->updateDayNightDiff();
1090 Add neighboring liquid nodes and the node itself if it is
1091 liquid (=water node was added) to transform queue.
1094 v3s16(0,0,0), // self
1095 v3s16(0,0,1), // back
1096 v3s16(0,1,0), // top
1097 v3s16(1,0,0), // right
1098 v3s16(0,0,-1), // front
1099 v3s16(0,-1,0), // bottom
1100 v3s16(-1,0,0), // left
1102 for(u16 i=0; i<7; i++)
1107 v3s16 p2 = p + dirs[i];
1109 MapNode n2 = getNode(p2);
1110 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1112 m_transforming_liquid.push_back(p2);
1115 }catch(InvalidPositionException &e)
1123 void Map::removeNodeAndUpdate(v3s16 p,
1124 core::map<v3s16, MapBlock*> &modified_blocks)
1126 /*PrintInfo(m_dout);
1127 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1128 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1130 bool node_under_sunlight = true;
1132 v3s16 toppos = p + v3s16(0,1,0);
1134 // Node will be replaced with this
1135 content_t replace_material = CONTENT_AIR;
1138 If there is a node at top and it doesn't have sunlight,
1139 there will be no sunlight going down.
1142 MapNode topnode = getNode(toppos);
1144 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1145 node_under_sunlight = false;
1147 catch(InvalidPositionException &e)
1151 core::map<v3s16, bool> light_sources;
1153 enum LightBank banks[] =
1158 for(s32 i=0; i<2; i++)
1160 enum LightBank bank = banks[i];
1163 Unlight neighbors (in case the node is a light source)
1165 unLightNeighbors(bank, p,
1166 getNode(p).getLight(bank),
1167 light_sources, modified_blocks);
1171 Remove node metadata
1174 removeNodeMetadata(p);
1178 This also clears the lighting.
1182 n.setContent(replace_material);
1185 for(s32 i=0; i<2; i++)
1187 enum LightBank bank = banks[i];
1190 Recalculate lighting
1192 spreadLight(bank, light_sources, modified_blocks);
1195 // Add the block of the removed node to modified_blocks
1196 v3s16 blockpos = getNodeBlockPos(p);
1197 MapBlock * block = getBlockNoCreate(blockpos);
1198 assert(block != NULL);
1199 modified_blocks.insert(blockpos, block);
1202 If the removed node was under sunlight, propagate the
1203 sunlight down from it and then light all neighbors
1204 of the propagated blocks.
1206 if(node_under_sunlight)
1208 s16 ybottom = propagateSunlight(p, modified_blocks);
1209 /*m_dout<<DTIME<<"Node was under sunlight. "
1210 "Propagating sunlight";
1211 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1213 for(; y >= ybottom; y--)
1215 v3s16 p2(p.X, y, p.Z);
1216 /*m_dout<<DTIME<<"lighting neighbors of node ("
1217 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1219 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1224 // Set the lighting of this node to 0
1225 // TODO: Is this needed? Lighting is cleared up there already.
1227 MapNode n = getNode(p);
1228 n.setLight(LIGHTBANK_DAY, 0);
1231 catch(InvalidPositionException &e)
1237 for(s32 i=0; i<2; i++)
1239 enum LightBank bank = banks[i];
1241 // Get the brightest neighbour node and propagate light from it
1242 v3s16 n2p = getBrightestNeighbour(bank, p);
1244 MapNode n2 = getNode(n2p);
1245 lightNeighbors(bank, n2p, modified_blocks);
1247 catch(InvalidPositionException &e)
1253 Update information about whether day and night light differ
1255 for(core::map<v3s16, MapBlock*>::Iterator
1256 i = modified_blocks.getIterator();
1257 i.atEnd() == false; i++)
1259 MapBlock *block = i.getNode()->getValue();
1260 block->updateDayNightDiff();
1264 Add neighboring liquid nodes and this node to transform queue.
1265 (it's vital for the node itself to get updated last.)
1268 v3s16(0,0,1), // back
1269 v3s16(0,1,0), // top
1270 v3s16(1,0,0), // right
1271 v3s16(0,0,-1), // front
1272 v3s16(0,-1,0), // bottom
1273 v3s16(-1,0,0), // left
1274 v3s16(0,0,0), // self
1276 for(u16 i=0; i<7; i++)
1281 v3s16 p2 = p + dirs[i];
1283 MapNode n2 = getNode(p2);
1284 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1286 m_transforming_liquid.push_back(p2);
1289 }catch(InvalidPositionException &e)
1295 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1298 event.type = MEET_ADDNODE;
1302 bool succeeded = true;
1304 core::map<v3s16, MapBlock*> modified_blocks;
1305 std::string st = std::string("");
1306 addNodeAndUpdate(p, n, modified_blocks, st);
1308 // Copy modified_blocks to event
1309 for(core::map<v3s16, MapBlock*>::Iterator
1310 i = modified_blocks.getIterator();
1311 i.atEnd()==false; i++)
1313 event.modified_blocks.insert(i.getNode()->getKey(), false);
1316 catch(InvalidPositionException &e){
1320 dispatchEvent(&event);
1325 bool Map::removeNodeWithEvent(v3s16 p)
1328 event.type = MEET_REMOVENODE;
1331 bool succeeded = true;
1333 core::map<v3s16, MapBlock*> modified_blocks;
1334 removeNodeAndUpdate(p, modified_blocks);
1336 // Copy modified_blocks to event
1337 for(core::map<v3s16, MapBlock*>::Iterator
1338 i = modified_blocks.getIterator();
1339 i.atEnd()==false; i++)
1341 event.modified_blocks.insert(i.getNode()->getKey(), false);
1344 catch(InvalidPositionException &e){
1348 dispatchEvent(&event);
1353 bool Map::dayNightDiffed(v3s16 blockpos)
1356 v3s16 p = blockpos + v3s16(0,0,0);
1357 MapBlock *b = getBlockNoCreate(p);
1358 if(b->dayNightDiffed())
1361 catch(InvalidPositionException &e){}
1364 v3s16 p = blockpos + v3s16(-1,0,0);
1365 MapBlock *b = getBlockNoCreate(p);
1366 if(b->dayNightDiffed())
1369 catch(InvalidPositionException &e){}
1371 v3s16 p = blockpos + v3s16(0,-1,0);
1372 MapBlock *b = getBlockNoCreate(p);
1373 if(b->dayNightDiffed())
1376 catch(InvalidPositionException &e){}
1378 v3s16 p = blockpos + v3s16(0,0,-1);
1379 MapBlock *b = getBlockNoCreate(p);
1380 if(b->dayNightDiffed())
1383 catch(InvalidPositionException &e){}
1386 v3s16 p = blockpos + v3s16(1,0,0);
1387 MapBlock *b = getBlockNoCreate(p);
1388 if(b->dayNightDiffed())
1391 catch(InvalidPositionException &e){}
1393 v3s16 p = blockpos + v3s16(0,1,0);
1394 MapBlock *b = getBlockNoCreate(p);
1395 if(b->dayNightDiffed())
1398 catch(InvalidPositionException &e){}
1400 v3s16 p = blockpos + v3s16(0,0,1);
1401 MapBlock *b = getBlockNoCreate(p);
1402 if(b->dayNightDiffed())
1405 catch(InvalidPositionException &e){}
1411 Updates usage timers
1413 void Map::timerUpdate(float dtime, float unload_timeout,
1414 core::list<v3s16> *unloaded_blocks)
1416 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1418 core::list<v2s16> sector_deletion_queue;
1419 u32 deleted_blocks_count = 0;
1420 u32 saved_blocks_count = 0;
1422 core::map<v2s16, MapSector*>::Iterator si;
1425 si = m_sectors.getIterator();
1426 for(; si.atEnd() == false; si++)
1428 MapSector *sector = si.getNode()->getValue();
1430 bool all_blocks_deleted = true;
1432 core::list<MapBlock*> blocks;
1433 sector->getBlocks(blocks);
1435 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1436 i != blocks.end(); i++)
1438 MapBlock *block = (*i);
1440 block->incrementUsageTimer(dtime);
1442 if(block->getUsageTimer() > unload_timeout)
1444 v3s16 p = block->getPos();
1447 if(block->getModified() != MOD_STATE_CLEAN
1448 && save_before_unloading)
1451 saved_blocks_count++;
1454 // Delete from memory
1455 sector->deleteBlock(block);
1458 unloaded_blocks->push_back(p);
1460 deleted_blocks_count++;
1464 all_blocks_deleted = false;
1468 if(all_blocks_deleted)
1470 sector_deletion_queue.push_back(si.getNode()->getKey());
1475 // Finally delete the empty sectors
1476 deleteSectors(sector_deletion_queue);
1478 if(deleted_blocks_count != 0)
1480 PrintInfo(infostream); // ServerMap/ClientMap:
1481 infostream<<"Unloaded "<<deleted_blocks_count
1482 <<" blocks from memory";
1483 if(save_before_unloading)
1484 infostream<<", of which "<<saved_blocks_count<<" were written";
1485 infostream<<"."<<std::endl;
1489 void Map::deleteSectors(core::list<v2s16> &list)
1491 core::list<v2s16>::Iterator j;
1492 for(j=list.begin(); j!=list.end(); j++)
1494 MapSector *sector = m_sectors[*j];
1495 // If sector is in sector cache, remove it from there
1496 if(m_sector_cache == sector)
1497 m_sector_cache = NULL;
1498 // Remove from map and delete
1499 m_sectors.remove(*j);
1505 void Map::unloadUnusedData(float timeout,
1506 core::list<v3s16> *deleted_blocks)
1508 core::list<v2s16> sector_deletion_queue;
1509 u32 deleted_blocks_count = 0;
1510 u32 saved_blocks_count = 0;
1512 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1513 for(; si.atEnd() == false; si++)
1515 MapSector *sector = si.getNode()->getValue();
1517 bool all_blocks_deleted = true;
1519 core::list<MapBlock*> blocks;
1520 sector->getBlocks(blocks);
1521 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1522 i != blocks.end(); i++)
1524 MapBlock *block = (*i);
1526 if(block->getUsageTimer() > timeout)
1529 if(block->getModified() != MOD_STATE_CLEAN)
1532 saved_blocks_count++;
1534 // Delete from memory
1535 sector->deleteBlock(block);
1536 deleted_blocks_count++;
1540 all_blocks_deleted = false;
1544 if(all_blocks_deleted)
1546 sector_deletion_queue.push_back(si.getNode()->getKey());
1550 deleteSectors(sector_deletion_queue);
1552 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1553 <<", of which "<<saved_blocks_count<<" were wr."
1556 //return sector_deletion_queue.getSize();
1557 //return deleted_blocks_count;
1561 void Map::PrintInfo(std::ostream &out)
1566 #define WATER_DROP_BOOST 4
1570 NEIGHBOR_SAME_LEVEL,
1573 struct NodeNeighbor {
1579 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1581 DSTACK(__FUNCTION_NAME);
1582 //TimeTaker timer("transformLiquids()");
1585 u32 initial_size = m_transforming_liquid.size();
1587 /*if(initial_size != 0)
1588 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1590 // list of nodes that due to viscosity have not reached their max level height
1591 UniqueQueue<v3s16> must_reflow;
1593 // List of MapBlocks that will require a lighting update (due to lava)
1594 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1596 while(m_transforming_liquid.size() != 0)
1598 // This should be done here so that it is done when continue is used
1599 if(loopcount >= initial_size * 3)
1604 Get a queued transforming liquid node
1606 v3s16 p0 = m_transforming_liquid.pop_front();
1608 MapNode n0 = getNodeNoEx(p0);
1611 Collect information about current node
1613 s8 liquid_level = -1;
1614 u8 liquid_kind = CONTENT_IGNORE;
1615 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1616 switch (liquid_type) {
1618 liquid_level = LIQUID_LEVEL_SOURCE;
1619 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1621 case LIQUID_FLOWING:
1622 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1623 liquid_kind = n0.getContent();
1626 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1627 // continue with the next node.
1628 if (n0.getContent() != CONTENT_AIR)
1630 liquid_kind = CONTENT_AIR;
1635 Collect information about the environment
1637 const v3s16 *dirs = g_6dirs;
1638 NodeNeighbor sources[6]; // surrounding sources
1639 int num_sources = 0;
1640 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1642 NodeNeighbor airs[6]; // surrounding air
1644 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1645 int num_neutrals = 0;
1646 bool flowing_down = false;
1647 for (u16 i = 0; i < 6; i++) {
1648 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1651 nt = NEIGHBOR_UPPER;
1654 nt = NEIGHBOR_LOWER;
1657 v3s16 npos = p0 + dirs[i];
1658 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1659 switch (content_features(nb.n.getContent()).liquid_type) {
1661 if (nb.n.getContent() == CONTENT_AIR) {
1662 airs[num_airs++] = nb;
1663 // if the current node is a water source the neighbor
1664 // should be enqueded for transformation regardless of whether the
1665 // current node changes or not.
1666 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1667 m_transforming_liquid.push_back(npos);
1668 // if the current node happens to be a flowing node, it will start to flow down here.
1669 if (nb.t == NEIGHBOR_LOWER) {
1670 flowing_down = true;
1673 neutrals[num_neutrals++] = nb;
1677 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1678 if (liquid_kind == CONTENT_AIR)
1679 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1680 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1681 neutrals[num_neutrals++] = nb;
1683 sources[num_sources++] = nb;
1686 case LIQUID_FLOWING:
1687 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1688 if (liquid_kind == CONTENT_AIR)
1689 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1690 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1691 neutrals[num_neutrals++] = nb;
1693 flows[num_flows++] = nb;
1694 if (nb.t == NEIGHBOR_LOWER)
1695 flowing_down = true;
1702 decide on the type (and possibly level) of the current node
1704 content_t new_node_content;
1705 s8 new_node_level = -1;
1706 s8 max_node_level = -1;
1707 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1708 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1709 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1710 // it's perfectly safe to use liquid_kind here to determine the new node content.
1711 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1712 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1713 // liquid_kind is set properly, see above
1714 new_node_content = liquid_kind;
1715 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1717 // no surrounding sources, so get the maximum level that can flow into this node
1718 for (u16 i = 0; i < num_flows; i++) {
1719 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1720 switch (flows[i].t) {
1721 case NEIGHBOR_UPPER:
1722 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1723 max_node_level = LIQUID_LEVEL_MAX;
1724 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1725 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1726 } else if (nb_liquid_level > max_node_level)
1727 max_node_level = nb_liquid_level;
1729 case NEIGHBOR_LOWER:
1731 case NEIGHBOR_SAME_LEVEL:
1732 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1733 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1734 max_node_level = nb_liquid_level - 1;
1740 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1741 if (viscosity > 1 && max_node_level != liquid_level) {
1742 // amount to gain, limited by viscosity
1743 // must be at least 1 in absolute value
1744 s8 level_inc = max_node_level - liquid_level;
1745 if (level_inc < -viscosity || level_inc > viscosity)
1746 new_node_level = liquid_level + level_inc/viscosity;
1747 else if (level_inc < 0)
1748 new_node_level = liquid_level - 1;
1749 else if (level_inc > 0)
1750 new_node_level = liquid_level + 1;
1751 if (new_node_level != max_node_level)
1752 must_reflow.push_back(p0);
1754 new_node_level = max_node_level;
1756 if (new_node_level >= 0)
1757 new_node_content = liquid_kind;
1759 new_node_content = CONTENT_AIR;
1764 check if anything has changed. if not, just continue with the next node.
1766 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1767 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1768 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1774 update the current node
1776 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1777 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1778 // set level to last 3 bits, flowing down bit to 4th bit
1779 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1781 // set the liquid level and flow bit to 0
1782 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1784 n0.setContent(new_node_content);
1786 v3s16 blockpos = getNodeBlockPos(p0);
1787 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1789 modified_blocks.insert(blockpos, block);
1790 // If node emits light, MapBlock requires lighting update
1791 if(content_features(n0).light_source != 0)
1792 lighting_modified_blocks[block->getPos()] = block;
1796 enqueue neighbors for update if neccessary
1798 switch (content_features(n0.getContent()).liquid_type) {
1800 case LIQUID_FLOWING:
1801 // make sure source flows into all neighboring nodes
1802 for (u16 i = 0; i < num_flows; i++)
1803 if (flows[i].t != NEIGHBOR_UPPER)
1804 m_transforming_liquid.push_back(flows[i].p);
1805 for (u16 i = 0; i < num_airs; i++)
1806 if (airs[i].t != NEIGHBOR_UPPER)
1807 m_transforming_liquid.push_back(airs[i].p);
1810 // this flow has turned to air; neighboring flows might need to do the same
1811 for (u16 i = 0; i < num_flows; i++)
1812 m_transforming_liquid.push_back(flows[i].p);
1816 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1817 while (must_reflow.size() > 0)
1818 m_transforming_liquid.push_back(must_reflow.pop_front());
1819 updateLighting(lighting_modified_blocks, modified_blocks);
1822 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1824 v3s16 blockpos = getNodeBlockPos(p);
1825 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1826 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1828 infostream<<"Map::getNodeMetadata(): Need to emerge "
1829 <<PP(blockpos)<<std::endl;
1830 block = emergeBlock(blockpos, false);
1834 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1838 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1842 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1844 v3s16 blockpos = getNodeBlockPos(p);
1845 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1846 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1848 infostream<<"Map::setNodeMetadata(): Need to emerge "
1849 <<PP(blockpos)<<std::endl;
1850 block = emergeBlock(blockpos, false);
1854 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1858 block->m_node_metadata.set(p_rel, meta);
1861 void Map::removeNodeMetadata(v3s16 p)
1863 v3s16 blockpos = getNodeBlockPos(p);
1864 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1865 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1868 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1872 block->m_node_metadata.remove(p_rel);
1875 void Map::nodeMetadataStep(float dtime,
1876 core::map<v3s16, MapBlock*> &changed_blocks)
1880 Currently there is no way to ensure that all the necessary
1881 blocks are loaded when this is run. (They might get unloaded)
1882 NOTE: ^- Actually, that might not be so. In a quick test it
1883 reloaded a block with a furnace when I walked back to it from
1886 core::map<v2s16, MapSector*>::Iterator si;
1887 si = m_sectors.getIterator();
1888 for(; si.atEnd() == false; si++)
1890 MapSector *sector = si.getNode()->getValue();
1891 core::list< MapBlock * > sectorblocks;
1892 sector->getBlocks(sectorblocks);
1893 core::list< MapBlock * >::Iterator i;
1894 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1896 MapBlock *block = *i;
1897 bool changed = block->m_node_metadata.step(dtime);
1899 changed_blocks[block->getPos()] = block;
1908 ServerMap::ServerMap(std::string savedir):
1911 m_map_metadata_changed(true),
1913 m_database_read(NULL),
1914 m_database_write(NULL)
1916 infostream<<__FUNCTION_NAME<<std::endl;
1918 //m_chunksize = 8; // Takes a few seconds
1920 if (g_settings->get("fixed_map_seed").empty())
1922 m_seed = (((u64)(myrand()%0xffff)<<0)
1923 + ((u64)(myrand()%0xffff)<<16)
1924 + ((u64)(myrand()%0xffff)<<32)
1925 + ((u64)(myrand()%0xffff)<<48));
1929 m_seed = g_settings->getU64("fixed_map_seed");
1933 Experimental and debug stuff
1940 Try to load map; if not found, create a new one.
1943 m_savedir = savedir;
1944 m_map_saving_enabled = false;
1948 // If directory exists, check contents and load if possible
1949 if(fs::PathExists(m_savedir))
1951 // If directory is empty, it is safe to save into it.
1952 if(fs::GetDirListing(m_savedir).size() == 0)
1954 infostream<<"Server: Empty save directory is valid."
1956 m_map_saving_enabled = true;
1961 // Load map metadata (seed, chunksize)
1964 catch(FileNotGoodException &e){
1965 infostream<<"WARNING: Could not load map metadata"
1966 //<<" Disabling chunk-based generator."
1972 // Load chunk metadata
1975 catch(FileNotGoodException &e){
1976 infostream<<"WARNING: Could not load chunk metadata."
1977 <<" Disabling chunk-based generator."
1982 /*infostream<<"Server: Successfully loaded chunk "
1983 "metadata and sector (0,0) from "<<savedir<<
1984 ", assuming valid save directory."
1987 infostream<<"Server: Successfully loaded map "
1988 <<"and chunk metadata from "<<savedir
1989 <<", assuming valid save directory."
1992 m_map_saving_enabled = true;
1993 // Map loaded, not creating new one
1997 // If directory doesn't exist, it is safe to save to it
1999 m_map_saving_enabled = true;
2002 catch(std::exception &e)
2004 infostream<<"WARNING: Server: Failed to load map from "<<savedir
2005 <<", exception: "<<e.what()<<std::endl;
2006 infostream<<"Please remove the map or fix it."<<std::endl;
2007 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2010 infostream<<"Initializing new map."<<std::endl;
2012 // Create zero sector
2013 emergeSector(v2s16(0,0));
2015 // Initially write whole map
2019 ServerMap::~ServerMap()
2021 infostream<<__FUNCTION_NAME<<std::endl;
2025 if(m_map_saving_enabled)
2027 // Save only changed parts
2029 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2033 infostream<<"Server: map not saved"<<std::endl;
2036 catch(std::exception &e)
2038 infostream<<"Server: Failed to save map to "<<m_savedir
2039 <<", exception: "<<e.what()<<std::endl;
2043 Close database if it was opened
2046 sqlite3_finalize(m_database_read);
2047 if(m_database_write)
2048 sqlite3_finalize(m_database_write);
2050 sqlite3_close(m_database);
2056 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2057 for(; i.atEnd() == false; i++)
2059 MapChunk *chunk = i.getNode()->getValue();
2065 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2067 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2068 if(enable_mapgen_debug_info)
2069 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2070 <<blockpos.Z<<")"<<std::endl;
2072 // Do nothing if not inside limits (+-1 because of neighbors)
2073 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2074 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2080 data->no_op = false;
2081 data->seed = m_seed;
2082 data->blockpos = blockpos;
2085 Create the whole area of this and the neighboring blocks
2088 //TimeTaker timer("initBlockMake() create area");
2090 for(s16 x=-1; x<=1; x++)
2091 for(s16 z=-1; z<=1; z++)
2093 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2094 // Sector metadata is loaded from disk if not already loaded.
2095 ServerMapSector *sector = createSector(sectorpos);
2098 for(s16 y=-1; y<=1; y++)
2100 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2101 //MapBlock *block = createBlock(p);
2102 // 1) get from memory, 2) load from disk
2103 MapBlock *block = emergeBlock(p, false);
2104 // 3) create a blank one
2107 block = createBlock(p);
2110 Block gets sunlight if this is true.
2112 Refer to the map generator heuristics.
2114 bool ug = mapgen::block_is_underground(data->seed, p);
2115 block->setIsUnderground(ug);
2118 // Lighting will not be valid after make_chunk is called
2119 block->setLightingExpired(true);
2120 // Lighting will be calculated
2121 //block->setLightingExpired(false);
2127 Now we have a big empty area.
2129 Make a ManualMapVoxelManipulator that contains this and the
2133 // The area that contains this block and it's neighbors
2134 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2135 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2137 data->vmanip = new ManualMapVoxelManipulator(this);
2138 //data->vmanip->setMap(this);
2142 //TimeTaker timer("initBlockMake() initialEmerge");
2143 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2146 // Data is ready now.
2149 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2150 core::map<v3s16, MapBlock*> &changed_blocks)
2152 v3s16 blockpos = data->blockpos;
2153 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2154 <<blockpos.Z<<")"<<std::endl;*/
2158 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2162 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2164 /*infostream<<"Resulting vmanip:"<<std::endl;
2165 data->vmanip.print(infostream);*/
2168 Blit generated stuff to map
2169 NOTE: blitBackAll adds nearly everything to changed_blocks
2173 //TimeTaker timer("finishBlockMake() blitBackAll");
2174 data->vmanip->blitBackAll(&changed_blocks);
2177 if(enable_mapgen_debug_info)
2178 infostream<<"finishBlockMake: changed_blocks.size()="
2179 <<changed_blocks.size()<<std::endl;
2182 Copy transforming liquid information
2184 while(data->transforming_liquid.size() > 0)
2186 v3s16 p = data->transforming_liquid.pop_front();
2187 m_transforming_liquid.push_back(p);
2193 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2197 Set is_underground flag for lighting with sunlight.
2199 Refer to map generator heuristics.
2201 NOTE: This is done in initChunkMake
2203 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2207 Add sunlight to central block.
2208 This makes in-dark-spawning monsters to not flood the whole thing.
2209 Do not spread the light, though.
2211 /*core::map<v3s16, bool> light_sources;
2212 bool black_air_left = false;
2213 block->propagateSunlight(light_sources, true, &black_air_left);*/
2216 NOTE: Lighting and object adding shouldn't really be here, but
2217 lighting is a bit tricky to move properly to makeBlock.
2218 TODO: Do this the right way anyway, that is, move it to makeBlock.
2219 - There needs to be some way for makeBlock to report back if
2220 the lighting update is going further down because of the
2221 new block blocking light
2226 NOTE: This takes ~60ms, TODO: Investigate why
2229 TimeTaker t("finishBlockMake lighting update");
2231 core::map<v3s16, MapBlock*> lighting_update_blocks;
2234 lighting_update_blocks.insert(block->getPos(), block);
2239 v3s16 p = block->getPos()+v3s16(x,1,z);
2240 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2244 // All modified blocks
2245 // NOTE: Should this be done? If this is not done, then the lighting
2246 // of the others will be updated in a different place, one by one, i
2247 // think... or they might not? Well, at least they are left marked as
2248 // "lighting expired"; it seems that is not handled at all anywhere,
2249 // so enabling this will slow it down A LOT because otherwise it
2250 // would not do this at all. This causes the black trees.
2251 for(core::map<v3s16, MapBlock*>::Iterator
2252 i = changed_blocks.getIterator();
2253 i.atEnd() == false; i++)
2255 lighting_update_blocks.insert(i.getNode()->getKey(),
2256 i.getNode()->getValue());
2258 /*// Also force-add all the upmost blocks for proper sunlight
2259 for(s16 x=-1; x<=1; x++)
2260 for(s16 z=-1; z<=1; z++)
2262 v3s16 p = block->getPos()+v3s16(x,1,z);
2263 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2266 updateLighting(lighting_update_blocks, changed_blocks);
2269 Set lighting to non-expired state in all of them.
2270 This is cheating, but it is not fast enough if all of them
2271 would actually be updated.
2273 for(s16 x=-1; x<=1; x++)
2274 for(s16 y=-1; y<=1; y++)
2275 for(s16 z=-1; z<=1; z++)
2277 v3s16 p = block->getPos()+v3s16(x,y,z);
2278 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2281 if(enable_mapgen_debug_info == false)
2282 t.stop(true); // Hide output
2286 Add random objects to block
2288 mapgen::add_random_objects(block);
2291 Go through changed blocks
2293 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2294 i.atEnd() == false; i++)
2296 MapBlock *block = i.getNode()->getValue();
2299 Update day/night difference cache of the MapBlocks
2301 block->updateDayNightDiff();
2303 Set block as modified
2305 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2309 Set central block as generated
2311 block->setGenerated(true);
2314 Save changed parts of map
2315 NOTE: Will be saved later.
2319 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2320 <<blockpos.Z<<")"<<std::endl;*/
2322 if(enable_mapgen_debug_info)
2325 Analyze resulting blocks
2327 for(s16 x=-1; x<=1; x++)
2328 for(s16 y=-1; y<=1; y++)
2329 for(s16 z=-1; z<=1; z++)
2331 v3s16 p = block->getPos()+v3s16(x,y,z);
2332 MapBlock *block = getBlockNoCreateNoEx(p);
2334 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2335 infostream<<"Generated "<<spos<<": "
2336 <<analyze_block(block)<<std::endl;
2344 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2346 DSTACKF("%s: p2d=(%d,%d)",
2351 Check if it exists already in memory
2353 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2358 Try to load it from disk (with blocks)
2360 //if(loadSectorFull(p2d) == true)
2363 Try to load metadata from disk
2366 if(loadSectorMeta(p2d) == true)
2368 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2371 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2372 throw InvalidPositionException("");
2378 Do not create over-limit
2380 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2381 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2382 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2383 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2384 throw InvalidPositionException("createSector(): pos. over limit");
2387 Generate blank sector
2390 sector = new ServerMapSector(this, p2d);
2392 // Sector position on map in nodes
2393 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2398 m_sectors.insert(p2d, sector);
2404 This is a quick-hand function for calling makeBlock().
2406 MapBlock * ServerMap::generateBlock(
2408 core::map<v3s16, MapBlock*> &modified_blocks
2411 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2413 /*infostream<<"generateBlock(): "
2414 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2417 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2419 TimeTaker timer("generateBlock");
2421 //MapBlock *block = original_dummy;
2423 v2s16 p2d(p.X, p.Z);
2424 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2427 Do not generate over-limit
2429 if(blockpos_over_limit(p))
2431 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2432 throw InvalidPositionException("generateBlock(): pos. over limit");
2436 Create block make data
2438 mapgen::BlockMakeData data;
2439 initBlockMake(&data, p);
2445 TimeTaker t("mapgen::make_block()");
2446 mapgen::make_block(&data);
2448 if(enable_mapgen_debug_info == false)
2449 t.stop(true); // Hide output
2453 Blit data back on map, update lighting, add mobs and whatever this does
2455 finishBlockMake(&data, modified_blocks);
2460 MapBlock *block = getBlockNoCreateNoEx(p);
2468 bool erroneus_content = false;
2469 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2470 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2471 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2474 MapNode n = block->getNode(p);
2475 if(n.getContent() == CONTENT_IGNORE)
2477 infostream<<"CONTENT_IGNORE at "
2478 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2480 erroneus_content = true;
2484 if(erroneus_content)
2493 Generate a completely empty block
2497 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2498 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2500 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2504 n.setContent(CONTENT_AIR);
2506 n.setContent(CONTENT_STONE);
2507 block->setNode(v3s16(x0,y0,z0), n);
2513 if(enable_mapgen_debug_info == false)
2514 timer.stop(true); // Hide output
2519 MapBlock * ServerMap::createBlock(v3s16 p)
2521 DSTACKF("%s: p=(%d,%d,%d)",
2522 __FUNCTION_NAME, p.X, p.Y, p.Z);
2525 Do not create over-limit
2527 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2528 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2529 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2530 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2531 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2532 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2533 throw InvalidPositionException("createBlock(): pos. over limit");
2535 v2s16 p2d(p.X, p.Z);
2538 This will create or load a sector if not found in memory.
2539 If block exists on disk, it will be loaded.
2541 NOTE: On old save formats, this will be slow, as it generates
2542 lighting on blocks for them.
2544 ServerMapSector *sector;
2546 sector = (ServerMapSector*)createSector(p2d);
2547 assert(sector->getId() == MAPSECTOR_SERVER);
2549 catch(InvalidPositionException &e)
2551 infostream<<"createBlock: createSector() failed"<<std::endl;
2555 NOTE: This should not be done, or at least the exception
2556 should not be passed on as std::exception, because it
2557 won't be catched at all.
2559 /*catch(std::exception &e)
2561 infostream<<"createBlock: createSector() failed: "
2562 <<e.what()<<std::endl;
2567 Try to get a block from the sector
2570 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2573 if(block->isDummy())
2578 block = sector->createBlankBlock(block_y);
2582 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2584 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2586 p.X, p.Y, p.Z, allow_generate);
2589 MapBlock *block = getBlockNoCreateNoEx(p);
2590 if(block && block->isDummy() == false)
2595 MapBlock *block = loadBlock(p);
2602 core::map<v3s16, MapBlock*> modified_blocks;
2603 MapBlock *block = generateBlock(p, modified_blocks);
2607 event.type = MEET_OTHER;
2610 // Copy modified_blocks to event
2611 for(core::map<v3s16, MapBlock*>::Iterator
2612 i = modified_blocks.getIterator();
2613 i.atEnd()==false; i++)
2615 event.modified_blocks.insert(i.getNode()->getKey(), false);
2619 dispatchEvent(&event);
2628 s16 ServerMap::findGroundLevel(v2s16 p2d)
2632 Uh, just do something random...
2634 // Find existing map from top to down
2637 v3s16 p(p2d.X, max, p2d.Y);
2638 for(; p.Y>min; p.Y--)
2640 MapNode n = getNodeNoEx(p);
2641 if(n.getContent() != CONTENT_IGNORE)
2646 // If this node is not air, go to plan b
2647 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2649 // Search existing walkable and return it
2650 for(; p.Y>min; p.Y--)
2652 MapNode n = getNodeNoEx(p);
2653 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2662 Determine from map generator noise functions
2665 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2668 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2669 //return (s16)level;
2672 void ServerMap::createDatabase() {
2675 e = sqlite3_exec(m_database,
2676 "CREATE TABLE IF NOT EXISTS `blocks` ("
2677 "`pos` INT NOT NULL PRIMARY KEY,"
2680 , NULL, NULL, NULL);
2681 if(e == SQLITE_ABORT)
2682 throw FileNotGoodException("Could not create database structure");
2684 infostream<<"Server: Database structure was created";
2687 void ServerMap::verifyDatabase() {
2692 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2693 bool needs_create = false;
2697 Open the database connection
2700 createDirs(m_savedir);
2702 if(!fs::PathExists(dbp))
2703 needs_create = true;
2705 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2706 if(d != SQLITE_OK) {
2707 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2708 throw FileNotGoodException("Cannot open database file");
2714 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2715 if(d != SQLITE_OK) {
2716 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2717 throw FileNotGoodException("Cannot prepare read statement");
2720 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2721 if(d != SQLITE_OK) {
2722 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2723 throw FileNotGoodException("Cannot prepare write statement");
2726 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2727 if(d != SQLITE_OK) {
2728 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2729 throw FileNotGoodException("Cannot prepare read statement");
2732 infostream<<"Server: Database opened"<<std::endl;
2736 bool ServerMap::loadFromFolders() {
2737 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2742 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2743 return (sqlite3_int64)pos.Z*16777216 +
2744 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2747 void ServerMap::createDirs(std::string path)
2749 if(fs::CreateAllDirs(path) == false)
2751 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2752 <<"\""<<path<<"\""<<std::endl;
2753 throw BaseException("ServerMap failed to create directory");
2757 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2763 snprintf(cc, 9, "%.4x%.4x",
2764 (unsigned int)pos.X&0xffff,
2765 (unsigned int)pos.Y&0xffff);
2767 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2769 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2770 (unsigned int)pos.X&0xfff,
2771 (unsigned int)pos.Y&0xfff);
2773 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2779 v2s16 ServerMap::getSectorPos(std::string dirname)
2783 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2784 assert(spos != std::string::npos);
2785 if(dirname.size() - spos == 8)
2788 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2790 else if(dirname.size() - spos == 3)
2793 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2794 // Sign-extend the 12 bit values up to 16 bits...
2795 if(x&0x800) x|=0xF000;
2796 if(y&0x800) y|=0xF000;
2803 v2s16 pos((s16)x, (s16)y);
2807 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2809 v2s16 p2d = getSectorPos(sectordir);
2811 if(blockfile.size() != 4){
2812 throw InvalidFilenameException("Invalid block filename");
2815 int r = sscanf(blockfile.c_str(), "%4x", &y);
2817 throw InvalidFilenameException("Invalid block filename");
2818 return v3s16(p2d.X, y, p2d.Y);
2821 std::string ServerMap::getBlockFilename(v3s16 p)
2824 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2828 void ServerMap::save(bool only_changed)
2830 DSTACK(__FUNCTION_NAME);
2831 if(m_map_saving_enabled == false)
2833 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2837 if(only_changed == false)
2838 infostream<<"ServerMap: Saving whole map, this can take time."
2841 if(only_changed == false || m_map_metadata_changed)
2846 u32 sector_meta_count = 0;
2847 u32 block_count = 0;
2848 u32 block_count_all = 0; // Number of blocks in memory
2851 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2852 for(; i.atEnd() == false; i++)
2854 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2855 assert(sector->getId() == MAPSECTOR_SERVER);
2857 if(sector->differs_from_disk || only_changed == false)
2859 saveSectorMeta(sector);
2860 sector_meta_count++;
2862 core::list<MapBlock*> blocks;
2863 sector->getBlocks(blocks);
2864 core::list<MapBlock*>::Iterator j;
2866 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2867 for(j=blocks.begin(); j!=blocks.end(); j++)
2869 MapBlock *block = *j;
2873 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2874 || only_changed == false)
2879 /*infostream<<"ServerMap: Written block ("
2880 <<block->getPos().X<<","
2881 <<block->getPos().Y<<","
2882 <<block->getPos().Z<<")"
2885 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
2891 Only print if something happened or saved whole map
2893 if(only_changed == false || sector_meta_count != 0
2894 || block_count != 0)
2896 infostream<<"ServerMap: Written: "
2897 <<sector_meta_count<<" sector metadata files, "
2898 <<block_count<<" block files"
2899 <<", "<<block_count_all<<" blocks in memory."
2904 static s32 unsignedToSigned(s32 i, s32 max_positive)
2906 if(i < max_positive)
2909 return i - 2*max_positive;
2912 // modulo of a negative number does not work consistently in C
2913 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2917 return mod - ((-i) % mod);
2920 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2922 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2924 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2926 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2927 return v3s16(x,y,z);
2930 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2932 if(loadFromFolders()){
2933 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2934 <<"all blocks that are stored in flat files"<<std::endl;
2940 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2942 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2943 v3s16 p = getIntegerAsBlock(block_i);
2944 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2950 void ServerMap::saveMapMeta()
2952 DSTACK(__FUNCTION_NAME);
2954 infostream<<"ServerMap::saveMapMeta(): "
2958 createDirs(m_savedir);
2960 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2961 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2962 if(os.good() == false)
2964 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2965 <<"could not open"<<fullpath<<std::endl;
2966 throw FileNotGoodException("Cannot open chunk metadata");
2970 params.setU64("seed", m_seed);
2972 params.writeLines(os);
2974 os<<"[end_of_params]\n";
2976 m_map_metadata_changed = false;
2979 void ServerMap::loadMapMeta()
2981 DSTACK(__FUNCTION_NAME);
2983 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2986 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2987 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2988 if(is.good() == false)
2990 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2991 <<"could not open"<<fullpath<<std::endl;
2992 throw FileNotGoodException("Cannot open map metadata");
3000 throw SerializationError
3001 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3003 std::getline(is, line);
3004 std::string trimmedline = trim(line);
3005 if(trimmedline == "[end_of_params]")
3007 params.parseConfigLine(line);
3010 m_seed = params.getU64("seed");
3012 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3015 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3017 DSTACK(__FUNCTION_NAME);
3018 // Format used for writing
3019 u8 version = SER_FMT_VER_HIGHEST;
3021 v2s16 pos = sector->getPos();
3022 std::string dir = getSectorDir(pos);
3025 std::string fullpath = dir + DIR_DELIM + "meta";
3026 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3027 if(o.good() == false)
3028 throw FileNotGoodException("Cannot open sector metafile");
3030 sector->serialize(o, version);
3032 sector->differs_from_disk = false;
3035 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3037 DSTACK(__FUNCTION_NAME);
3039 v2s16 p2d = getSectorPos(sectordir);
3041 ServerMapSector *sector = NULL;
3043 std::string fullpath = sectordir + DIR_DELIM + "meta";
3044 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3045 if(is.good() == false)
3047 // If the directory exists anyway, it probably is in some old
3048 // format. Just go ahead and create the sector.
3049 if(fs::PathExists(sectordir))
3051 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3052 <<fullpath<<" doesn't exist but directory does."
3053 <<" Continuing with a sector with no metadata."
3055 sector = new ServerMapSector(this, p2d);
3056 m_sectors.insert(p2d, sector);
3060 throw FileNotGoodException("Cannot open sector metafile");
3065 sector = ServerMapSector::deSerialize
3066 (is, this, p2d, m_sectors);
3068 saveSectorMeta(sector);
3071 sector->differs_from_disk = false;
3076 bool ServerMap::loadSectorMeta(v2s16 p2d)
3078 DSTACK(__FUNCTION_NAME);
3080 MapSector *sector = NULL;
3082 // The directory layout we're going to load from.
3083 // 1 - original sectors/xxxxzzzz/
3084 // 2 - new sectors2/xxx/zzz/
3085 // If we load from anything but the latest structure, we will
3086 // immediately save to the new one, and remove the old.
3088 std::string sectordir1 = getSectorDir(p2d, 1);
3089 std::string sectordir;
3090 if(fs::PathExists(sectordir1))
3092 sectordir = sectordir1;
3097 sectordir = getSectorDir(p2d, 2);
3101 sector = loadSectorMeta(sectordir, loadlayout != 2);
3103 catch(InvalidFilenameException &e)
3107 catch(FileNotGoodException &e)
3111 catch(std::exception &e)
3120 bool ServerMap::loadSectorFull(v2s16 p2d)
3122 DSTACK(__FUNCTION_NAME);
3124 MapSector *sector = NULL;
3126 // The directory layout we're going to load from.
3127 // 1 - original sectors/xxxxzzzz/
3128 // 2 - new sectors2/xxx/zzz/
3129 // If we load from anything but the latest structure, we will
3130 // immediately save to the new one, and remove the old.
3132 std::string sectordir1 = getSectorDir(p2d, 1);
3133 std::string sectordir;
3134 if(fs::PathExists(sectordir1))
3136 sectordir = sectordir1;
3141 sectordir = getSectorDir(p2d, 2);
3145 sector = loadSectorMeta(sectordir, loadlayout != 2);
3147 catch(InvalidFilenameException &e)
3151 catch(FileNotGoodException &e)
3155 catch(std::exception &e)
3163 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3165 std::vector<fs::DirListNode>::iterator i2;
3166 for(i2=list2.begin(); i2!=list2.end(); i2++)
3172 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3174 catch(InvalidFilenameException &e)
3176 // This catches unknown crap in directory
3182 infostream<<"Sector converted to new layout - deleting "<<
3183 sectordir1<<std::endl;
3184 fs::RecursiveDelete(sectordir1);
3191 void ServerMap::beginSave() {
3193 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3194 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3197 void ServerMap::endSave() {
3199 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3200 infostream<<"WARNING: endSave() failed, map might not have saved.";
3203 void ServerMap::saveBlock(MapBlock *block)
3205 DSTACK(__FUNCTION_NAME);
3207 Dummy blocks are not written
3209 if(block->isDummy())
3211 /*v3s16 p = block->getPos();
3212 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3213 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3217 // Format used for writing
3218 u8 version = SER_FMT_VER_HIGHEST;
3220 v3s16 p3d = block->getPos();
3224 v2s16 p2d(p3d.X, p3d.Z);
3225 std::string sectordir = getSectorDir(p2d);
3227 createDirs(sectordir);
3229 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3230 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3231 if(o.good() == false)
3232 throw FileNotGoodException("Cannot open block data");
3235 [0] u8 serialization version
3241 std::ostringstream o(std::ios_base::binary);
3243 o.write((char*)&version, 1);
3246 block->serialize(o, version);
3248 // Write extra data stored on disk
3249 block->serializeDiskExtra(o, version);
3251 // Write block to database
3253 std::string tmp = o.str();
3254 const char *bytes = tmp.c_str();
3256 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3257 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3258 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3259 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3260 int written = sqlite3_step(m_database_write);
3261 if(written != SQLITE_DONE)
3262 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3263 <<sqlite3_errmsg(m_database)<<std::endl;
3264 // Make ready for later reuse
3265 sqlite3_reset(m_database_write);
3267 // We just wrote it to the disk so clear modified flag
3268 block->resetModified();
3271 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3273 DSTACK(__FUNCTION_NAME);
3275 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3278 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3279 if(is.good() == false)
3280 throw FileNotGoodException("Cannot open block file");
3282 v3s16 p3d = getBlockPos(sectordir, blockfile);
3283 v2s16 p2d(p3d.X, p3d.Z);
3285 assert(sector->getPos() == p2d);
3287 u8 version = SER_FMT_VER_INVALID;
3288 is.read((char*)&version, 1);
3291 throw SerializationError("ServerMap::loadBlock(): Failed"
3292 " to read MapBlock version");
3294 /*u32 block_size = MapBlock::serializedLength(version);
3295 SharedBuffer<u8> data(block_size);
3296 is.read((char*)*data, block_size);*/
3298 // This will always return a sector because we're the server
3299 //MapSector *sector = emergeSector(p2d);
3301 MapBlock *block = NULL;
3302 bool created_new = false;
3303 block = sector->getBlockNoCreateNoEx(p3d.Y);
3306 block = sector->createBlankBlockNoInsert(p3d.Y);
3311 block->deSerialize(is, version);
3313 // Read extra data stored on disk
3314 block->deSerializeDiskExtra(is, version);
3316 // If it's a new block, insert it to the map
3318 sector->insertBlock(block);
3321 Save blocks loaded in old format in new format
3324 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3328 // Should be in database now, so delete the old file
3329 fs::RecursiveDelete(fullpath);
3332 // We just loaded it from the disk, so it's up-to-date.
3333 block->resetModified();
3336 catch(SerializationError &e)
3338 infostream<<"WARNING: Invalid block data on disk "
3339 <<"fullpath="<<fullpath
3340 <<" (SerializationError). "
3341 <<"what()="<<e.what()
3343 //" Ignoring. A new one will be generated.
3346 // TODO: Backup file; name is in fullpath.
3350 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3352 DSTACK(__FUNCTION_NAME);
3355 std::istringstream is(*blob, std::ios_base::binary);
3357 u8 version = SER_FMT_VER_INVALID;
3358 is.read((char*)&version, 1);
3361 throw SerializationError("ServerMap::loadBlock(): Failed"
3362 " to read MapBlock version");
3364 /*u32 block_size = MapBlock::serializedLength(version);
3365 SharedBuffer<u8> data(block_size);
3366 is.read((char*)*data, block_size);*/
3368 // This will always return a sector because we're the server
3369 //MapSector *sector = emergeSector(p2d);
3371 MapBlock *block = NULL;
3372 bool created_new = false;
3373 block = sector->getBlockNoCreateNoEx(p3d.Y);
3376 block = sector->createBlankBlockNoInsert(p3d.Y);
3381 block->deSerialize(is, version);
3383 // Read extra data stored on disk
3384 block->deSerializeDiskExtra(is, version);
3386 // If it's a new block, insert it to the map
3388 sector->insertBlock(block);
3391 Save blocks loaded in old format in new format
3394 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3399 // We just loaded it from, so it's up-to-date.
3400 block->resetModified();
3403 catch(SerializationError &e)
3405 infostream<<"WARNING: Invalid block data in database "
3406 <<" (SerializationError). "
3407 <<"what()="<<e.what()
3409 //" Ignoring. A new one will be generated.
3412 // TODO: Copy to a backup database.
3416 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3418 DSTACK(__FUNCTION_NAME);
3420 v2s16 p2d(blockpos.X, blockpos.Z);
3422 if(!loadFromFolders()) {
3425 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3426 infostream<<"WARNING: Could not bind block position for load: "
3427 <<sqlite3_errmsg(m_database)<<std::endl;
3428 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3430 Make sure sector is loaded
3432 MapSector *sector = createSector(p2d);
3437 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3438 size_t len = sqlite3_column_bytes(m_database_read, 0);
3440 std::string datastr(data, len);
3442 loadBlock(&datastr, blockpos, sector, false);
3444 sqlite3_step(m_database_read);
3445 // We should never get more than 1 row, so ok to reset
3446 sqlite3_reset(m_database_read);
3448 return getBlockNoCreateNoEx(blockpos);
3450 sqlite3_reset(m_database_read);
3452 // Not found in database, try the files
3455 // The directory layout we're going to load from.
3456 // 1 - original sectors/xxxxzzzz/
3457 // 2 - new sectors2/xxx/zzz/
3458 // If we load from anything but the latest structure, we will
3459 // immediately save to the new one, and remove the old.
3461 std::string sectordir1 = getSectorDir(p2d, 1);
3462 std::string sectordir;
3463 if(fs::PathExists(sectordir1))
3465 sectordir = sectordir1;
3470 sectordir = getSectorDir(p2d, 2);
3474 Make sure sector is loaded
3476 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3480 sector = loadSectorMeta(sectordir, loadlayout != 2);
3482 catch(InvalidFilenameException &e)
3486 catch(FileNotGoodException &e)
3490 catch(std::exception &e)
3497 Make sure file exists
3500 std::string blockfilename = getBlockFilename(blockpos);
3501 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3505 Load block and save it to the database
3507 loadBlock(sectordir, blockfilename, sector, true);
3508 return getBlockNoCreateNoEx(blockpos);
3511 void ServerMap::PrintInfo(std::ostream &out)
3522 ClientMap::ClientMap(
3524 MapDrawControl &control,
3525 scene::ISceneNode* parent,
3526 scene::ISceneManager* mgr,
3530 scene::ISceneNode(parent, mgr, id),
3533 m_camera_position(0,0,0),
3534 m_camera_direction(0,0,1),
3537 m_camera_mutex.Init();
3538 assert(m_camera_mutex.IsInitialized());
3540 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3541 BS*1000000,BS*1000000,BS*1000000);
3544 ClientMap::~ClientMap()
3546 /*JMutexAutoLock lock(mesh_mutex);
3555 MapSector * ClientMap::emergeSector(v2s16 p2d)
3557 DSTACK(__FUNCTION_NAME);
3558 // Check that it doesn't exist already
3560 return getSectorNoGenerate(p2d);
3562 catch(InvalidPositionException &e)
3567 ClientMapSector *sector = new ClientMapSector(this, p2d);
3570 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3571 m_sectors.insert(p2d, sector);
3578 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3580 DSTACK(__FUNCTION_NAME);
3581 ClientMapSector *sector = NULL;
3583 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3585 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3589 sector = (ClientMapSector*)n->getValue();
3590 assert(sector->getId() == MAPSECTOR_CLIENT);
3594 sector = new ClientMapSector(this, p2d);
3596 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3597 m_sectors.insert(p2d, sector);
3601 sector->deSerialize(is);
3605 void ClientMap::OnRegisterSceneNode()
3609 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3610 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3613 ISceneNode::OnRegisterSceneNode();
3616 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3617 float start_off, float end_off, u32 needed_count)
3619 float d0 = (float)BS * p0.getDistanceFrom(p1);
3621 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3623 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3625 for(float s=start_off; s<d0+end_off; s+=step){
3626 v3f pf = p0f + uf * s;
3627 v3s16 p = floatToInt(pf, BS);
3628 MapNode n = map->getNodeNoEx(p);
3629 bool is_transparent = false;
3630 ContentFeatures &f = content_features(n);
3631 if(f.solidness == 0)
3632 is_transparent = (f.visual_solidness != 2);
3634 is_transparent = (f.solidness != 2);
3635 if(!is_transparent){
3637 if(count >= needed_count)
3645 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3647 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3648 DSTACK(__FUNCTION_NAME);
3650 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3653 if(pass == scene::ESNRP_SOLID)
3654 prefix = "CM: solid: ";
3656 prefix = "CM: transparent: ";
3659 This is called two times per frame, reset on the non-transparent one
3661 if(pass == scene::ESNRP_SOLID)
3663 m_last_drawn_sectors.clear();
3667 Get time for measuring timeout.
3669 Measuring time is very useful for long delays when the
3670 machine is swapping a lot.
3672 int time1 = time(0);
3674 //u32 daynight_ratio = m_client->getDayNightRatio();
3676 m_camera_mutex.Lock();
3677 v3f camera_position = m_camera_position;
3678 v3f camera_direction = m_camera_direction;
3679 f32 camera_fov = m_camera_fov;
3680 m_camera_mutex.Unlock();
3683 Get all blocks and draw all visible ones
3686 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3688 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3690 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3691 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3693 // Take a fair amount as we will be dropping more out later
3694 // Umm... these additions are a bit strange but they are needed.
3696 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3697 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3698 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3700 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3701 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3702 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3704 u32 vertex_count = 0;
3705 u32 meshbuffer_count = 0;
3707 // For limiting number of mesh updates per frame
3708 u32 mesh_update_count = 0;
3710 // Number of blocks in rendering range
3711 u32 blocks_in_range = 0;
3712 // Number of blocks occlusion culled
3713 u32 blocks_occlusion_culled = 0;
3714 // Number of blocks in rendering range but don't have a mesh
3715 u32 blocks_in_range_without_mesh = 0;
3716 // Blocks that had mesh that would have been drawn according to
3717 // rendering range (if max blocks limit didn't kick in)
3718 u32 blocks_would_have_drawn = 0;
3719 // Blocks that were drawn and had a mesh
3720 u32 blocks_drawn = 0;
3721 // Blocks which had a corresponding meshbuffer for this pass
3722 u32 blocks_had_pass_meshbuf = 0;
3723 // Blocks from which stuff was actually drawn
3724 u32 blocks_without_stuff = 0;
3727 Collect a set of blocks for drawing
3730 core::map<v3s16, MapBlock*> drawset;
3733 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3735 for(core::map<v2s16, MapSector*>::Iterator
3736 si = m_sectors.getIterator();
3737 si.atEnd() == false; si++)
3739 MapSector *sector = si.getNode()->getValue();
3740 v2s16 sp = sector->getPos();
3742 if(m_control.range_all == false)
3744 if(sp.X < p_blocks_min.X
3745 || sp.X > p_blocks_max.X
3746 || sp.Y < p_blocks_min.Z
3747 || sp.Y > p_blocks_max.Z)
3751 core::list< MapBlock * > sectorblocks;
3752 sector->getBlocks(sectorblocks);
3755 Loop through blocks in sector
3758 u32 sector_blocks_drawn = 0;
3760 core::list< MapBlock * >::Iterator i;
3761 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3763 MapBlock *block = *i;
3766 Compare block position to camera position, skip
3767 if not seen on display
3770 float range = 100000 * BS;
3771 if(m_control.range_all == false)
3772 range = m_control.wanted_range * BS;
3775 if(isBlockInSight(block->getPos(), camera_position,
3776 camera_direction, camera_fov,
3777 range, &d) == false)
3782 // This is ugly (spherical distance limit?)
3783 /*if(m_control.range_all == false &&
3784 d - 0.5*BS*MAP_BLOCKSIZE > range)
3791 Update expired mesh (used for day/night change)
3793 It doesn't work exactly like it should now with the
3794 tasked mesh update but whatever.
3797 bool mesh_expired = false;
3800 JMutexAutoLock lock(block->mesh_mutex);
3802 mesh_expired = block->getMeshExpired();
3804 // Mesh has not been expired and there is no mesh:
3805 // block has no content
3806 if(block->mesh == NULL && mesh_expired == false){
3807 blocks_in_range_without_mesh++;
3812 f32 faraway = BS*50;
3813 //f32 faraway = m_control.wanted_range * BS;
3816 This has to be done with the mesh_mutex unlocked
3818 // Pretty random but this should work somewhat nicely
3819 if(mesh_expired && (
3820 (mesh_update_count < 3
3821 && (d < faraway || mesh_update_count < 2)
3824 (m_control.range_all && mesh_update_count < 20)
3827 /*if(mesh_expired && mesh_update_count < 6
3828 && (d < faraway || mesh_update_count < 3))*/
3830 mesh_update_count++;
3832 // Mesh has been expired: generate new mesh
3833 //block->updateMesh(daynight_ratio);
3834 m_client->addUpdateMeshTask(block->getPos());
3836 mesh_expired = false;
3844 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3845 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3847 float stepfac = 1.1;
3848 float startoff = BS*1;
3849 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3850 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3851 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3852 u32 needed_count = 1;
3854 isOccluded(this, spn, cpn + v3s16(0,0,0),
3855 step, stepfac, startoff, endoff, needed_count) &&
3856 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
3857 step, stepfac, startoff, endoff, needed_count) &&
3858 isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
3859 step, stepfac, startoff, endoff, needed_count) &&
3860 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
3861 step, stepfac, startoff, endoff, needed_count) &&
3862 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
3863 step, stepfac, startoff, endoff, needed_count) &&
3864 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3865 step, stepfac, startoff, endoff, needed_count) &&
3866 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3867 step, stepfac, startoff, endoff, needed_count) &&
3868 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3869 step, stepfac, startoff, endoff, needed_count) &&
3870 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3871 step, stepfac, startoff, endoff, needed_count)
3874 blocks_occlusion_culled++;
3878 // This block is in range. Reset usage timer.
3879 block->resetUsageTimer();
3882 Ignore if mesh doesn't exist
3885 JMutexAutoLock lock(block->mesh_mutex);
3887 scene::SMesh *mesh = block->mesh;
3890 blocks_in_range_without_mesh++;
3895 // Limit block count in case of a sudden increase
3896 blocks_would_have_drawn++;
3897 if(blocks_drawn >= m_control.wanted_max_blocks
3898 && m_control.range_all == false
3899 && d > m_control.wanted_min_range * BS)
3903 drawset[block->getPos()] = block;
3905 sector_blocks_drawn++;
3908 } // foreach sectorblocks
3910 if(sector_blocks_drawn != 0)
3911 m_last_drawn_sectors[sp] = true;
3916 Draw the selected MapBlocks
3920 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3922 int timecheck_counter = 0;
3923 for(core::map<v3s16, MapBlock*>::Iterator
3924 i = drawset.getIterator();
3925 i.atEnd() == false; i++)
3928 timecheck_counter++;
3929 if(timecheck_counter > 50)
3931 timecheck_counter = 0;
3932 int time2 = time(0);
3933 if(time2 > time1 + 4)
3935 infostream<<"ClientMap::renderMap(): "
3936 "Rendering takes ages, returning."
3943 MapBlock *block = i.getNode()->getValue();
3946 Draw the faces of the block
3949 JMutexAutoLock lock(block->mesh_mutex);
3951 scene::SMesh *mesh = block->mesh;
3954 u32 c = mesh->getMeshBufferCount();
3955 bool stuff_actually_drawn = false;
3956 for(u32 i=0; i<c; i++)
3958 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3959 const video::SMaterial& material = buf->getMaterial();
3960 video::IMaterialRenderer* rnd =
3961 driver->getMaterialRenderer(material.MaterialType);
3962 bool transparent = (rnd && rnd->isTransparent());
3963 // Render transparent on transparent pass and likewise.
3964 if(transparent == is_transparent_pass)
3966 if(buf->getVertexCount() == 0)
3967 errorstream<<"Block ["<<analyze_block(block)
3968 <<"] contains an empty meshbuf"<<std::endl;
3970 This *shouldn't* hurt too much because Irrlicht
3971 doesn't change opengl textures if the old
3972 material has the same texture.
3974 driver->setMaterial(buf->getMaterial());
3975 driver->drawMeshBuffer(buf);
3976 vertex_count += buf->getVertexCount();
3978 stuff_actually_drawn = true;
3981 if(stuff_actually_drawn)
3982 blocks_had_pass_meshbuf++;
3984 blocks_without_stuff++;
3989 // Log only on solid pass because values are the same
3990 if(pass == scene::ESNRP_SOLID){
3991 g_profiler->avg("CM: blocks in range", blocks_in_range);
3992 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
3993 if(blocks_in_range != 0)
3994 g_profiler->avg("CM: blocks in range without mesh (frac)",
3995 (float)blocks_in_range_without_mesh/blocks_in_range);
3996 g_profiler->avg("CM: blocks drawn", blocks_drawn);
3999 g_profiler->avg(prefix+"vertices drawn", vertex_count);
4000 if(blocks_had_pass_meshbuf != 0)
4001 g_profiler->avg(prefix+"meshbuffers per block",
4002 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
4003 if(blocks_drawn != 0)
4004 g_profiler->avg(prefix+"empty blocks (frac)",
4005 (float)blocks_without_stuff / blocks_drawn);
4007 m_control.blocks_drawn = blocks_drawn;
4008 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4010 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4011 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4014 void ClientMap::renderPostFx()
4016 // Sadly ISceneManager has no "post effects" render pass, in that case we
4017 // could just register for that and handle it in renderMap().
4019 m_camera_mutex.Lock();
4020 v3f camera_position = m_camera_position;
4021 m_camera_mutex.Unlock();
4023 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4025 // - If the player is in a solid node, make everything black.
4026 // - If the player is in liquid, draw a semi-transparent overlay.
4027 ContentFeatures& features = content_features(n);
4028 video::SColor post_effect_color = features.post_effect_color;
4029 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4031 post_effect_color = video::SColor(255, 0, 0, 0);
4033 if (post_effect_color.getAlpha() != 0)
4035 // Draw a full-screen rectangle
4036 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4037 v2u32 ss = driver->getScreenSize();
4038 core::rect<s32> rect(0,0, ss.X, ss.Y);
4039 driver->draw2DRectangle(post_effect_color, rect);
4043 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4044 core::map<v3s16, MapBlock*> *affected_blocks)
4046 bool changed = false;
4048 Add it to all blocks touching it
4051 v3s16(0,0,0), // this
4052 v3s16(0,0,1), // back
4053 v3s16(0,1,0), // top
4054 v3s16(1,0,0), // right
4055 v3s16(0,0,-1), // front
4056 v3s16(0,-1,0), // bottom
4057 v3s16(-1,0,0), // left
4059 for(u16 i=0; i<7; i++)
4061 v3s16 p2 = p + dirs[i];
4062 // Block position of neighbor (or requested) node
4063 v3s16 blockpos = getNodeBlockPos(p2);
4064 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4065 if(blockref == NULL)
4067 // Relative position of requested node
4068 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4069 if(blockref->setTempMod(relpos, mod))
4074 if(changed && affected_blocks!=NULL)
4076 for(u16 i=0; i<7; i++)
4078 v3s16 p2 = p + dirs[i];
4079 // Block position of neighbor (or requested) node
4080 v3s16 blockpos = getNodeBlockPos(p2);
4081 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4082 if(blockref == NULL)
4084 affected_blocks->insert(blockpos, blockref);
4090 bool ClientMap::clearTempMod(v3s16 p,
4091 core::map<v3s16, MapBlock*> *affected_blocks)
4093 bool changed = false;
4095 v3s16(0,0,0), // this
4096 v3s16(0,0,1), // back
4097 v3s16(0,1,0), // top
4098 v3s16(1,0,0), // right
4099 v3s16(0,0,-1), // front
4100 v3s16(0,-1,0), // bottom
4101 v3s16(-1,0,0), // left
4103 for(u16 i=0; i<7; i++)
4105 v3s16 p2 = p + dirs[i];
4106 // Block position of neighbor (or requested) node
4107 v3s16 blockpos = getNodeBlockPos(p2);
4108 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4109 if(blockref == NULL)
4111 // Relative position of requested node
4112 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4113 if(blockref->clearTempMod(relpos))
4118 if(changed && affected_blocks!=NULL)
4120 for(u16 i=0; i<7; i++)
4122 v3s16 p2 = p + dirs[i];
4123 // Block position of neighbor (or requested) node
4124 v3s16 blockpos = getNodeBlockPos(p2);
4125 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4126 if(blockref == NULL)
4128 affected_blocks->insert(blockpos, blockref);
4134 void ClientMap::expireMeshes(bool only_daynight_diffed)
4136 TimeTaker timer("expireMeshes()");
4138 core::map<v2s16, MapSector*>::Iterator si;
4139 si = m_sectors.getIterator();
4140 for(; si.atEnd() == false; si++)
4142 MapSector *sector = si.getNode()->getValue();
4144 core::list< MapBlock * > sectorblocks;
4145 sector->getBlocks(sectorblocks);
4147 core::list< MapBlock * >::Iterator i;
4148 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4150 MapBlock *block = *i;
4152 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4158 JMutexAutoLock lock(block->mesh_mutex);
4159 if(block->mesh != NULL)
4161 /*block->mesh->drop();
4162 block->mesh = NULL;*/
4163 block->setMeshExpired(true);
4170 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4172 assert(mapType() == MAPTYPE_CLIENT);
4175 v3s16 p = blockpos + v3s16(0,0,0);
4176 MapBlock *b = getBlockNoCreate(p);
4177 b->updateMesh(daynight_ratio);
4178 //b->setMeshExpired(true);
4180 catch(InvalidPositionException &e){}
4183 v3s16 p = blockpos + v3s16(-1,0,0);
4184 MapBlock *b = getBlockNoCreate(p);
4185 b->updateMesh(daynight_ratio);
4186 //b->setMeshExpired(true);
4188 catch(InvalidPositionException &e){}
4190 v3s16 p = blockpos + v3s16(0,-1,0);
4191 MapBlock *b = getBlockNoCreate(p);
4192 b->updateMesh(daynight_ratio);
4193 //b->setMeshExpired(true);
4195 catch(InvalidPositionException &e){}
4197 v3s16 p = blockpos + v3s16(0,0,-1);
4198 MapBlock *b = getBlockNoCreate(p);
4199 b->updateMesh(daynight_ratio);
4200 //b->setMeshExpired(true);
4202 catch(InvalidPositionException &e){}
4207 Update mesh of block in which the node is, and if the node is at the
4208 leading edge, update the appropriate leading blocks too.
4210 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4218 v3s16 blockposes[4];
4219 for(u32 i=0; i<4; i++)
4221 v3s16 np = nodepos + dirs[i];
4222 blockposes[i] = getNodeBlockPos(np);
4223 // Don't update mesh of block if it has been done already
4224 bool already_updated = false;
4225 for(u32 j=0; j<i; j++)
4227 if(blockposes[j] == blockposes[i])
4229 already_updated = true;
4236 MapBlock *b = getBlockNoCreate(blockposes[i]);
4237 b->updateMesh(daynight_ratio);
4242 void ClientMap::PrintInfo(std::ostream &out)
4253 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4258 MapVoxelManipulator::~MapVoxelManipulator()
4260 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4264 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4266 TimeTaker timer1("emerge", &emerge_time);
4268 // Units of these are MapBlocks
4269 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4270 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4272 VoxelArea block_area_nodes
4273 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4275 addArea(block_area_nodes);
4277 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4278 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4279 for(s32 x=p_min.X; x<=p_max.X; x++)
4282 core::map<v3s16, bool>::Node *n;
4283 n = m_loaded_blocks.find(p);
4287 bool block_data_inexistent = false;
4290 TimeTaker timer1("emerge load", &emerge_load_time);
4292 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4293 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4295 a.print(infostream);
4296 infostream<<std::endl;*/
4298 MapBlock *block = m_map->getBlockNoCreate(p);
4299 if(block->isDummy())
4300 block_data_inexistent = true;
4302 block->copyTo(*this);
4304 catch(InvalidPositionException &e)
4306 block_data_inexistent = true;
4309 if(block_data_inexistent)
4311 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4312 // Fill with VOXELFLAG_INEXISTENT
4313 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4314 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4316 s32 i = m_area.index(a.MinEdge.X,y,z);
4317 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4321 m_loaded_blocks.insert(p, !block_data_inexistent);
4324 //infostream<<"emerge done"<<std::endl;
4328 SUGG: Add an option to only update eg. water and air nodes.
4329 This will make it interfere less with important stuff if
4332 void MapVoxelManipulator::blitBack
4333 (core::map<v3s16, MapBlock*> & modified_blocks)
4335 if(m_area.getExtent() == v3s16(0,0,0))
4338 //TimeTaker timer1("blitBack");
4340 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4341 <<m_loaded_blocks.size()<<std::endl;*/
4344 Initialize block cache
4346 v3s16 blockpos_last;
4347 MapBlock *block = NULL;
4348 bool block_checked_in_modified = false;
4350 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4351 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4352 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4356 u8 f = m_flags[m_area.index(p)];
4357 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4360 MapNode &n = m_data[m_area.index(p)];
4362 v3s16 blockpos = getNodeBlockPos(p);
4367 if(block == NULL || blockpos != blockpos_last){
4368 block = m_map->getBlockNoCreate(blockpos);
4369 blockpos_last = blockpos;
4370 block_checked_in_modified = false;
4373 // Calculate relative position in block
4374 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4376 // Don't continue if nothing has changed here
4377 if(block->getNode(relpos) == n)
4380 //m_map->setNode(m_area.MinEdge + p, n);
4381 block->setNode(relpos, n);
4384 Make sure block is in modified_blocks
4386 if(block_checked_in_modified == false)
4388 modified_blocks[blockpos] = block;
4389 block_checked_in_modified = true;
4392 catch(InvalidPositionException &e)
4398 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4399 MapVoxelManipulator(map),
4400 m_create_area(false)
4404 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4408 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4410 // Just create the area so that it can be pointed to
4411 VoxelManipulator::emerge(a, caller_id);
4414 void ManualMapVoxelManipulator::initialEmerge(
4415 v3s16 blockpos_min, v3s16 blockpos_max)
4417 TimeTaker timer1("initialEmerge", &emerge_time);
4419 // Units of these are MapBlocks
4420 v3s16 p_min = blockpos_min;
4421 v3s16 p_max = blockpos_max;
4423 VoxelArea block_area_nodes
4424 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4426 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4429 infostream<<"initialEmerge: area: ";
4430 block_area_nodes.print(infostream);
4431 infostream<<" ("<<size_MB<<"MB)";
4432 infostream<<std::endl;
4435 addArea(block_area_nodes);
4437 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4438 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4439 for(s32 x=p_min.X; x<=p_max.X; x++)
4442 core::map<v3s16, bool>::Node *n;
4443 n = m_loaded_blocks.find(p);
4447 bool block_data_inexistent = false;
4450 TimeTaker timer1("emerge load", &emerge_load_time);
4452 MapBlock *block = m_map->getBlockNoCreate(p);
4453 if(block->isDummy())
4454 block_data_inexistent = true;
4456 block->copyTo(*this);
4458 catch(InvalidPositionException &e)
4460 block_data_inexistent = true;
4463 if(block_data_inexistent)
4466 Mark area inexistent
4468 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4469 // Fill with VOXELFLAG_INEXISTENT
4470 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4471 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4473 s32 i = m_area.index(a.MinEdge.X,y,z);
4474 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4478 m_loaded_blocks.insert(p, !block_data_inexistent);
4482 void ManualMapVoxelManipulator::blitBackAll(
4483 core::map<v3s16, MapBlock*> * modified_blocks)
4485 if(m_area.getExtent() == v3s16(0,0,0))
4489 Copy data of all blocks
4491 for(core::map<v3s16, bool>::Iterator
4492 i = m_loaded_blocks.getIterator();
4493 i.atEnd() == false; i++)
4495 v3s16 p = i.getNode()->getKey();
4496 bool existed = i.getNode()->getValue();
4497 if(existed == false)
4499 // The Great Bug was found using this
4500 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4501 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4505 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4508 infostream<<"WARNING: "<<__FUNCTION_NAME
4509 <<": got NULL block "
4510 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4515 block->copyFrom(*this);
4518 modified_blocks->insert(p, block);