3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
30 #include "nodemetadata.h"
31 #include "content_mapnode.h"
33 #include <IMaterialRenderer.h>
38 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
41 SQLite format specification:
42 - Initially only replaces sectors/ and sectors2/
44 If map.sqlite does not exist in the save dir
45 or the block was not found in the database
46 the map will try to load from sectors folder.
47 In either case, map.sqlite will be created
48 and all future saves will save there.
50 Structure of map.sqlite:
61 Map::Map(std::ostream &dout):
65 /*m_sector_mutex.Init();
66 assert(m_sector_mutex.IsInitialized());*/
74 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
75 for(; i.atEnd() == false; i++)
77 MapSector *sector = i.getNode()->getValue();
82 void Map::addEventReceiver(MapEventReceiver *event_receiver)
84 m_event_receivers.insert(event_receiver, false);
87 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
89 if(m_event_receivers.find(event_receiver) == NULL)
91 m_event_receivers.remove(event_receiver);
94 void Map::dispatchEvent(MapEditEvent *event)
96 for(core::map<MapEventReceiver*, bool>::Iterator
97 i = m_event_receivers.getIterator();
98 i.atEnd()==false; i++)
100 MapEventReceiver* event_receiver = i.getNode()->getKey();
101 event_receiver->onMapEditEvent(event);
105 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
107 if(m_sector_cache != NULL && p == m_sector_cache_p){
108 MapSector * sector = m_sector_cache;
112 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
117 MapSector *sector = n->getValue();
119 // Cache the last result
120 m_sector_cache_p = p;
121 m_sector_cache = sector;
126 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
128 return getSectorNoGenerateNoExNoLock(p);
131 MapSector * Map::getSectorNoGenerate(v2s16 p)
133 MapSector *sector = getSectorNoGenerateNoEx(p);
135 throw InvalidPositionException();
140 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
142 v2s16 p2d(p3d.X, p3d.Z);
143 MapSector * sector = getSectorNoGenerateNoEx(p2d);
146 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
150 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
152 MapBlock *block = getBlockNoCreateNoEx(p3d);
154 throw InvalidPositionException();
158 bool Map::isNodeUnderground(v3s16 p)
160 v3s16 blockpos = getNodeBlockPos(p);
162 MapBlock * block = getBlockNoCreate(blockpos);
163 return block->getIsUnderground();
165 catch(InvalidPositionException &e)
171 bool Map::isValidPosition(v3s16 p)
173 v3s16 blockpos = getNodeBlockPos(p);
174 MapBlock *block = getBlockNoCreate(blockpos);
175 return (block != NULL);
178 // Returns a CONTENT_IGNORE node if not found
179 MapNode Map::getNodeNoEx(v3s16 p)
181 v3s16 blockpos = getNodeBlockPos(p);
182 MapBlock *block = getBlockNoCreateNoEx(blockpos);
184 return MapNode(CONTENT_IGNORE);
185 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
186 return block->getNodeNoCheck(relpos);
189 // throws InvalidPositionException if not found
190 MapNode Map::getNode(v3s16 p)
192 v3s16 blockpos = getNodeBlockPos(p);
193 MapBlock *block = getBlockNoCreateNoEx(blockpos);
195 throw InvalidPositionException();
196 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
197 return block->getNodeNoCheck(relpos);
200 // throws InvalidPositionException if not found
201 void Map::setNode(v3s16 p, MapNode & n)
203 v3s16 blockpos = getNodeBlockPos(p);
204 MapBlock *block = getBlockNoCreate(blockpos);
205 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
206 block->setNodeNoCheck(relpos, n);
211 Goes recursively through the neighbours of the node.
213 Alters only transparent nodes.
215 If the lighting of the neighbour is lower than the lighting of
216 the node was (before changing it to 0 at the step before), the
217 lighting of the neighbour is set to 0 and then the same stuff
218 repeats for the neighbour.
220 The ending nodes of the routine are stored in light_sources.
221 This is useful when a light is removed. In such case, this
222 routine can be called for the light node and then again for
223 light_sources to re-light the area without the removed light.
225 values of from_nodes are lighting values.
227 void Map::unspreadLight(enum LightBank bank,
228 core::map<v3s16, u8> & from_nodes,
229 core::map<v3s16, bool> & light_sources,
230 core::map<v3s16, MapBlock*> & modified_blocks)
233 v3s16(0,0,1), // back
235 v3s16(1,0,0), // right
236 v3s16(0,0,-1), // front
237 v3s16(0,-1,0), // bottom
238 v3s16(-1,0,0), // left
241 if(from_nodes.size() == 0)
244 u32 blockchangecount = 0;
246 core::map<v3s16, u8> unlighted_nodes;
247 core::map<v3s16, u8>::Iterator j;
248 j = from_nodes.getIterator();
251 Initialize block cache
254 MapBlock *block = NULL;
255 // Cache this a bit, too
256 bool block_checked_in_modified = false;
258 for(; j.atEnd() == false; j++)
260 v3s16 pos = j.getNode()->getKey();
261 v3s16 blockpos = getNodeBlockPos(pos);
263 // Only fetch a new block if the block position has changed
265 if(block == NULL || blockpos != blockpos_last){
266 block = getBlockNoCreate(blockpos);
267 blockpos_last = blockpos;
269 block_checked_in_modified = false;
273 catch(InvalidPositionException &e)
281 // Calculate relative position in block
282 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
284 // Get node straight from the block
285 MapNode n = block->getNode(relpos);
287 u8 oldlight = j.getNode()->getValue();
289 // Loop through 6 neighbors
290 for(u16 i=0; i<6; i++)
292 // Get the position of the neighbor node
293 v3s16 n2pos = pos + dirs[i];
295 // Get the block where the node is located
296 v3s16 blockpos = getNodeBlockPos(n2pos);
300 // Only fetch a new block if the block position has changed
302 if(block == NULL || blockpos != blockpos_last){
303 block = getBlockNoCreate(blockpos);
304 blockpos_last = blockpos;
306 block_checked_in_modified = false;
310 catch(InvalidPositionException &e)
315 // Calculate relative position in block
316 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
317 // Get node straight from the block
318 MapNode n2 = block->getNode(relpos);
320 bool changed = false;
322 //TODO: Optimize output by optimizing light_sources?
325 If the neighbor is dimmer than what was specified
326 as oldlight (the light of the previous node)
328 if(n2.getLight(bank) < oldlight)
331 And the neighbor is transparent and it has some light
333 if(n2.light_propagates() && n2.getLight(bank) != 0)
336 Set light to 0 and add to queue
339 u8 current_light = n2.getLight(bank);
340 n2.setLight(bank, 0);
341 block->setNode(relpos, n2);
343 unlighted_nodes.insert(n2pos, current_light);
347 Remove from light_sources if it is there
348 NOTE: This doesn't happen nearly at all
350 /*if(light_sources.find(n2pos))
352 infostream<<"Removed from light_sources"<<std::endl;
353 light_sources.remove(n2pos);
358 if(light_sources.find(n2pos) != NULL)
359 light_sources.remove(n2pos);*/
362 light_sources.insert(n2pos, true);
365 // Add to modified_blocks
366 if(changed == true && block_checked_in_modified == false)
368 // If the block is not found in modified_blocks, add.
369 if(modified_blocks.find(blockpos) == NULL)
371 modified_blocks.insert(blockpos, block);
373 block_checked_in_modified = true;
376 catch(InvalidPositionException &e)
383 /*infostream<<"unspreadLight(): Changed block "
384 <<blockchangecount<<" times"
385 <<" for "<<from_nodes.size()<<" nodes"
388 if(unlighted_nodes.size() > 0)
389 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
393 A single-node wrapper of the above
395 void Map::unLightNeighbors(enum LightBank bank,
396 v3s16 pos, u8 lightwas,
397 core::map<v3s16, bool> & light_sources,
398 core::map<v3s16, MapBlock*> & modified_blocks)
400 core::map<v3s16, u8> from_nodes;
401 from_nodes.insert(pos, lightwas);
403 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
407 Lights neighbors of from_nodes, collects all them and then
410 void Map::spreadLight(enum LightBank bank,
411 core::map<v3s16, bool> & from_nodes,
412 core::map<v3s16, MapBlock*> & modified_blocks)
414 const v3s16 dirs[6] = {
415 v3s16(0,0,1), // back
417 v3s16(1,0,0), // right
418 v3s16(0,0,-1), // front
419 v3s16(0,-1,0), // bottom
420 v3s16(-1,0,0), // left
423 if(from_nodes.size() == 0)
426 u32 blockchangecount = 0;
428 core::map<v3s16, bool> lighted_nodes;
429 core::map<v3s16, bool>::Iterator j;
430 j = from_nodes.getIterator();
433 Initialize block cache
436 MapBlock *block = NULL;
437 // Cache this a bit, too
438 bool block_checked_in_modified = false;
440 for(; j.atEnd() == false; j++)
441 //for(; j != from_nodes.end(); j++)
443 v3s16 pos = j.getNode()->getKey();
445 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
446 v3s16 blockpos = getNodeBlockPos(pos);
448 // Only fetch a new block if the block position has changed
450 if(block == NULL || blockpos != blockpos_last){
451 block = getBlockNoCreate(blockpos);
452 blockpos_last = blockpos;
454 block_checked_in_modified = false;
458 catch(InvalidPositionException &e)
466 // Calculate relative position in block
467 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
469 // Get node straight from the block
470 MapNode n = block->getNode(relpos);
472 u8 oldlight = n.getLight(bank);
473 u8 newlight = diminish_light(oldlight);
475 // Loop through 6 neighbors
476 for(u16 i=0; i<6; i++){
477 // Get the position of the neighbor node
478 v3s16 n2pos = pos + dirs[i];
480 // Get the block where the node is located
481 v3s16 blockpos = getNodeBlockPos(n2pos);
485 // Only fetch a new block if the block position has changed
487 if(block == NULL || blockpos != blockpos_last){
488 block = getBlockNoCreate(blockpos);
489 blockpos_last = blockpos;
491 block_checked_in_modified = false;
495 catch(InvalidPositionException &e)
500 // Calculate relative position in block
501 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
502 // Get node straight from the block
503 MapNode n2 = block->getNode(relpos);
505 bool changed = false;
507 If the neighbor is brighter than the current node,
508 add to list (it will light up this node on its turn)
510 if(n2.getLight(bank) > undiminish_light(oldlight))
512 lighted_nodes.insert(n2pos, true);
513 //lighted_nodes.push_back(n2pos);
517 If the neighbor is dimmer than how much light this node
518 would spread on it, add to list
520 if(n2.getLight(bank) < newlight)
522 if(n2.light_propagates())
524 n2.setLight(bank, newlight);
525 block->setNode(relpos, n2);
526 lighted_nodes.insert(n2pos, true);
527 //lighted_nodes.push_back(n2pos);
532 // Add to modified_blocks
533 if(changed == true && block_checked_in_modified == false)
535 // If the block is not found in modified_blocks, add.
536 if(modified_blocks.find(blockpos) == NULL)
538 modified_blocks.insert(blockpos, block);
540 block_checked_in_modified = true;
543 catch(InvalidPositionException &e)
550 /*infostream<<"spreadLight(): Changed block "
551 <<blockchangecount<<" times"
552 <<" for "<<from_nodes.size()<<" nodes"
555 if(lighted_nodes.size() > 0)
556 spreadLight(bank, lighted_nodes, modified_blocks);
560 A single-node source variation of the above.
562 void Map::lightNeighbors(enum LightBank bank,
564 core::map<v3s16, MapBlock*> & modified_blocks)
566 core::map<v3s16, bool> from_nodes;
567 from_nodes.insert(pos, true);
568 spreadLight(bank, from_nodes, modified_blocks);
571 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
574 v3s16(0,0,1), // back
576 v3s16(1,0,0), // right
577 v3s16(0,0,-1), // front
578 v3s16(0,-1,0), // bottom
579 v3s16(-1,0,0), // left
582 u8 brightest_light = 0;
583 v3s16 brightest_pos(0,0,0);
584 bool found_something = false;
586 // Loop through 6 neighbors
587 for(u16 i=0; i<6; i++){
588 // Get the position of the neighbor node
589 v3s16 n2pos = p + dirs[i];
594 catch(InvalidPositionException &e)
598 if(n2.getLight(bank) > brightest_light || found_something == false){
599 brightest_light = n2.getLight(bank);
600 brightest_pos = n2pos;
601 found_something = true;
605 if(found_something == false)
606 throw InvalidPositionException();
608 return brightest_pos;
612 Propagates sunlight down from a node.
613 Starting point gets sunlight.
615 Returns the lowest y value of where the sunlight went.
617 Mud is turned into grass in where the sunlight stops.
619 s16 Map::propagateSunlight(v3s16 start,
620 core::map<v3s16, MapBlock*> & modified_blocks)
625 v3s16 pos(start.X, y, start.Z);
627 v3s16 blockpos = getNodeBlockPos(pos);
630 block = getBlockNoCreate(blockpos);
632 catch(InvalidPositionException &e)
637 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
638 MapNode n = block->getNode(relpos);
640 if(n.sunlight_propagates())
642 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
643 block->setNode(relpos, n);
645 modified_blocks.insert(blockpos, block);
649 /*// Turn mud into grass
650 if(n.getContent() == CONTENT_MUD)
652 n.setContent(CONTENT_GRASS);
653 block->setNode(relpos, n);
654 modified_blocks.insert(blockpos, block);
657 // Sunlight goes no further
664 void Map::updateLighting(enum LightBank bank,
665 core::map<v3s16, MapBlock*> & a_blocks,
666 core::map<v3s16, MapBlock*> & modified_blocks)
668 /*m_dout<<DTIME<<"Map::updateLighting(): "
669 <<a_blocks.size()<<" blocks."<<std::endl;*/
671 //TimeTaker timer("updateLighting");
675 //u32 count_was = modified_blocks.size();
677 core::map<v3s16, MapBlock*> blocks_to_update;
679 core::map<v3s16, bool> light_sources;
681 core::map<v3s16, u8> unlight_from;
683 core::map<v3s16, MapBlock*>::Iterator i;
684 i = a_blocks.getIterator();
685 for(; i.atEnd() == false; i++)
687 MapBlock *block = i.getNode()->getValue();
691 // Don't bother with dummy blocks.
695 v3s16 pos = block->getPos();
696 modified_blocks.insert(pos, block);
698 blocks_to_update.insert(pos, block);
701 Clear all light from block
703 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
704 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
705 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
710 MapNode n = block->getNode(v3s16(x,y,z));
711 u8 oldlight = n.getLight(bank);
713 block->setNode(v3s16(x,y,z), n);
715 // Collect borders for unlighting
716 if(x==0 || x == MAP_BLOCKSIZE-1
717 || y==0 || y == MAP_BLOCKSIZE-1
718 || z==0 || z == MAP_BLOCKSIZE-1)
720 v3s16 p_map = p + v3s16(
723 MAP_BLOCKSIZE*pos.Z);
724 unlight_from.insert(p_map, oldlight);
727 catch(InvalidPositionException &e)
730 This would happen when dealing with a
734 infostream<<"updateLighting(): InvalidPositionException"
739 if(bank == LIGHTBANK_DAY)
741 bool bottom_valid = block->propagateSunlight(light_sources);
743 // If bottom is valid, we're done.
747 else if(bank == LIGHTBANK_NIGHT)
749 // For night lighting, sunlight is not propagated
754 // Invalid lighting bank
758 /*infostream<<"Bottom for sunlight-propagated block ("
759 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
762 // Bottom sunlight is not valid; get the block and loop to it
766 block = getBlockNoCreate(pos);
768 catch(InvalidPositionException &e)
777 Enable this to disable proper lighting for speeding up map
778 generation for testing or whatever
781 //if(g_settings->get(""))
783 core::map<v3s16, MapBlock*>::Iterator i;
784 i = blocks_to_update.getIterator();
785 for(; i.atEnd() == false; i++)
787 MapBlock *block = i.getNode()->getValue();
788 v3s16 p = block->getPos();
789 block->setLightingExpired(false);
797 TimeTaker timer("unspreadLight");
798 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
803 u32 diff = modified_blocks.size() - count_was;
804 count_was = modified_blocks.size();
805 infostream<<"unspreadLight modified "<<diff<<std::endl;
809 TimeTaker timer("spreadLight");
810 spreadLight(bank, light_sources, modified_blocks);
815 u32 diff = modified_blocks.size() - count_was;
816 count_was = modified_blocks.size();
817 infostream<<"spreadLight modified "<<diff<<std::endl;
822 //MapVoxelManipulator vmanip(this);
824 // Make a manual voxel manipulator and load all the blocks
825 // that touch the requested blocks
826 ManualMapVoxelManipulator vmanip(this);
827 core::map<v3s16, MapBlock*>::Iterator i;
828 i = blocks_to_update.getIterator();
829 for(; i.atEnd() == false; i++)
831 MapBlock *block = i.getNode()->getValue();
832 v3s16 p = block->getPos();
834 // Add all surrounding blocks
835 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
838 Add all surrounding blocks that have up-to-date lighting
839 NOTE: This doesn't quite do the job (not everything
840 appropriate is lighted)
842 /*for(s16 z=-1; z<=1; z++)
843 for(s16 y=-1; y<=1; y++)
844 for(s16 x=-1; x<=1; x++)
847 MapBlock *block = getBlockNoCreateNoEx(p);
852 if(block->getLightingExpired())
854 vmanip.initialEmerge(p, p);
857 // Lighting of block will be updated completely
858 block->setLightingExpired(false);
862 //TimeTaker timer("unSpreadLight");
863 vmanip.unspreadLight(bank, unlight_from, light_sources);
866 //TimeTaker timer("spreadLight");
867 vmanip.spreadLight(bank, light_sources);
870 //TimeTaker timer("blitBack");
871 vmanip.blitBack(modified_blocks);
873 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
877 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
880 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
881 core::map<v3s16, MapBlock*> & modified_blocks)
883 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
884 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
887 Update information about whether day and night light differ
889 for(core::map<v3s16, MapBlock*>::Iterator
890 i = modified_blocks.getIterator();
891 i.atEnd() == false; i++)
893 MapBlock *block = i.getNode()->getValue();
894 block->updateDayNightDiff();
900 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
901 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
904 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
905 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
908 From this node to nodes underneath:
909 If lighting is sunlight (1.0), unlight neighbours and
914 v3s16 toppos = p + v3s16(0,1,0);
915 v3s16 bottompos = p + v3s16(0,-1,0);
917 bool node_under_sunlight = true;
918 core::map<v3s16, bool> light_sources;
921 If there is a node at top and it doesn't have sunlight,
922 there has not been any sunlight going down.
924 Otherwise there probably is.
927 MapNode topnode = getNode(toppos);
929 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
930 node_under_sunlight = false;
932 catch(InvalidPositionException &e)
938 If the new node is solid and there is grass below, change it to mud
940 if(content_features(n).walkable == true)
943 MapNode bottomnode = getNode(bottompos);
945 if(bottomnode.getContent() == CONTENT_GRASS
946 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
948 bottomnode.setContent(CONTENT_MUD);
949 setNode(bottompos, bottomnode);
952 catch(InvalidPositionException &e)
960 If the new node is mud and it is under sunlight, change it
963 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
965 n.setContent(CONTENT_GRASS);
970 Remove all light that has come out of this node
973 enum LightBank banks[] =
978 for(s32 i=0; i<2; i++)
980 enum LightBank bank = banks[i];
982 u8 lightwas = getNode(p).getLight(bank);
984 // Add the block of the added node to modified_blocks
985 v3s16 blockpos = getNodeBlockPos(p);
986 MapBlock * block = getBlockNoCreate(blockpos);
987 assert(block != NULL);
988 modified_blocks.insert(blockpos, block);
990 assert(isValidPosition(p));
992 // Unlight neighbours of node.
993 // This means setting light of all consequent dimmer nodes
995 // This also collects the nodes at the border which will spread
996 // light again into this.
997 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1003 If node lets sunlight through and is under sunlight, it has
1006 if(node_under_sunlight && content_features(n).sunlight_propagates)
1008 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1012 Set the node on the map
1021 NodeMetadata *meta_proto = content_features(n).initial_metadata;
1024 NodeMetadata *meta = meta_proto->clone();
1025 meta->setOwner(player_name);
1026 setNodeMetadata(p, meta);
1030 If node is under sunlight and doesn't let sunlight through,
1031 take all sunlighted nodes under it and clear light from them
1032 and from where the light has been spread.
1033 TODO: This could be optimized by mass-unlighting instead
1036 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1040 //m_dout<<DTIME<<"y="<<y<<std::endl;
1041 v3s16 n2pos(p.X, y, p.Z);
1045 n2 = getNode(n2pos);
1047 catch(InvalidPositionException &e)
1052 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1054 unLightNeighbors(LIGHTBANK_DAY,
1055 n2pos, n2.getLight(LIGHTBANK_DAY),
1056 light_sources, modified_blocks);
1057 n2.setLight(LIGHTBANK_DAY, 0);
1065 for(s32 i=0; i<2; i++)
1067 enum LightBank bank = banks[i];
1070 Spread light from all nodes that might be capable of doing so
1072 spreadLight(bank, light_sources, modified_blocks);
1076 Update information about whether day and night light differ
1078 for(core::map<v3s16, MapBlock*>::Iterator
1079 i = modified_blocks.getIterator();
1080 i.atEnd() == false; i++)
1082 MapBlock *block = i.getNode()->getValue();
1083 block->updateDayNightDiff();
1087 Add neighboring liquid nodes and the node itself if it is
1088 liquid (=water node was added) to transform queue.
1091 v3s16(0,0,0), // self
1092 v3s16(0,0,1), // back
1093 v3s16(0,1,0), // top
1094 v3s16(1,0,0), // right
1095 v3s16(0,0,-1), // front
1096 v3s16(0,-1,0), // bottom
1097 v3s16(-1,0,0), // left
1099 for(u16 i=0; i<7; i++)
1104 v3s16 p2 = p + dirs[i];
1106 MapNode n2 = getNode(p2);
1107 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1109 m_transforming_liquid.push_back(p2);
1112 }catch(InvalidPositionException &e)
1120 void Map::removeNodeAndUpdate(v3s16 p,
1121 core::map<v3s16, MapBlock*> &modified_blocks)
1123 /*PrintInfo(m_dout);
1124 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1125 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1127 bool node_under_sunlight = true;
1129 v3s16 toppos = p + v3s16(0,1,0);
1131 // Node will be replaced with this
1132 content_t replace_material = CONTENT_AIR;
1135 If there is a node at top and it doesn't have sunlight,
1136 there will be no sunlight going down.
1139 MapNode topnode = getNode(toppos);
1141 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1142 node_under_sunlight = false;
1144 catch(InvalidPositionException &e)
1148 core::map<v3s16, bool> light_sources;
1150 enum LightBank banks[] =
1155 for(s32 i=0; i<2; i++)
1157 enum LightBank bank = banks[i];
1160 Unlight neighbors (in case the node is a light source)
1162 unLightNeighbors(bank, p,
1163 getNode(p).getLight(bank),
1164 light_sources, modified_blocks);
1168 Remove node metadata
1171 removeNodeMetadata(p);
1175 This also clears the lighting.
1179 n.setContent(replace_material);
1182 for(s32 i=0; i<2; i++)
1184 enum LightBank bank = banks[i];
1187 Recalculate lighting
1189 spreadLight(bank, light_sources, modified_blocks);
1192 // Add the block of the removed node to modified_blocks
1193 v3s16 blockpos = getNodeBlockPos(p);
1194 MapBlock * block = getBlockNoCreate(blockpos);
1195 assert(block != NULL);
1196 modified_blocks.insert(blockpos, block);
1199 If the removed node was under sunlight, propagate the
1200 sunlight down from it and then light all neighbors
1201 of the propagated blocks.
1203 if(node_under_sunlight)
1205 s16 ybottom = propagateSunlight(p, modified_blocks);
1206 /*m_dout<<DTIME<<"Node was under sunlight. "
1207 "Propagating sunlight";
1208 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1210 for(; y >= ybottom; y--)
1212 v3s16 p2(p.X, y, p.Z);
1213 /*m_dout<<DTIME<<"lighting neighbors of node ("
1214 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1216 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1221 // Set the lighting of this node to 0
1222 // TODO: Is this needed? Lighting is cleared up there already.
1224 MapNode n = getNode(p);
1225 n.setLight(LIGHTBANK_DAY, 0);
1228 catch(InvalidPositionException &e)
1234 for(s32 i=0; i<2; i++)
1236 enum LightBank bank = banks[i];
1238 // Get the brightest neighbour node and propagate light from it
1239 v3s16 n2p = getBrightestNeighbour(bank, p);
1241 MapNode n2 = getNode(n2p);
1242 lightNeighbors(bank, n2p, modified_blocks);
1244 catch(InvalidPositionException &e)
1250 Update information about whether day and night light differ
1252 for(core::map<v3s16, MapBlock*>::Iterator
1253 i = modified_blocks.getIterator();
1254 i.atEnd() == false; i++)
1256 MapBlock *block = i.getNode()->getValue();
1257 block->updateDayNightDiff();
1261 Add neighboring liquid nodes and this node to transform queue.
1262 (it's vital for the node itself to get updated last.)
1265 v3s16(0,0,1), // back
1266 v3s16(0,1,0), // top
1267 v3s16(1,0,0), // right
1268 v3s16(0,0,-1), // front
1269 v3s16(0,-1,0), // bottom
1270 v3s16(-1,0,0), // left
1271 v3s16(0,0,0), // self
1273 for(u16 i=0; i<7; i++)
1278 v3s16 p2 = p + dirs[i];
1280 MapNode n2 = getNode(p2);
1281 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1283 m_transforming_liquid.push_back(p2);
1286 }catch(InvalidPositionException &e)
1292 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1295 event.type = MEET_ADDNODE;
1299 bool succeeded = true;
1301 core::map<v3s16, MapBlock*> modified_blocks;
1302 std::string st = std::string("");
1303 addNodeAndUpdate(p, n, modified_blocks, st);
1305 // Copy modified_blocks to event
1306 for(core::map<v3s16, MapBlock*>::Iterator
1307 i = modified_blocks.getIterator();
1308 i.atEnd()==false; i++)
1310 event.modified_blocks.insert(i.getNode()->getKey(), false);
1313 catch(InvalidPositionException &e){
1317 dispatchEvent(&event);
1322 bool Map::removeNodeWithEvent(v3s16 p)
1325 event.type = MEET_REMOVENODE;
1328 bool succeeded = true;
1330 core::map<v3s16, MapBlock*> modified_blocks;
1331 removeNodeAndUpdate(p, modified_blocks);
1333 // Copy modified_blocks to event
1334 for(core::map<v3s16, MapBlock*>::Iterator
1335 i = modified_blocks.getIterator();
1336 i.atEnd()==false; i++)
1338 event.modified_blocks.insert(i.getNode()->getKey(), false);
1341 catch(InvalidPositionException &e){
1345 dispatchEvent(&event);
1350 bool Map::dayNightDiffed(v3s16 blockpos)
1353 v3s16 p = blockpos + v3s16(0,0,0);
1354 MapBlock *b = getBlockNoCreate(p);
1355 if(b->dayNightDiffed())
1358 catch(InvalidPositionException &e){}
1361 v3s16 p = blockpos + v3s16(-1,0,0);
1362 MapBlock *b = getBlockNoCreate(p);
1363 if(b->dayNightDiffed())
1366 catch(InvalidPositionException &e){}
1368 v3s16 p = blockpos + v3s16(0,-1,0);
1369 MapBlock *b = getBlockNoCreate(p);
1370 if(b->dayNightDiffed())
1373 catch(InvalidPositionException &e){}
1375 v3s16 p = blockpos + v3s16(0,0,-1);
1376 MapBlock *b = getBlockNoCreate(p);
1377 if(b->dayNightDiffed())
1380 catch(InvalidPositionException &e){}
1383 v3s16 p = blockpos + v3s16(1,0,0);
1384 MapBlock *b = getBlockNoCreate(p);
1385 if(b->dayNightDiffed())
1388 catch(InvalidPositionException &e){}
1390 v3s16 p = blockpos + v3s16(0,1,0);
1391 MapBlock *b = getBlockNoCreate(p);
1392 if(b->dayNightDiffed())
1395 catch(InvalidPositionException &e){}
1397 v3s16 p = blockpos + v3s16(0,0,1);
1398 MapBlock *b = getBlockNoCreate(p);
1399 if(b->dayNightDiffed())
1402 catch(InvalidPositionException &e){}
1408 Updates usage timers
1410 void Map::timerUpdate(float dtime, float unload_timeout,
1411 core::list<v3s16> *unloaded_blocks)
1413 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1415 core::list<v2s16> sector_deletion_queue;
1416 u32 deleted_blocks_count = 0;
1417 u32 saved_blocks_count = 0;
1419 core::map<v2s16, MapSector*>::Iterator si;
1422 si = m_sectors.getIterator();
1423 for(; si.atEnd() == false; si++)
1425 MapSector *sector = si.getNode()->getValue();
1427 bool all_blocks_deleted = true;
1429 core::list<MapBlock*> blocks;
1430 sector->getBlocks(blocks);
1432 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1433 i != blocks.end(); i++)
1435 MapBlock *block = (*i);
1437 block->incrementUsageTimer(dtime);
1439 if(block->getUsageTimer() > unload_timeout)
1441 v3s16 p = block->getPos();
1444 if(block->getModified() != MOD_STATE_CLEAN
1445 && save_before_unloading)
1448 saved_blocks_count++;
1451 // Delete from memory
1452 sector->deleteBlock(block);
1455 unloaded_blocks->push_back(p);
1457 deleted_blocks_count++;
1461 all_blocks_deleted = false;
1465 if(all_blocks_deleted)
1467 sector_deletion_queue.push_back(si.getNode()->getKey());
1472 // Finally delete the empty sectors
1473 deleteSectors(sector_deletion_queue);
1475 if(deleted_blocks_count != 0)
1477 PrintInfo(infostream); // ServerMap/ClientMap:
1478 infostream<<"Unloaded "<<deleted_blocks_count
1479 <<" blocks from memory";
1480 if(save_before_unloading)
1481 infostream<<", of which "<<saved_blocks_count<<" were written";
1482 infostream<<"."<<std::endl;
1486 void Map::deleteSectors(core::list<v2s16> &list)
1488 core::list<v2s16>::Iterator j;
1489 for(j=list.begin(); j!=list.end(); j++)
1491 MapSector *sector = m_sectors[*j];
1492 // If sector is in sector cache, remove it from there
1493 if(m_sector_cache == sector)
1494 m_sector_cache = NULL;
1495 // Remove from map and delete
1496 m_sectors.remove(*j);
1502 void Map::unloadUnusedData(float timeout,
1503 core::list<v3s16> *deleted_blocks)
1505 core::list<v2s16> sector_deletion_queue;
1506 u32 deleted_blocks_count = 0;
1507 u32 saved_blocks_count = 0;
1509 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1510 for(; si.atEnd() == false; si++)
1512 MapSector *sector = si.getNode()->getValue();
1514 bool all_blocks_deleted = true;
1516 core::list<MapBlock*> blocks;
1517 sector->getBlocks(blocks);
1518 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1519 i != blocks.end(); i++)
1521 MapBlock *block = (*i);
1523 if(block->getUsageTimer() > timeout)
1526 if(block->getModified() != MOD_STATE_CLEAN)
1529 saved_blocks_count++;
1531 // Delete from memory
1532 sector->deleteBlock(block);
1533 deleted_blocks_count++;
1537 all_blocks_deleted = false;
1541 if(all_blocks_deleted)
1543 sector_deletion_queue.push_back(si.getNode()->getKey());
1547 deleteSectors(sector_deletion_queue);
1549 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1550 <<", of which "<<saved_blocks_count<<" were wr."
1553 //return sector_deletion_queue.getSize();
1554 //return deleted_blocks_count;
1558 void Map::PrintInfo(std::ostream &out)
1563 #define WATER_DROP_BOOST 4
1567 NEIGHBOR_SAME_LEVEL,
1570 struct NodeNeighbor {
1576 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1578 DSTACK(__FUNCTION_NAME);
1579 //TimeTaker timer("transformLiquids()");
1582 u32 initial_size = m_transforming_liquid.size();
1584 /*if(initial_size != 0)
1585 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1587 // list of nodes that due to viscosity have not reached their max level height
1588 UniqueQueue<v3s16> must_reflow;
1590 // List of MapBlocks that will require a lighting update (due to lava)
1591 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1593 while(m_transforming_liquid.size() != 0)
1595 // This should be done here so that it is done when continue is used
1596 if(loopcount >= initial_size * 3)
1601 Get a queued transforming liquid node
1603 v3s16 p0 = m_transforming_liquid.pop_front();
1605 MapNode n0 = getNodeNoEx(p0);
1608 Collect information about current node
1610 s8 liquid_level = -1;
1611 u8 liquid_kind = CONTENT_IGNORE;
1612 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1613 switch (liquid_type) {
1615 liquid_level = LIQUID_LEVEL_SOURCE;
1616 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1618 case LIQUID_FLOWING:
1619 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1620 liquid_kind = n0.getContent();
1623 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1624 // continue with the next node.
1625 if (n0.getContent() != CONTENT_AIR)
1627 liquid_kind = CONTENT_AIR;
1632 Collect information about the environment
1634 const v3s16 *dirs = g_6dirs;
1635 NodeNeighbor sources[6]; // surrounding sources
1636 int num_sources = 0;
1637 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1639 NodeNeighbor airs[6]; // surrounding air
1641 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1642 int num_neutrals = 0;
1643 bool flowing_down = false;
1644 for (u16 i = 0; i < 6; i++) {
1645 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1648 nt = NEIGHBOR_UPPER;
1651 nt = NEIGHBOR_LOWER;
1654 v3s16 npos = p0 + dirs[i];
1655 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1656 switch (content_features(nb.n.getContent()).liquid_type) {
1658 if (nb.n.getContent() == CONTENT_AIR) {
1659 airs[num_airs++] = nb;
1660 // if the current node is a water source the neighbor
1661 // should be enqueded for transformation regardless of whether the
1662 // current node changes or not.
1663 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1664 m_transforming_liquid.push_back(npos);
1665 // if the current node happens to be a flowing node, it will start to flow down here.
1666 if (nb.t == NEIGHBOR_LOWER) {
1667 flowing_down = true;
1670 neutrals[num_neutrals++] = nb;
1674 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1675 if (liquid_kind == CONTENT_AIR)
1676 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1677 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1678 neutrals[num_neutrals++] = nb;
1680 sources[num_sources++] = nb;
1683 case LIQUID_FLOWING:
1684 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1685 if (liquid_kind == CONTENT_AIR)
1686 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1687 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1688 neutrals[num_neutrals++] = nb;
1690 flows[num_flows++] = nb;
1691 if (nb.t == NEIGHBOR_LOWER)
1692 flowing_down = true;
1699 decide on the type (and possibly level) of the current node
1701 content_t new_node_content;
1702 s8 new_node_level = -1;
1703 s8 max_node_level = -1;
1704 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1705 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1706 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1707 // it's perfectly safe to use liquid_kind here to determine the new node content.
1708 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1709 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1710 // liquid_kind is set properly, see above
1711 new_node_content = liquid_kind;
1712 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1714 // no surrounding sources, so get the maximum level that can flow into this node
1715 for (u16 i = 0; i < num_flows; i++) {
1716 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1717 switch (flows[i].t) {
1718 case NEIGHBOR_UPPER:
1719 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1720 max_node_level = LIQUID_LEVEL_MAX;
1721 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1722 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1723 } else if (nb_liquid_level > max_node_level)
1724 max_node_level = nb_liquid_level;
1726 case NEIGHBOR_LOWER:
1728 case NEIGHBOR_SAME_LEVEL:
1729 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1730 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1731 max_node_level = nb_liquid_level - 1;
1737 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1738 if (viscosity > 1 && max_node_level != liquid_level) {
1739 // amount to gain, limited by viscosity
1740 // must be at least 1 in absolute value
1741 s8 level_inc = max_node_level - liquid_level;
1742 if (level_inc < -viscosity || level_inc > viscosity)
1743 new_node_level = liquid_level + level_inc/viscosity;
1744 else if (level_inc < 0)
1745 new_node_level = liquid_level - 1;
1746 else if (level_inc > 0)
1747 new_node_level = liquid_level + 1;
1748 if (new_node_level != max_node_level)
1749 must_reflow.push_back(p0);
1751 new_node_level = max_node_level;
1753 if (new_node_level >= 0)
1754 new_node_content = liquid_kind;
1756 new_node_content = CONTENT_AIR;
1761 check if anything has changed. if not, just continue with the next node.
1763 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1764 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1765 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1771 update the current node
1773 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1774 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1775 // set level to last 3 bits, flowing down bit to 4th bit
1776 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1778 // set the liquid level and flow bit to 0
1779 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1781 n0.setContent(new_node_content);
1783 v3s16 blockpos = getNodeBlockPos(p0);
1784 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1786 modified_blocks.insert(blockpos, block);
1787 // If node emits light, MapBlock requires lighting update
1788 if(content_features(n0).light_source != 0)
1789 lighting_modified_blocks[block->getPos()] = block;
1793 enqueue neighbors for update if neccessary
1795 switch (content_features(n0.getContent()).liquid_type) {
1797 case LIQUID_FLOWING:
1798 // make sure source flows into all neighboring nodes
1799 for (u16 i = 0; i < num_flows; i++)
1800 if (flows[i].t != NEIGHBOR_UPPER)
1801 m_transforming_liquid.push_back(flows[i].p);
1802 for (u16 i = 0; i < num_airs; i++)
1803 if (airs[i].t != NEIGHBOR_UPPER)
1804 m_transforming_liquid.push_back(airs[i].p);
1807 // this flow has turned to air; neighboring flows might need to do the same
1808 for (u16 i = 0; i < num_flows; i++)
1809 m_transforming_liquid.push_back(flows[i].p);
1813 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1814 while (must_reflow.size() > 0)
1815 m_transforming_liquid.push_back(must_reflow.pop_front());
1816 updateLighting(lighting_modified_blocks, modified_blocks);
1819 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1821 v3s16 blockpos = getNodeBlockPos(p);
1822 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1823 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1825 infostream<<"Map::getNodeMetadata(): Need to emerge "
1826 <<PP(blockpos)<<std::endl;
1827 block = emergeBlock(blockpos, false);
1831 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1835 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1839 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1841 v3s16 blockpos = getNodeBlockPos(p);
1842 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1843 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1845 infostream<<"Map::setNodeMetadata(): Need to emerge "
1846 <<PP(blockpos)<<std::endl;
1847 block = emergeBlock(blockpos, false);
1851 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1855 block->m_node_metadata.set(p_rel, meta);
1858 void Map::removeNodeMetadata(v3s16 p)
1860 v3s16 blockpos = getNodeBlockPos(p);
1861 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1862 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1865 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1869 block->m_node_metadata.remove(p_rel);
1872 void Map::nodeMetadataStep(float dtime,
1873 core::map<v3s16, MapBlock*> &changed_blocks)
1877 Currently there is no way to ensure that all the necessary
1878 blocks are loaded when this is run. (They might get unloaded)
1879 NOTE: ^- Actually, that might not be so. In a quick test it
1880 reloaded a block with a furnace when I walked back to it from
1883 core::map<v2s16, MapSector*>::Iterator si;
1884 si = m_sectors.getIterator();
1885 for(; si.atEnd() == false; si++)
1887 MapSector *sector = si.getNode()->getValue();
1888 core::list< MapBlock * > sectorblocks;
1889 sector->getBlocks(sectorblocks);
1890 core::list< MapBlock * >::Iterator i;
1891 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1893 MapBlock *block = *i;
1894 bool changed = block->m_node_metadata.step(dtime);
1896 changed_blocks[block->getPos()] = block;
1905 ServerMap::ServerMap(std::string savedir):
1908 m_map_metadata_changed(true),
1910 m_database_read(NULL),
1911 m_database_write(NULL)
1913 infostream<<__FUNCTION_NAME<<std::endl;
1915 //m_chunksize = 8; // Takes a few seconds
1917 if (g_settings->get("fixed_map_seed").empty())
1919 m_seed = (((u64)(myrand()%0xffff)<<0)
1920 + ((u64)(myrand()%0xffff)<<16)
1921 + ((u64)(myrand()%0xffff)<<32)
1922 + ((u64)(myrand()%0xffff)<<48));
1926 m_seed = g_settings->getU64("fixed_map_seed");
1930 Experimental and debug stuff
1937 Try to load map; if not found, create a new one.
1940 m_savedir = savedir;
1941 m_map_saving_enabled = false;
1945 // If directory exists, check contents and load if possible
1946 if(fs::PathExists(m_savedir))
1948 // If directory is empty, it is safe to save into it.
1949 if(fs::GetDirListing(m_savedir).size() == 0)
1951 infostream<<"Server: Empty save directory is valid."
1953 m_map_saving_enabled = true;
1958 // Load map metadata (seed, chunksize)
1961 catch(FileNotGoodException &e){
1962 infostream<<"WARNING: Could not load map metadata"
1963 //<<" Disabling chunk-based generator."
1969 // Load chunk metadata
1972 catch(FileNotGoodException &e){
1973 infostream<<"WARNING: Could not load chunk metadata."
1974 <<" Disabling chunk-based generator."
1979 /*infostream<<"Server: Successfully loaded chunk "
1980 "metadata and sector (0,0) from "<<savedir<<
1981 ", assuming valid save directory."
1984 infostream<<"Server: Successfully loaded map "
1985 <<"and chunk metadata from "<<savedir
1986 <<", assuming valid save directory."
1989 m_map_saving_enabled = true;
1990 // Map loaded, not creating new one
1994 // If directory doesn't exist, it is safe to save to it
1996 m_map_saving_enabled = true;
1999 catch(std::exception &e)
2001 infostream<<"WARNING: Server: Failed to load map from "<<savedir
2002 <<", exception: "<<e.what()<<std::endl;
2003 infostream<<"Please remove the map or fix it."<<std::endl;
2004 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2007 infostream<<"Initializing new map."<<std::endl;
2009 // Create zero sector
2010 emergeSector(v2s16(0,0));
2012 // Initially write whole map
2016 ServerMap::~ServerMap()
2018 infostream<<__FUNCTION_NAME<<std::endl;
2022 if(m_map_saving_enabled)
2024 // Save only changed parts
2026 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2030 infostream<<"Server: map not saved"<<std::endl;
2033 catch(std::exception &e)
2035 infostream<<"Server: Failed to save map to "<<m_savedir
2036 <<", exception: "<<e.what()<<std::endl;
2040 Close database if it was opened
2043 sqlite3_finalize(m_database_read);
2044 if(m_database_write)
2045 sqlite3_finalize(m_database_write);
2047 sqlite3_close(m_database);
2053 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2054 for(; i.atEnd() == false; i++)
2056 MapChunk *chunk = i.getNode()->getValue();
2062 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2064 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2065 if(enable_mapgen_debug_info)
2066 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2067 <<blockpos.Z<<")"<<std::endl;
2069 // Do nothing if not inside limits (+-1 because of neighbors)
2070 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2071 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2077 data->no_op = false;
2078 data->seed = m_seed;
2079 data->blockpos = blockpos;
2082 Create the whole area of this and the neighboring blocks
2085 //TimeTaker timer("initBlockMake() create area");
2087 for(s16 x=-1; x<=1; x++)
2088 for(s16 z=-1; z<=1; z++)
2090 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2091 // Sector metadata is loaded from disk if not already loaded.
2092 ServerMapSector *sector = createSector(sectorpos);
2095 for(s16 y=-1; y<=1; y++)
2097 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2098 //MapBlock *block = createBlock(p);
2099 // 1) get from memory, 2) load from disk
2100 MapBlock *block = emergeBlock(p, false);
2101 // 3) create a blank one
2104 block = createBlock(p);
2107 Block gets sunlight if this is true.
2109 Refer to the map generator heuristics.
2111 bool ug = mapgen::block_is_underground(data->seed, p);
2112 block->setIsUnderground(ug);
2115 // Lighting will not be valid after make_chunk is called
2116 block->setLightingExpired(true);
2117 // Lighting will be calculated
2118 //block->setLightingExpired(false);
2124 Now we have a big empty area.
2126 Make a ManualMapVoxelManipulator that contains this and the
2130 // The area that contains this block and it's neighbors
2131 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2132 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2134 data->vmanip = new ManualMapVoxelManipulator(this);
2135 //data->vmanip->setMap(this);
2139 //TimeTaker timer("initBlockMake() initialEmerge");
2140 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2143 // Data is ready now.
2146 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2147 core::map<v3s16, MapBlock*> &changed_blocks)
2149 v3s16 blockpos = data->blockpos;
2150 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2151 <<blockpos.Z<<")"<<std::endl;*/
2155 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2159 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2161 /*infostream<<"Resulting vmanip:"<<std::endl;
2162 data->vmanip.print(infostream);*/
2165 Blit generated stuff to map
2166 NOTE: blitBackAll adds nearly everything to changed_blocks
2170 //TimeTaker timer("finishBlockMake() blitBackAll");
2171 data->vmanip->blitBackAll(&changed_blocks);
2174 if(enable_mapgen_debug_info)
2175 infostream<<"finishBlockMake: changed_blocks.size()="
2176 <<changed_blocks.size()<<std::endl;
2179 Copy transforming liquid information
2181 while(data->transforming_liquid.size() > 0)
2183 v3s16 p = data->transforming_liquid.pop_front();
2184 m_transforming_liquid.push_back(p);
2190 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2194 Set is_underground flag for lighting with sunlight.
2196 Refer to map generator heuristics.
2198 NOTE: This is done in initChunkMake
2200 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2204 Add sunlight to central block.
2205 This makes in-dark-spawning monsters to not flood the whole thing.
2206 Do not spread the light, though.
2208 /*core::map<v3s16, bool> light_sources;
2209 bool black_air_left = false;
2210 block->propagateSunlight(light_sources, true, &black_air_left);*/
2213 NOTE: Lighting and object adding shouldn't really be here, but
2214 lighting is a bit tricky to move properly to makeBlock.
2215 TODO: Do this the right way anyway, that is, move it to makeBlock.
2216 - There needs to be some way for makeBlock to report back if
2217 the lighting update is going further down because of the
2218 new block blocking light
2223 NOTE: This takes ~60ms, TODO: Investigate why
2226 TimeTaker t("finishBlockMake lighting update");
2228 core::map<v3s16, MapBlock*> lighting_update_blocks;
2231 lighting_update_blocks.insert(block->getPos(), block);
2236 v3s16 p = block->getPos()+v3s16(x,1,z);
2237 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2241 // All modified blocks
2242 // NOTE: Should this be done? If this is not done, then the lighting
2243 // of the others will be updated in a different place, one by one, i
2244 // think... or they might not? Well, at least they are left marked as
2245 // "lighting expired"; it seems that is not handled at all anywhere,
2246 // so enabling this will slow it down A LOT because otherwise it
2247 // would not do this at all. This causes the black trees.
2248 for(core::map<v3s16, MapBlock*>::Iterator
2249 i = changed_blocks.getIterator();
2250 i.atEnd() == false; i++)
2252 lighting_update_blocks.insert(i.getNode()->getKey(),
2253 i.getNode()->getValue());
2255 /*// Also force-add all the upmost blocks for proper sunlight
2256 for(s16 x=-1; x<=1; x++)
2257 for(s16 z=-1; z<=1; z++)
2259 v3s16 p = block->getPos()+v3s16(x,1,z);
2260 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2263 updateLighting(lighting_update_blocks, changed_blocks);
2266 Set lighting to non-expired state in all of them.
2267 This is cheating, but it is not fast enough if all of them
2268 would actually be updated.
2270 for(s16 x=-1; x<=1; x++)
2271 for(s16 y=-1; y<=1; y++)
2272 for(s16 z=-1; z<=1; z++)
2274 v3s16 p = block->getPos()+v3s16(x,y,z);
2275 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2278 if(enable_mapgen_debug_info == false)
2279 t.stop(true); // Hide output
2283 Add random objects to block
2285 mapgen::add_random_objects(block);
2288 Go through changed blocks
2290 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2291 i.atEnd() == false; i++)
2293 MapBlock *block = i.getNode()->getValue();
2296 Update day/night difference cache of the MapBlocks
2298 block->updateDayNightDiff();
2300 Set block as modified
2302 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2306 Set central block as generated
2308 block->setGenerated(true);
2311 Save changed parts of map
2312 NOTE: Will be saved later.
2316 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2317 <<blockpos.Z<<")"<<std::endl;*/
2319 if(enable_mapgen_debug_info)
2322 Analyze resulting blocks
2324 for(s16 x=-1; x<=1; x++)
2325 for(s16 y=-1; y<=1; y++)
2326 for(s16 z=-1; z<=1; z++)
2328 v3s16 p = block->getPos()+v3s16(x,y,z);
2329 MapBlock *block = getBlockNoCreateNoEx(p);
2331 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2332 infostream<<"Generated "<<spos<<": "
2333 <<analyze_block(block)<<std::endl;
2341 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2343 DSTACKF("%s: p2d=(%d,%d)",
2348 Check if it exists already in memory
2350 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2355 Try to load it from disk (with blocks)
2357 //if(loadSectorFull(p2d) == true)
2360 Try to load metadata from disk
2363 if(loadSectorMeta(p2d) == true)
2365 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2368 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2369 throw InvalidPositionException("");
2375 Do not create over-limit
2377 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2378 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2379 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2380 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2381 throw InvalidPositionException("createSector(): pos. over limit");
2384 Generate blank sector
2387 sector = new ServerMapSector(this, p2d);
2389 // Sector position on map in nodes
2390 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2395 m_sectors.insert(p2d, sector);
2401 This is a quick-hand function for calling makeBlock().
2403 MapBlock * ServerMap::generateBlock(
2405 core::map<v3s16, MapBlock*> &modified_blocks
2408 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2410 /*infostream<<"generateBlock(): "
2411 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2414 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2416 TimeTaker timer("generateBlock");
2418 //MapBlock *block = original_dummy;
2420 v2s16 p2d(p.X, p.Z);
2421 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2424 Do not generate over-limit
2426 if(blockpos_over_limit(p))
2428 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2429 throw InvalidPositionException("generateBlock(): pos. over limit");
2433 Create block make data
2435 mapgen::BlockMakeData data;
2436 initBlockMake(&data, p);
2442 TimeTaker t("mapgen::make_block()");
2443 mapgen::make_block(&data);
2445 if(enable_mapgen_debug_info == false)
2446 t.stop(true); // Hide output
2450 Blit data back on map, update lighting, add mobs and whatever this does
2452 finishBlockMake(&data, modified_blocks);
2457 MapBlock *block = getBlockNoCreateNoEx(p);
2465 bool erroneus_content = false;
2466 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2467 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2468 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2471 MapNode n = block->getNode(p);
2472 if(n.getContent() == CONTENT_IGNORE)
2474 infostream<<"CONTENT_IGNORE at "
2475 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2477 erroneus_content = true;
2481 if(erroneus_content)
2490 Generate a completely empty block
2494 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2495 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2497 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2501 n.setContent(CONTENT_AIR);
2503 n.setContent(CONTENT_STONE);
2504 block->setNode(v3s16(x0,y0,z0), n);
2510 if(enable_mapgen_debug_info == false)
2511 timer.stop(true); // Hide output
2516 MapBlock * ServerMap::createBlock(v3s16 p)
2518 DSTACKF("%s: p=(%d,%d,%d)",
2519 __FUNCTION_NAME, p.X, p.Y, p.Z);
2522 Do not create over-limit
2524 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2525 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2526 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2527 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2528 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2529 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2530 throw InvalidPositionException("createBlock(): pos. over limit");
2532 v2s16 p2d(p.X, p.Z);
2535 This will create or load a sector if not found in memory.
2536 If block exists on disk, it will be loaded.
2538 NOTE: On old save formats, this will be slow, as it generates
2539 lighting on blocks for them.
2541 ServerMapSector *sector;
2543 sector = (ServerMapSector*)createSector(p2d);
2544 assert(sector->getId() == MAPSECTOR_SERVER);
2546 catch(InvalidPositionException &e)
2548 infostream<<"createBlock: createSector() failed"<<std::endl;
2552 NOTE: This should not be done, or at least the exception
2553 should not be passed on as std::exception, because it
2554 won't be catched at all.
2556 /*catch(std::exception &e)
2558 infostream<<"createBlock: createSector() failed: "
2559 <<e.what()<<std::endl;
2564 Try to get a block from the sector
2567 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2570 if(block->isDummy())
2575 block = sector->createBlankBlock(block_y);
2579 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2581 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2583 p.X, p.Y, p.Z, allow_generate);
2586 MapBlock *block = getBlockNoCreateNoEx(p);
2587 if(block && block->isDummy() == false)
2592 MapBlock *block = loadBlock(p);
2599 core::map<v3s16, MapBlock*> modified_blocks;
2600 MapBlock *block = generateBlock(p, modified_blocks);
2604 event.type = MEET_OTHER;
2607 // Copy modified_blocks to event
2608 for(core::map<v3s16, MapBlock*>::Iterator
2609 i = modified_blocks.getIterator();
2610 i.atEnd()==false; i++)
2612 event.modified_blocks.insert(i.getNode()->getKey(), false);
2616 dispatchEvent(&event);
2627 Do not generate over-limit
2629 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2630 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2631 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2632 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2633 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2634 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2635 throw InvalidPositionException("emergeBlock(): pos. over limit");
2637 v2s16 p2d(p.X, p.Z);
2640 This will create or load a sector if not found in memory.
2641 If block exists on disk, it will be loaded.
2643 ServerMapSector *sector;
2645 sector = createSector(p2d);
2646 //sector = emergeSector(p2d, changed_blocks);
2648 catch(InvalidPositionException &e)
2650 infostream<<"emergeBlock: createSector() failed: "
2651 <<e.what()<<std::endl;
2652 infostream<<"Path to failed sector: "<<getSectorDir(p2d)
2654 <<"You could try to delete it."<<std::endl;
2657 catch(VersionMismatchException &e)
2659 infostream<<"emergeBlock: createSector() failed: "
2660 <<e.what()<<std::endl;
2661 infostream<<"Path to failed sector: "<<getSectorDir(p2d)
2663 <<"You could try to delete it."<<std::endl;
2668 Try to get a block from the sector
2671 bool does_not_exist = false;
2672 bool lighting_expired = false;
2673 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2675 // If not found, try loading from disk
2678 block = loadBlock(p);
2684 does_not_exist = true;
2686 else if(block->isDummy() == true)
2688 does_not_exist = true;
2690 else if(block->getLightingExpired())
2692 lighting_expired = true;
2697 //infostream<<"emergeBlock(): Returning already valid block"<<std::endl;
2702 If block was not found on disk and not going to generate a
2703 new one, make sure there is a dummy block in place.
2705 if(only_from_disk && (does_not_exist || lighting_expired))
2707 //infostream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2711 // Create dummy block
2712 block = new MapBlock(this, p, true);
2714 // Add block to sector
2715 sector->insertBlock(block);
2721 //infostream<<"Not found on disk, generating."<<std::endl;
2723 //TimeTaker("emergeBlock() generate");
2725 //infostream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2728 If the block doesn't exist, generate the block.
2732 block = generateBlock(p, block, sector, changed_blocks,
2733 lighting_invalidated_blocks);
2736 if(lighting_expired)
2738 lighting_invalidated_blocks.insert(p, block);
2743 Initially update sunlight
2746 core::map<v3s16, bool> light_sources;
2747 bool black_air_left = false;
2748 bool bottom_invalid =
2749 block->propagateSunlight(light_sources, true,
2752 // If sunlight didn't reach everywhere and part of block is
2753 // above ground, lighting has to be properly updated
2754 //if(black_air_left && some_part_underground)
2757 lighting_invalidated_blocks[block->getPos()] = block;
2762 lighting_invalidated_blocks[block->getPos()] = block;
2771 s16 ServerMap::findGroundLevel(v2s16 p2d)
2775 Uh, just do something random...
2777 // Find existing map from top to down
2780 v3s16 p(p2d.X, max, p2d.Y);
2781 for(; p.Y>min; p.Y--)
2783 MapNode n = getNodeNoEx(p);
2784 if(n.getContent() != CONTENT_IGNORE)
2789 // If this node is not air, go to plan b
2790 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2792 // Search existing walkable and return it
2793 for(; p.Y>min; p.Y--)
2795 MapNode n = getNodeNoEx(p);
2796 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2805 Determine from map generator noise functions
2808 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2811 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2812 //return (s16)level;
2815 void ServerMap::createDatabase() {
2818 e = sqlite3_exec(m_database,
2819 "CREATE TABLE IF NOT EXISTS `blocks` ("
2820 "`pos` INT NOT NULL PRIMARY KEY,"
2823 , NULL, NULL, NULL);
2824 if(e == SQLITE_ABORT)
2825 throw FileNotGoodException("Could not create database structure");
2827 infostream<<"Server: Database structure was created";
2830 void ServerMap::verifyDatabase() {
2835 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2836 bool needs_create = false;
2840 Open the database connection
2843 createDirs(m_savedir);
2845 if(!fs::PathExists(dbp))
2846 needs_create = true;
2848 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2849 if(d != SQLITE_OK) {
2850 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2851 throw FileNotGoodException("Cannot open database file");
2857 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2858 if(d != SQLITE_OK) {
2859 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2860 throw FileNotGoodException("Cannot prepare read statement");
2863 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2864 if(d != SQLITE_OK) {
2865 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2866 throw FileNotGoodException("Cannot prepare write statement");
2869 infostream<<"Server: Database opened"<<std::endl;
2873 bool ServerMap::loadFromFolders() {
2874 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2879 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2880 return (sqlite3_int64)pos.Z*16777216 +
2881 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2884 void ServerMap::createDirs(std::string path)
2886 if(fs::CreateAllDirs(path) == false)
2888 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2889 <<"\""<<path<<"\""<<std::endl;
2890 throw BaseException("ServerMap failed to create directory");
2894 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2900 snprintf(cc, 9, "%.4x%.4x",
2901 (unsigned int)pos.X&0xffff,
2902 (unsigned int)pos.Y&0xffff);
2904 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2906 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2907 (unsigned int)pos.X&0xfff,
2908 (unsigned int)pos.Y&0xfff);
2910 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2916 v2s16 ServerMap::getSectorPos(std::string dirname)
2920 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2921 assert(spos != std::string::npos);
2922 if(dirname.size() - spos == 8)
2925 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2927 else if(dirname.size() - spos == 3)
2930 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2931 // Sign-extend the 12 bit values up to 16 bits...
2932 if(x&0x800) x|=0xF000;
2933 if(y&0x800) y|=0xF000;
2940 v2s16 pos((s16)x, (s16)y);
2944 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2946 v2s16 p2d = getSectorPos(sectordir);
2948 if(blockfile.size() != 4){
2949 throw InvalidFilenameException("Invalid block filename");
2952 int r = sscanf(blockfile.c_str(), "%4x", &y);
2954 throw InvalidFilenameException("Invalid block filename");
2955 return v3s16(p2d.X, y, p2d.Y);
2958 std::string ServerMap::getBlockFilename(v3s16 p)
2961 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2965 void ServerMap::save(bool only_changed)
2967 DSTACK(__FUNCTION_NAME);
2968 if(m_map_saving_enabled == false)
2970 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2974 if(only_changed == false)
2975 infostream<<"ServerMap: Saving whole map, this can take time."
2978 if(only_changed == false || m_map_metadata_changed)
2983 u32 sector_meta_count = 0;
2984 u32 block_count = 0;
2985 u32 block_count_all = 0; // Number of blocks in memory
2988 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2989 for(; i.atEnd() == false; i++)
2991 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2992 assert(sector->getId() == MAPSECTOR_SERVER);
2994 if(sector->differs_from_disk || only_changed == false)
2996 saveSectorMeta(sector);
2997 sector_meta_count++;
2999 core::list<MapBlock*> blocks;
3000 sector->getBlocks(blocks);
3001 core::list<MapBlock*>::Iterator j;
3003 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
3004 for(j=blocks.begin(); j!=blocks.end(); j++)
3006 MapBlock *block = *j;
3010 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
3011 || only_changed == false)
3016 /*infostream<<"ServerMap: Written block ("
3017 <<block->getPos().X<<","
3018 <<block->getPos().Y<<","
3019 <<block->getPos().Z<<")"
3022 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
3028 Only print if something happened or saved whole map
3030 if(only_changed == false || sector_meta_count != 0
3031 || block_count != 0)
3033 infostream<<"ServerMap: Written: "
3034 <<sector_meta_count<<" sector metadata files, "
3035 <<block_count<<" block files"
3036 <<", "<<block_count_all<<" blocks in memory."
3041 void ServerMap::saveMapMeta()
3043 DSTACK(__FUNCTION_NAME);
3045 infostream<<"ServerMap::saveMapMeta(): "
3049 createDirs(m_savedir);
3051 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3052 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3053 if(os.good() == false)
3055 infostream<<"ERROR: ServerMap::saveMapMeta(): "
3056 <<"could not open"<<fullpath<<std::endl;
3057 throw FileNotGoodException("Cannot open chunk metadata");
3061 params.setU64("seed", m_seed);
3063 params.writeLines(os);
3065 os<<"[end_of_params]\n";
3067 m_map_metadata_changed = false;
3070 void ServerMap::loadMapMeta()
3072 DSTACK(__FUNCTION_NAME);
3074 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
3077 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
3078 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3079 if(is.good() == false)
3081 infostream<<"ERROR: ServerMap::loadMapMeta(): "
3082 <<"could not open"<<fullpath<<std::endl;
3083 throw FileNotGoodException("Cannot open map metadata");
3091 throw SerializationError
3092 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3094 std::getline(is, line);
3095 std::string trimmedline = trim(line);
3096 if(trimmedline == "[end_of_params]")
3098 params.parseConfigLine(line);
3101 m_seed = params.getU64("seed");
3103 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3106 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3108 DSTACK(__FUNCTION_NAME);
3109 // Format used for writing
3110 u8 version = SER_FMT_VER_HIGHEST;
3112 v2s16 pos = sector->getPos();
3113 std::string dir = getSectorDir(pos);
3116 std::string fullpath = dir + DIR_DELIM + "meta";
3117 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3118 if(o.good() == false)
3119 throw FileNotGoodException("Cannot open sector metafile");
3121 sector->serialize(o, version);
3123 sector->differs_from_disk = false;
3126 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3128 DSTACK(__FUNCTION_NAME);
3130 v2s16 p2d = getSectorPos(sectordir);
3132 ServerMapSector *sector = NULL;
3134 std::string fullpath = sectordir + DIR_DELIM + "meta";
3135 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3136 if(is.good() == false)
3138 // If the directory exists anyway, it probably is in some old
3139 // format. Just go ahead and create the sector.
3140 if(fs::PathExists(sectordir))
3142 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3143 <<fullpath<<" doesn't exist but directory does."
3144 <<" Continuing with a sector with no metadata."
3146 sector = new ServerMapSector(this, p2d);
3147 m_sectors.insert(p2d, sector);
3151 throw FileNotGoodException("Cannot open sector metafile");
3156 sector = ServerMapSector::deSerialize
3157 (is, this, p2d, m_sectors);
3159 saveSectorMeta(sector);
3162 sector->differs_from_disk = false;
3167 bool ServerMap::loadSectorMeta(v2s16 p2d)
3169 DSTACK(__FUNCTION_NAME);
3171 MapSector *sector = NULL;
3173 // The directory layout we're going to load from.
3174 // 1 - original sectors/xxxxzzzz/
3175 // 2 - new sectors2/xxx/zzz/
3176 // If we load from anything but the latest structure, we will
3177 // immediately save to the new one, and remove the old.
3179 std::string sectordir1 = getSectorDir(p2d, 1);
3180 std::string sectordir;
3181 if(fs::PathExists(sectordir1))
3183 sectordir = sectordir1;
3188 sectordir = getSectorDir(p2d, 2);
3192 sector = loadSectorMeta(sectordir, loadlayout != 2);
3194 catch(InvalidFilenameException &e)
3198 catch(FileNotGoodException &e)
3202 catch(std::exception &e)
3211 bool ServerMap::loadSectorFull(v2s16 p2d)
3213 DSTACK(__FUNCTION_NAME);
3215 MapSector *sector = NULL;
3217 // The directory layout we're going to load from.
3218 // 1 - original sectors/xxxxzzzz/
3219 // 2 - new sectors2/xxx/zzz/
3220 // If we load from anything but the latest structure, we will
3221 // immediately save to the new one, and remove the old.
3223 std::string sectordir1 = getSectorDir(p2d, 1);
3224 std::string sectordir;
3225 if(fs::PathExists(sectordir1))
3227 sectordir = sectordir1;
3232 sectordir = getSectorDir(p2d, 2);
3236 sector = loadSectorMeta(sectordir, loadlayout != 2);
3238 catch(InvalidFilenameException &e)
3242 catch(FileNotGoodException &e)
3246 catch(std::exception &e)
3254 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3256 std::vector<fs::DirListNode>::iterator i2;
3257 for(i2=list2.begin(); i2!=list2.end(); i2++)
3263 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3265 catch(InvalidFilenameException &e)
3267 // This catches unknown crap in directory
3273 infostream<<"Sector converted to new layout - deleting "<<
3274 sectordir1<<std::endl;
3275 fs::RecursiveDelete(sectordir1);
3282 void ServerMap::beginSave() {
3284 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3285 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3288 void ServerMap::endSave() {
3290 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3291 infostream<<"WARNING: endSave() failed, map might not have saved.";
3294 void ServerMap::saveBlock(MapBlock *block)
3296 DSTACK(__FUNCTION_NAME);
3298 Dummy blocks are not written
3300 if(block->isDummy())
3302 /*v3s16 p = block->getPos();
3303 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3304 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3308 // Format used for writing
3309 u8 version = SER_FMT_VER_HIGHEST;
3311 v3s16 p3d = block->getPos();
3315 v2s16 p2d(p3d.X, p3d.Z);
3316 std::string sectordir = getSectorDir(p2d);
3318 createDirs(sectordir);
3320 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3321 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3322 if(o.good() == false)
3323 throw FileNotGoodException("Cannot open block data");
3326 [0] u8 serialization version
3332 std::ostringstream o(std::ios_base::binary);
3334 o.write((char*)&version, 1);
3337 block->serialize(o, version);
3339 // Write extra data stored on disk
3340 block->serializeDiskExtra(o, version);
3342 // Write block to database
3344 std::string tmp = o.str();
3345 const char *bytes = tmp.c_str();
3347 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3348 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3349 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3350 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3351 int written = sqlite3_step(m_database_write);
3352 if(written != SQLITE_DONE)
3353 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3354 <<sqlite3_errmsg(m_database)<<std::endl;
3355 // Make ready for later reuse
3356 sqlite3_reset(m_database_write);
3358 // We just wrote it to the disk so clear modified flag
3359 block->resetModified();
3362 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3364 DSTACK(__FUNCTION_NAME);
3366 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3369 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3370 if(is.good() == false)
3371 throw FileNotGoodException("Cannot open block file");
3373 v3s16 p3d = getBlockPos(sectordir, blockfile);
3374 v2s16 p2d(p3d.X, p3d.Z);
3376 assert(sector->getPos() == p2d);
3378 u8 version = SER_FMT_VER_INVALID;
3379 is.read((char*)&version, 1);
3382 throw SerializationError("ServerMap::loadBlock(): Failed"
3383 " to read MapBlock version");
3385 /*u32 block_size = MapBlock::serializedLength(version);
3386 SharedBuffer<u8> data(block_size);
3387 is.read((char*)*data, block_size);*/
3389 // This will always return a sector because we're the server
3390 //MapSector *sector = emergeSector(p2d);
3392 MapBlock *block = NULL;
3393 bool created_new = false;
3394 block = sector->getBlockNoCreateNoEx(p3d.Y);
3397 block = sector->createBlankBlockNoInsert(p3d.Y);
3402 block->deSerialize(is, version);
3404 // Read extra data stored on disk
3405 block->deSerializeDiskExtra(is, version);
3407 // If it's a new block, insert it to the map
3409 sector->insertBlock(block);
3412 Save blocks loaded in old format in new format
3415 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3419 // Should be in database now, so delete the old file
3420 fs::RecursiveDelete(fullpath);
3423 // We just loaded it from the disk, so it's up-to-date.
3424 block->resetModified();
3427 catch(SerializationError &e)
3429 infostream<<"WARNING: Invalid block data on disk "
3430 <<"fullpath="<<fullpath
3431 <<" (SerializationError). "
3432 <<"what()="<<e.what()
3434 //" Ignoring. A new one will be generated.
3437 // TODO: Backup file; name is in fullpath.
3441 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3443 DSTACK(__FUNCTION_NAME);
3446 std::istringstream is(*blob, std::ios_base::binary);
3448 u8 version = SER_FMT_VER_INVALID;
3449 is.read((char*)&version, 1);
3452 throw SerializationError("ServerMap::loadBlock(): Failed"
3453 " to read MapBlock version");
3455 /*u32 block_size = MapBlock::serializedLength(version);
3456 SharedBuffer<u8> data(block_size);
3457 is.read((char*)*data, block_size);*/
3459 // This will always return a sector because we're the server
3460 //MapSector *sector = emergeSector(p2d);
3462 MapBlock *block = NULL;
3463 bool created_new = false;
3464 block = sector->getBlockNoCreateNoEx(p3d.Y);
3467 block = sector->createBlankBlockNoInsert(p3d.Y);
3472 block->deSerialize(is, version);
3474 // Read extra data stored on disk
3475 block->deSerializeDiskExtra(is, version);
3477 // If it's a new block, insert it to the map
3479 sector->insertBlock(block);
3482 Save blocks loaded in old format in new format
3485 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3490 // We just loaded it from, so it's up-to-date.
3491 block->resetModified();
3494 catch(SerializationError &e)
3496 infostream<<"WARNING: Invalid block data in database "
3497 <<" (SerializationError). "
3498 <<"what()="<<e.what()
3500 //" Ignoring. A new one will be generated.
3503 // TODO: Copy to a backup database.
3507 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3509 DSTACK(__FUNCTION_NAME);
3511 v2s16 p2d(blockpos.X, blockpos.Z);
3513 if(!loadFromFolders()) {
3516 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3517 infostream<<"WARNING: Could not bind block position for load: "
3518 <<sqlite3_errmsg(m_database)<<std::endl;
3519 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3521 Make sure sector is loaded
3523 MapSector *sector = createSector(p2d);
3528 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3529 size_t len = sqlite3_column_bytes(m_database_read, 0);
3531 std::string datastr(data, len);
3533 loadBlock(&datastr, blockpos, sector, false);
3535 sqlite3_step(m_database_read);
3536 // We should never get more than 1 row, so ok to reset
3537 sqlite3_reset(m_database_read);
3539 return getBlockNoCreateNoEx(blockpos);
3541 sqlite3_reset(m_database_read);
3543 // Not found in database, try the files
3546 // The directory layout we're going to load from.
3547 // 1 - original sectors/xxxxzzzz/
3548 // 2 - new sectors2/xxx/zzz/
3549 // If we load from anything but the latest structure, we will
3550 // immediately save to the new one, and remove the old.
3552 std::string sectordir1 = getSectorDir(p2d, 1);
3553 std::string sectordir;
3554 if(fs::PathExists(sectordir1))
3556 sectordir = sectordir1;
3561 sectordir = getSectorDir(p2d, 2);
3565 Make sure sector is loaded
3567 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3571 sector = loadSectorMeta(sectordir, loadlayout != 2);
3573 catch(InvalidFilenameException &e)
3577 catch(FileNotGoodException &e)
3581 catch(std::exception &e)
3588 Make sure file exists
3591 std::string blockfilename = getBlockFilename(blockpos);
3592 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3596 Load block and save it to the database
3598 loadBlock(sectordir, blockfilename, sector, true);
3599 return getBlockNoCreateNoEx(blockpos);
3602 void ServerMap::PrintInfo(std::ostream &out)
3613 ClientMap::ClientMap(
3615 MapDrawControl &control,
3616 scene::ISceneNode* parent,
3617 scene::ISceneManager* mgr,
3621 scene::ISceneNode(parent, mgr, id),
3624 m_camera_position(0,0,0),
3625 m_camera_direction(0,0,1),
3628 m_camera_mutex.Init();
3629 assert(m_camera_mutex.IsInitialized());
3631 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3632 BS*1000000,BS*1000000,BS*1000000);
3635 ClientMap::~ClientMap()
3637 /*JMutexAutoLock lock(mesh_mutex);
3646 MapSector * ClientMap::emergeSector(v2s16 p2d)
3648 DSTACK(__FUNCTION_NAME);
3649 // Check that it doesn't exist already
3651 return getSectorNoGenerate(p2d);
3653 catch(InvalidPositionException &e)
3658 ClientMapSector *sector = new ClientMapSector(this, p2d);
3661 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3662 m_sectors.insert(p2d, sector);
3669 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3671 DSTACK(__FUNCTION_NAME);
3672 ClientMapSector *sector = NULL;
3674 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3676 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3680 sector = (ClientMapSector*)n->getValue();
3681 assert(sector->getId() == MAPSECTOR_CLIENT);
3685 sector = new ClientMapSector(this, p2d);
3687 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3688 m_sectors.insert(p2d, sector);
3692 sector->deSerialize(is);
3696 void ClientMap::OnRegisterSceneNode()
3700 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3701 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3704 ISceneNode::OnRegisterSceneNode();
3707 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3709 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3710 DSTACK(__FUNCTION_NAME);
3712 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3715 This is called two times per frame, reset on the non-transparent one
3717 if(pass == scene::ESNRP_SOLID)
3719 m_last_drawn_sectors.clear();
3723 Get time for measuring timeout.
3725 Measuring time is very useful for long delays when the
3726 machine is swapping a lot.
3728 int time1 = time(0);
3730 //u32 daynight_ratio = m_client->getDayNightRatio();
3732 m_camera_mutex.Lock();
3733 v3f camera_position = m_camera_position;
3734 v3f camera_direction = m_camera_direction;
3735 f32 camera_fov = m_camera_fov;
3736 m_camera_mutex.Unlock();
3739 Get all blocks and draw all visible ones
3742 v3s16 cam_pos_nodes(
3743 camera_position.X / BS,
3744 camera_position.Y / BS,
3745 camera_position.Z / BS);
3747 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3749 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3750 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3752 // Take a fair amount as we will be dropping more out later
3754 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3755 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3756 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3758 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3759 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3760 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3762 u32 vertex_count = 0;
3764 // For limiting number of mesh updates per frame
3765 u32 mesh_update_count = 0;
3767 u32 blocks_would_have_drawn = 0;
3768 u32 blocks_drawn = 0;
3770 int timecheck_counter = 0;
3771 core::map<v2s16, MapSector*>::Iterator si;
3772 si = m_sectors.getIterator();
3773 for(; si.atEnd() == false; si++)
3776 timecheck_counter++;
3777 if(timecheck_counter > 50)
3779 timecheck_counter = 0;
3780 int time2 = time(0);
3781 if(time2 > time1 + 4)
3783 infostream<<"ClientMap::renderMap(): "
3784 "Rendering takes ages, returning."
3791 MapSector *sector = si.getNode()->getValue();
3792 v2s16 sp = sector->getPos();
3794 if(m_control.range_all == false)
3796 if(sp.X < p_blocks_min.X
3797 || sp.X > p_blocks_max.X
3798 || sp.Y < p_blocks_min.Z
3799 || sp.Y > p_blocks_max.Z)
3803 core::list< MapBlock * > sectorblocks;
3804 sector->getBlocks(sectorblocks);
3810 u32 sector_blocks_drawn = 0;
3812 core::list< MapBlock * >::Iterator i;
3813 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3815 MapBlock *block = *i;
3818 Compare block position to camera position, skip
3819 if not seen on display
3822 float range = 100000 * BS;
3823 if(m_control.range_all == false)
3824 range = m_control.wanted_range * BS;
3827 if(isBlockInSight(block->getPos(), camera_position,
3828 camera_direction, camera_fov,
3829 range, &d) == false)
3834 // Okay, this block will be drawn. Reset usage timer.
3835 block->resetUsageTimer();
3837 // This is ugly (spherical distance limit?)
3838 /*if(m_control.range_all == false &&
3839 d - 0.5*BS*MAP_BLOCKSIZE > range)
3844 Update expired mesh (used for day/night change)
3846 It doesn't work exactly like it should now with the
3847 tasked mesh update but whatever.
3850 bool mesh_expired = false;
3853 JMutexAutoLock lock(block->mesh_mutex);
3855 mesh_expired = block->getMeshExpired();
3857 // Mesh has not been expired and there is no mesh:
3858 // block has no content
3859 if(block->mesh == NULL && mesh_expired == false)
3863 f32 faraway = BS*50;
3864 //f32 faraway = m_control.wanted_range * BS;
3867 This has to be done with the mesh_mutex unlocked
3869 // Pretty random but this should work somewhat nicely
3870 if(mesh_expired && (
3871 (mesh_update_count < 3
3872 && (d < faraway || mesh_update_count < 2)
3875 (m_control.range_all && mesh_update_count < 20)
3878 /*if(mesh_expired && mesh_update_count < 6
3879 && (d < faraway || mesh_update_count < 3))*/
3881 mesh_update_count++;
3883 // Mesh has been expired: generate new mesh
3884 //block->updateMesh(daynight_ratio);
3885 m_client->addUpdateMeshTask(block->getPos());
3887 mesh_expired = false;
3892 Draw the faces of the block
3895 JMutexAutoLock lock(block->mesh_mutex);
3897 scene::SMesh *mesh = block->mesh;
3902 blocks_would_have_drawn++;
3903 if(blocks_drawn >= m_control.wanted_max_blocks
3904 && m_control.range_all == false
3905 && d > m_control.wanted_min_range * BS)
3909 sector_blocks_drawn++;
3911 u32 c = mesh->getMeshBufferCount();
3913 for(u32 i=0; i<c; i++)
3915 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3916 const video::SMaterial& material = buf->getMaterial();
3917 video::IMaterialRenderer* rnd =
3918 driver->getMaterialRenderer(material.MaterialType);
3919 bool transparent = (rnd && rnd->isTransparent());
3920 // Render transparent on transparent pass and likewise.
3921 if(transparent == is_transparent_pass)
3924 This *shouldn't* hurt too much because Irrlicht
3925 doesn't change opengl textures if the old
3926 material is set again.
3928 driver->setMaterial(buf->getMaterial());
3929 driver->drawMeshBuffer(buf);
3930 vertex_count += buf->getVertexCount();
3934 } // foreach sectorblocks
3936 if(sector_blocks_drawn != 0)
3938 m_last_drawn_sectors[sp] = true;
3942 m_control.blocks_drawn = blocks_drawn;
3943 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3945 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3946 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3949 void ClientMap::renderPostFx()
3951 // Sadly ISceneManager has no "post effects" render pass, in that case we
3952 // could just register for that and handle it in renderMap().
3954 m_camera_mutex.Lock();
3955 v3f camera_position = m_camera_position;
3956 m_camera_mutex.Unlock();
3958 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
3960 // - If the player is in a solid node, make everything black.
3961 // - If the player is in liquid, draw a semi-transparent overlay.
3962 ContentFeatures& features = content_features(n);
3963 video::SColor post_effect_color = features.post_effect_color;
3964 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
3966 post_effect_color = video::SColor(255, 0, 0, 0);
3968 if (post_effect_color.getAlpha() != 0)
3970 // Draw a full-screen rectangle
3971 video::IVideoDriver* driver = SceneManager->getVideoDriver();
3972 v2u32 ss = driver->getScreenSize();
3973 core::rect<s32> rect(0,0, ss.X, ss.Y);
3974 driver->draw2DRectangle(post_effect_color, rect);
3978 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3979 core::map<v3s16, MapBlock*> *affected_blocks)
3981 bool changed = false;
3983 Add it to all blocks touching it
3986 v3s16(0,0,0), // this
3987 v3s16(0,0,1), // back
3988 v3s16(0,1,0), // top
3989 v3s16(1,0,0), // right
3990 v3s16(0,0,-1), // front
3991 v3s16(0,-1,0), // bottom
3992 v3s16(-1,0,0), // left
3994 for(u16 i=0; i<7; i++)
3996 v3s16 p2 = p + dirs[i];
3997 // Block position of neighbor (or requested) node
3998 v3s16 blockpos = getNodeBlockPos(p2);
3999 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4000 if(blockref == NULL)
4002 // Relative position of requested node
4003 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4004 if(blockref->setTempMod(relpos, mod))
4009 if(changed && affected_blocks!=NULL)
4011 for(u16 i=0; i<7; i++)
4013 v3s16 p2 = p + dirs[i];
4014 // Block position of neighbor (or requested) node
4015 v3s16 blockpos = getNodeBlockPos(p2);
4016 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4017 if(blockref == NULL)
4019 affected_blocks->insert(blockpos, blockref);
4025 bool ClientMap::clearTempMod(v3s16 p,
4026 core::map<v3s16, MapBlock*> *affected_blocks)
4028 bool changed = false;
4030 v3s16(0,0,0), // this
4031 v3s16(0,0,1), // back
4032 v3s16(0,1,0), // top
4033 v3s16(1,0,0), // right
4034 v3s16(0,0,-1), // front
4035 v3s16(0,-1,0), // bottom
4036 v3s16(-1,0,0), // left
4038 for(u16 i=0; i<7; i++)
4040 v3s16 p2 = p + dirs[i];
4041 // Block position of neighbor (or requested) node
4042 v3s16 blockpos = getNodeBlockPos(p2);
4043 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4044 if(blockref == NULL)
4046 // Relative position of requested node
4047 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4048 if(blockref->clearTempMod(relpos))
4053 if(changed && affected_blocks!=NULL)
4055 for(u16 i=0; i<7; i++)
4057 v3s16 p2 = p + dirs[i];
4058 // Block position of neighbor (or requested) node
4059 v3s16 blockpos = getNodeBlockPos(p2);
4060 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4061 if(blockref == NULL)
4063 affected_blocks->insert(blockpos, blockref);
4069 void ClientMap::expireMeshes(bool only_daynight_diffed)
4071 TimeTaker timer("expireMeshes()");
4073 core::map<v2s16, MapSector*>::Iterator si;
4074 si = m_sectors.getIterator();
4075 for(; si.atEnd() == false; si++)
4077 MapSector *sector = si.getNode()->getValue();
4079 core::list< MapBlock * > sectorblocks;
4080 sector->getBlocks(sectorblocks);
4082 core::list< MapBlock * >::Iterator i;
4083 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4085 MapBlock *block = *i;
4087 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4093 JMutexAutoLock lock(block->mesh_mutex);
4094 if(block->mesh != NULL)
4096 /*block->mesh->drop();
4097 block->mesh = NULL;*/
4098 block->setMeshExpired(true);
4105 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4107 assert(mapType() == MAPTYPE_CLIENT);
4110 v3s16 p = blockpos + v3s16(0,0,0);
4111 MapBlock *b = getBlockNoCreate(p);
4112 b->updateMesh(daynight_ratio);
4113 //b->setMeshExpired(true);
4115 catch(InvalidPositionException &e){}
4118 v3s16 p = blockpos + v3s16(-1,0,0);
4119 MapBlock *b = getBlockNoCreate(p);
4120 b->updateMesh(daynight_ratio);
4121 //b->setMeshExpired(true);
4123 catch(InvalidPositionException &e){}
4125 v3s16 p = blockpos + v3s16(0,-1,0);
4126 MapBlock *b = getBlockNoCreate(p);
4127 b->updateMesh(daynight_ratio);
4128 //b->setMeshExpired(true);
4130 catch(InvalidPositionException &e){}
4132 v3s16 p = blockpos + v3s16(0,0,-1);
4133 MapBlock *b = getBlockNoCreate(p);
4134 b->updateMesh(daynight_ratio);
4135 //b->setMeshExpired(true);
4137 catch(InvalidPositionException &e){}
4142 Update mesh of block in which the node is, and if the node is at the
4143 leading edge, update the appropriate leading blocks too.
4145 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4153 v3s16 blockposes[4];
4154 for(u32 i=0; i<4; i++)
4156 v3s16 np = nodepos + dirs[i];
4157 blockposes[i] = getNodeBlockPos(np);
4158 // Don't update mesh of block if it has been done already
4159 bool already_updated = false;
4160 for(u32 j=0; j<i; j++)
4162 if(blockposes[j] == blockposes[i])
4164 already_updated = true;
4171 MapBlock *b = getBlockNoCreate(blockposes[i]);
4172 b->updateMesh(daynight_ratio);
4177 void ClientMap::PrintInfo(std::ostream &out)
4188 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4193 MapVoxelManipulator::~MapVoxelManipulator()
4195 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4199 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4201 TimeTaker timer1("emerge", &emerge_time);
4203 // Units of these are MapBlocks
4204 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4205 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4207 VoxelArea block_area_nodes
4208 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4210 addArea(block_area_nodes);
4212 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4213 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4214 for(s32 x=p_min.X; x<=p_max.X; x++)
4217 core::map<v3s16, bool>::Node *n;
4218 n = m_loaded_blocks.find(p);
4222 bool block_data_inexistent = false;
4225 TimeTaker timer1("emerge load", &emerge_load_time);
4227 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4228 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4230 a.print(infostream);
4231 infostream<<std::endl;*/
4233 MapBlock *block = m_map->getBlockNoCreate(p);
4234 if(block->isDummy())
4235 block_data_inexistent = true;
4237 block->copyTo(*this);
4239 catch(InvalidPositionException &e)
4241 block_data_inexistent = true;
4244 if(block_data_inexistent)
4246 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4247 // Fill with VOXELFLAG_INEXISTENT
4248 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4249 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4251 s32 i = m_area.index(a.MinEdge.X,y,z);
4252 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4256 m_loaded_blocks.insert(p, !block_data_inexistent);
4259 //infostream<<"emerge done"<<std::endl;
4263 SUGG: Add an option to only update eg. water and air nodes.
4264 This will make it interfere less with important stuff if
4267 void MapVoxelManipulator::blitBack
4268 (core::map<v3s16, MapBlock*> & modified_blocks)
4270 if(m_area.getExtent() == v3s16(0,0,0))
4273 //TimeTaker timer1("blitBack");
4275 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4276 <<m_loaded_blocks.size()<<std::endl;*/
4279 Initialize block cache
4281 v3s16 blockpos_last;
4282 MapBlock *block = NULL;
4283 bool block_checked_in_modified = false;
4285 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4286 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4287 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4291 u8 f = m_flags[m_area.index(p)];
4292 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4295 MapNode &n = m_data[m_area.index(p)];
4297 v3s16 blockpos = getNodeBlockPos(p);
4302 if(block == NULL || blockpos != blockpos_last){
4303 block = m_map->getBlockNoCreate(blockpos);
4304 blockpos_last = blockpos;
4305 block_checked_in_modified = false;
4308 // Calculate relative position in block
4309 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4311 // Don't continue if nothing has changed here
4312 if(block->getNode(relpos) == n)
4315 //m_map->setNode(m_area.MinEdge + p, n);
4316 block->setNode(relpos, n);
4319 Make sure block is in modified_blocks
4321 if(block_checked_in_modified == false)
4323 modified_blocks[blockpos] = block;
4324 block_checked_in_modified = true;
4327 catch(InvalidPositionException &e)
4333 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4334 MapVoxelManipulator(map),
4335 m_create_area(false)
4339 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4343 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4345 // Just create the area so that it can be pointed to
4346 VoxelManipulator::emerge(a, caller_id);
4349 void ManualMapVoxelManipulator::initialEmerge(
4350 v3s16 blockpos_min, v3s16 blockpos_max)
4352 TimeTaker timer1("initialEmerge", &emerge_time);
4354 // Units of these are MapBlocks
4355 v3s16 p_min = blockpos_min;
4356 v3s16 p_max = blockpos_max;
4358 VoxelArea block_area_nodes
4359 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4361 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4364 infostream<<"initialEmerge: area: ";
4365 block_area_nodes.print(infostream);
4366 infostream<<" ("<<size_MB<<"MB)";
4367 infostream<<std::endl;
4370 addArea(block_area_nodes);
4372 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4373 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4374 for(s32 x=p_min.X; x<=p_max.X; x++)
4377 core::map<v3s16, bool>::Node *n;
4378 n = m_loaded_blocks.find(p);
4382 bool block_data_inexistent = false;
4385 TimeTaker timer1("emerge load", &emerge_load_time);
4387 MapBlock *block = m_map->getBlockNoCreate(p);
4388 if(block->isDummy())
4389 block_data_inexistent = true;
4391 block->copyTo(*this);
4393 catch(InvalidPositionException &e)
4395 block_data_inexistent = true;
4398 if(block_data_inexistent)
4401 Mark area inexistent
4403 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4404 // Fill with VOXELFLAG_INEXISTENT
4405 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4406 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4408 s32 i = m_area.index(a.MinEdge.X,y,z);
4409 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4413 m_loaded_blocks.insert(p, !block_data_inexistent);
4417 void ManualMapVoxelManipulator::blitBackAll(
4418 core::map<v3s16, MapBlock*> * modified_blocks)
4420 if(m_area.getExtent() == v3s16(0,0,0))
4424 Copy data of all blocks
4426 for(core::map<v3s16, bool>::Iterator
4427 i = m_loaded_blocks.getIterator();
4428 i.atEnd() == false; i++)
4430 v3s16 p = i.getNode()->getKey();
4431 bool existed = i.getNode()->getValue();
4432 if(existed == false)
4434 // The Great Bug was found using this
4435 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4436 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4440 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4443 infostream<<"WARNING: "<<__FUNCTION_NAME
4444 <<": got NULL block "
4445 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4450 block->copyFrom(*this);
4453 modified_blocks->insert(p, block);