3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
32 #include "nodemetadata.h"
33 #include "content_mapnode.h"
35 #include <IMaterialRenderer.h>
40 #include "mapnode_contentfeatures.h"
42 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
45 SQLite format specification:
46 - Initially only replaces sectors/ and sectors2/
48 If map.sqlite does not exist in the save dir
49 or the block was not found in the database
50 the map will try to load from sectors folder.
51 In either case, map.sqlite will be created
52 and all future saves will save there.
54 Structure of map.sqlite:
65 Map::Map(std::ostream &dout, IGameDef *gamedef):
70 /*m_sector_mutex.Init();
71 assert(m_sector_mutex.IsInitialized());*/
79 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
80 for(; i.atEnd() == false; i++)
82 MapSector *sector = i.getNode()->getValue();
87 void Map::addEventReceiver(MapEventReceiver *event_receiver)
89 m_event_receivers.insert(event_receiver, false);
92 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
94 if(m_event_receivers.find(event_receiver) == NULL)
96 m_event_receivers.remove(event_receiver);
99 void Map::dispatchEvent(MapEditEvent *event)
101 for(core::map<MapEventReceiver*, bool>::Iterator
102 i = m_event_receivers.getIterator();
103 i.atEnd()==false; i++)
105 MapEventReceiver* event_receiver = i.getNode()->getKey();
106 event_receiver->onMapEditEvent(event);
110 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
112 if(m_sector_cache != NULL && p == m_sector_cache_p){
113 MapSector * sector = m_sector_cache;
117 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
122 MapSector *sector = n->getValue();
124 // Cache the last result
125 m_sector_cache_p = p;
126 m_sector_cache = sector;
131 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
133 return getSectorNoGenerateNoExNoLock(p);
136 MapSector * Map::getSectorNoGenerate(v2s16 p)
138 MapSector *sector = getSectorNoGenerateNoEx(p);
140 throw InvalidPositionException();
145 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
147 v2s16 p2d(p3d.X, p3d.Z);
148 MapSector * sector = getSectorNoGenerateNoEx(p2d);
151 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
155 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
157 MapBlock *block = getBlockNoCreateNoEx(p3d);
159 throw InvalidPositionException();
163 bool Map::isNodeUnderground(v3s16 p)
165 v3s16 blockpos = getNodeBlockPos(p);
167 MapBlock * block = getBlockNoCreate(blockpos);
168 return block->getIsUnderground();
170 catch(InvalidPositionException &e)
176 bool Map::isValidPosition(v3s16 p)
178 v3s16 blockpos = getNodeBlockPos(p);
179 MapBlock *block = getBlockNoCreate(blockpos);
180 return (block != NULL);
183 // Returns a CONTENT_IGNORE node if not found
184 MapNode Map::getNodeNoEx(v3s16 p)
186 v3s16 blockpos = getNodeBlockPos(p);
187 MapBlock *block = getBlockNoCreateNoEx(blockpos);
189 return MapNode(CONTENT_IGNORE);
190 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
191 return block->getNodeNoCheck(relpos);
194 // throws InvalidPositionException if not found
195 MapNode Map::getNode(v3s16 p)
197 v3s16 blockpos = getNodeBlockPos(p);
198 MapBlock *block = getBlockNoCreateNoEx(blockpos);
200 throw InvalidPositionException();
201 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
202 return block->getNodeNoCheck(relpos);
205 // throws InvalidPositionException if not found
206 void Map::setNode(v3s16 p, MapNode & n)
208 v3s16 blockpos = getNodeBlockPos(p);
209 MapBlock *block = getBlockNoCreate(blockpos);
210 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
211 block->setNodeNoCheck(relpos, n);
216 Goes recursively through the neighbours of the node.
218 Alters only transparent nodes.
220 If the lighting of the neighbour is lower than the lighting of
221 the node was (before changing it to 0 at the step before), the
222 lighting of the neighbour is set to 0 and then the same stuff
223 repeats for the neighbour.
225 The ending nodes of the routine are stored in light_sources.
226 This is useful when a light is removed. In such case, this
227 routine can be called for the light node and then again for
228 light_sources to re-light the area without the removed light.
230 values of from_nodes are lighting values.
232 void Map::unspreadLight(enum LightBank bank,
233 core::map<v3s16, u8> & from_nodes,
234 core::map<v3s16, bool> & light_sources,
235 core::map<v3s16, MapBlock*> & modified_blocks)
238 v3s16(0,0,1), // back
240 v3s16(1,0,0), // right
241 v3s16(0,0,-1), // front
242 v3s16(0,-1,0), // bottom
243 v3s16(-1,0,0), // left
246 if(from_nodes.size() == 0)
249 u32 blockchangecount = 0;
251 core::map<v3s16, u8> unlighted_nodes;
252 core::map<v3s16, u8>::Iterator j;
253 j = from_nodes.getIterator();
256 Initialize block cache
259 MapBlock *block = NULL;
260 // Cache this a bit, too
261 bool block_checked_in_modified = false;
263 for(; j.atEnd() == false; j++)
265 v3s16 pos = j.getNode()->getKey();
266 v3s16 blockpos = getNodeBlockPos(pos);
268 // Only fetch a new block if the block position has changed
270 if(block == NULL || blockpos != blockpos_last){
271 block = getBlockNoCreate(blockpos);
272 blockpos_last = blockpos;
274 block_checked_in_modified = false;
278 catch(InvalidPositionException &e)
286 // Calculate relative position in block
287 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
289 // Get node straight from the block
290 MapNode n = block->getNode(relpos);
292 u8 oldlight = j.getNode()->getValue();
294 // Loop through 6 neighbors
295 for(u16 i=0; i<6; i++)
297 // Get the position of the neighbor node
298 v3s16 n2pos = pos + dirs[i];
300 // Get the block where the node is located
301 v3s16 blockpos = getNodeBlockPos(n2pos);
305 // Only fetch a new block if the block position has changed
307 if(block == NULL || blockpos != blockpos_last){
308 block = getBlockNoCreate(blockpos);
309 blockpos_last = blockpos;
311 block_checked_in_modified = false;
315 catch(InvalidPositionException &e)
320 // Calculate relative position in block
321 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
322 // Get node straight from the block
323 MapNode n2 = block->getNode(relpos);
325 bool changed = false;
327 //TODO: Optimize output by optimizing light_sources?
330 If the neighbor is dimmer than what was specified
331 as oldlight (the light of the previous node)
333 if(n2.getLight(bank) < oldlight)
336 And the neighbor is transparent and it has some light
338 if(n2.light_propagates() && n2.getLight(bank) != 0)
341 Set light to 0 and add to queue
344 u8 current_light = n2.getLight(bank);
345 n2.setLight(bank, 0);
346 block->setNode(relpos, n2);
348 unlighted_nodes.insert(n2pos, current_light);
352 Remove from light_sources if it is there
353 NOTE: This doesn't happen nearly at all
355 /*if(light_sources.find(n2pos))
357 infostream<<"Removed from light_sources"<<std::endl;
358 light_sources.remove(n2pos);
363 if(light_sources.find(n2pos) != NULL)
364 light_sources.remove(n2pos);*/
367 light_sources.insert(n2pos, true);
370 // Add to modified_blocks
371 if(changed == true && block_checked_in_modified == false)
373 // If the block is not found in modified_blocks, add.
374 if(modified_blocks.find(blockpos) == NULL)
376 modified_blocks.insert(blockpos, block);
378 block_checked_in_modified = true;
381 catch(InvalidPositionException &e)
388 /*infostream<<"unspreadLight(): Changed block "
389 <<blockchangecount<<" times"
390 <<" for "<<from_nodes.size()<<" nodes"
393 if(unlighted_nodes.size() > 0)
394 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
398 A single-node wrapper of the above
400 void Map::unLightNeighbors(enum LightBank bank,
401 v3s16 pos, u8 lightwas,
402 core::map<v3s16, bool> & light_sources,
403 core::map<v3s16, MapBlock*> & modified_blocks)
405 core::map<v3s16, u8> from_nodes;
406 from_nodes.insert(pos, lightwas);
408 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
412 Lights neighbors of from_nodes, collects all them and then
415 void Map::spreadLight(enum LightBank bank,
416 core::map<v3s16, bool> & from_nodes,
417 core::map<v3s16, MapBlock*> & modified_blocks)
419 const v3s16 dirs[6] = {
420 v3s16(0,0,1), // back
422 v3s16(1,0,0), // right
423 v3s16(0,0,-1), // front
424 v3s16(0,-1,0), // bottom
425 v3s16(-1,0,0), // left
428 if(from_nodes.size() == 0)
431 u32 blockchangecount = 0;
433 core::map<v3s16, bool> lighted_nodes;
434 core::map<v3s16, bool>::Iterator j;
435 j = from_nodes.getIterator();
438 Initialize block cache
441 MapBlock *block = NULL;
442 // Cache this a bit, too
443 bool block_checked_in_modified = false;
445 for(; j.atEnd() == false; j++)
446 //for(; j != from_nodes.end(); j++)
448 v3s16 pos = j.getNode()->getKey();
450 //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
451 v3s16 blockpos = getNodeBlockPos(pos);
453 // Only fetch a new block if the block position has changed
455 if(block == NULL || blockpos != blockpos_last){
456 block = getBlockNoCreate(blockpos);
457 blockpos_last = blockpos;
459 block_checked_in_modified = false;
463 catch(InvalidPositionException &e)
471 // Calculate relative position in block
472 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
474 // Get node straight from the block
475 MapNode n = block->getNode(relpos);
477 u8 oldlight = n.getLight(bank);
478 u8 newlight = diminish_light(oldlight);
480 // Loop through 6 neighbors
481 for(u16 i=0; i<6; i++){
482 // Get the position of the neighbor node
483 v3s16 n2pos = pos + dirs[i];
485 // Get the block where the node is located
486 v3s16 blockpos = getNodeBlockPos(n2pos);
490 // Only fetch a new block if the block position has changed
492 if(block == NULL || blockpos != blockpos_last){
493 block = getBlockNoCreate(blockpos);
494 blockpos_last = blockpos;
496 block_checked_in_modified = false;
500 catch(InvalidPositionException &e)
505 // Calculate relative position in block
506 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
507 // Get node straight from the block
508 MapNode n2 = block->getNode(relpos);
510 bool changed = false;
512 If the neighbor is brighter than the current node,
513 add to list (it will light up this node on its turn)
515 if(n2.getLight(bank) > undiminish_light(oldlight))
517 lighted_nodes.insert(n2pos, true);
518 //lighted_nodes.push_back(n2pos);
522 If the neighbor is dimmer than how much light this node
523 would spread on it, add to list
525 if(n2.getLight(bank) < newlight)
527 if(n2.light_propagates())
529 n2.setLight(bank, newlight);
530 block->setNode(relpos, n2);
531 lighted_nodes.insert(n2pos, true);
532 //lighted_nodes.push_back(n2pos);
537 // Add to modified_blocks
538 if(changed == true && block_checked_in_modified == false)
540 // If the block is not found in modified_blocks, add.
541 if(modified_blocks.find(blockpos) == NULL)
543 modified_blocks.insert(blockpos, block);
545 block_checked_in_modified = true;
548 catch(InvalidPositionException &e)
555 /*infostream<<"spreadLight(): Changed block "
556 <<blockchangecount<<" times"
557 <<" for "<<from_nodes.size()<<" nodes"
560 if(lighted_nodes.size() > 0)
561 spreadLight(bank, lighted_nodes, modified_blocks);
565 A single-node source variation of the above.
567 void Map::lightNeighbors(enum LightBank bank,
569 core::map<v3s16, MapBlock*> & modified_blocks)
571 core::map<v3s16, bool> from_nodes;
572 from_nodes.insert(pos, true);
573 spreadLight(bank, from_nodes, modified_blocks);
576 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
579 v3s16(0,0,1), // back
581 v3s16(1,0,0), // right
582 v3s16(0,0,-1), // front
583 v3s16(0,-1,0), // bottom
584 v3s16(-1,0,0), // left
587 u8 brightest_light = 0;
588 v3s16 brightest_pos(0,0,0);
589 bool found_something = false;
591 // Loop through 6 neighbors
592 for(u16 i=0; i<6; i++){
593 // Get the position of the neighbor node
594 v3s16 n2pos = p + dirs[i];
599 catch(InvalidPositionException &e)
603 if(n2.getLight(bank) > brightest_light || found_something == false){
604 brightest_light = n2.getLight(bank);
605 brightest_pos = n2pos;
606 found_something = true;
610 if(found_something == false)
611 throw InvalidPositionException();
613 return brightest_pos;
617 Propagates sunlight down from a node.
618 Starting point gets sunlight.
620 Returns the lowest y value of where the sunlight went.
622 Mud is turned into grass in where the sunlight stops.
624 s16 Map::propagateSunlight(v3s16 start,
625 core::map<v3s16, MapBlock*> & modified_blocks)
630 v3s16 pos(start.X, y, start.Z);
632 v3s16 blockpos = getNodeBlockPos(pos);
635 block = getBlockNoCreate(blockpos);
637 catch(InvalidPositionException &e)
642 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
643 MapNode n = block->getNode(relpos);
645 if(n.sunlight_propagates())
647 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
648 block->setNode(relpos, n);
650 modified_blocks.insert(blockpos, block);
654 /*// Turn mud into grass
655 if(n.getContent() == CONTENT_MUD)
657 n.setContent(CONTENT_GRASS);
658 block->setNode(relpos, n);
659 modified_blocks.insert(blockpos, block);
662 // Sunlight goes no further
669 void Map::updateLighting(enum LightBank bank,
670 core::map<v3s16, MapBlock*> & a_blocks,
671 core::map<v3s16, MapBlock*> & modified_blocks)
673 /*m_dout<<DTIME<<"Map::updateLighting(): "
674 <<a_blocks.size()<<" blocks."<<std::endl;*/
676 //TimeTaker timer("updateLighting");
680 //u32 count_was = modified_blocks.size();
682 core::map<v3s16, MapBlock*> blocks_to_update;
684 core::map<v3s16, bool> light_sources;
686 core::map<v3s16, u8> unlight_from;
688 core::map<v3s16, MapBlock*>::Iterator i;
689 i = a_blocks.getIterator();
690 for(; i.atEnd() == false; i++)
692 MapBlock *block = i.getNode()->getValue();
696 // Don't bother with dummy blocks.
700 v3s16 pos = block->getPos();
701 modified_blocks.insert(pos, block);
703 blocks_to_update.insert(pos, block);
706 Clear all light from block
708 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
709 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
710 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
715 MapNode n = block->getNode(v3s16(x,y,z));
716 u8 oldlight = n.getLight(bank);
718 block->setNode(v3s16(x,y,z), n);
720 // Collect borders for unlighting
721 if(x==0 || x == MAP_BLOCKSIZE-1
722 || y==0 || y == MAP_BLOCKSIZE-1
723 || z==0 || z == MAP_BLOCKSIZE-1)
725 v3s16 p_map = p + v3s16(
728 MAP_BLOCKSIZE*pos.Z);
729 unlight_from.insert(p_map, oldlight);
732 catch(InvalidPositionException &e)
735 This would happen when dealing with a
739 infostream<<"updateLighting(): InvalidPositionException"
744 if(bank == LIGHTBANK_DAY)
746 bool bottom_valid = block->propagateSunlight(light_sources);
748 // If bottom is valid, we're done.
752 else if(bank == LIGHTBANK_NIGHT)
754 // For night lighting, sunlight is not propagated
759 // Invalid lighting bank
763 /*infostream<<"Bottom for sunlight-propagated block ("
764 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
767 // Bottom sunlight is not valid; get the block and loop to it
771 block = getBlockNoCreate(pos);
773 catch(InvalidPositionException &e)
782 Enable this to disable proper lighting for speeding up map
783 generation for testing or whatever
786 //if(g_settings->get(""))
788 core::map<v3s16, MapBlock*>::Iterator i;
789 i = blocks_to_update.getIterator();
790 for(; i.atEnd() == false; i++)
792 MapBlock *block = i.getNode()->getValue();
793 v3s16 p = block->getPos();
794 block->setLightingExpired(false);
802 TimeTaker timer("unspreadLight");
803 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
808 u32 diff = modified_blocks.size() - count_was;
809 count_was = modified_blocks.size();
810 infostream<<"unspreadLight modified "<<diff<<std::endl;
814 TimeTaker timer("spreadLight");
815 spreadLight(bank, light_sources, modified_blocks);
820 u32 diff = modified_blocks.size() - count_was;
821 count_was = modified_blocks.size();
822 infostream<<"spreadLight modified "<<diff<<std::endl;
827 //MapVoxelManipulator vmanip(this);
829 // Make a manual voxel manipulator and load all the blocks
830 // that touch the requested blocks
831 ManualMapVoxelManipulator vmanip(this);
832 core::map<v3s16, MapBlock*>::Iterator i;
833 i = blocks_to_update.getIterator();
834 for(; i.atEnd() == false; i++)
836 MapBlock *block = i.getNode()->getValue();
837 v3s16 p = block->getPos();
839 // Add all surrounding blocks
840 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
843 Add all surrounding blocks that have up-to-date lighting
844 NOTE: This doesn't quite do the job (not everything
845 appropriate is lighted)
847 /*for(s16 z=-1; z<=1; z++)
848 for(s16 y=-1; y<=1; y++)
849 for(s16 x=-1; x<=1; x++)
852 MapBlock *block = getBlockNoCreateNoEx(p);
857 if(block->getLightingExpired())
859 vmanip.initialEmerge(p, p);
862 // Lighting of block will be updated completely
863 block->setLightingExpired(false);
867 //TimeTaker timer("unSpreadLight");
868 vmanip.unspreadLight(bank, unlight_from, light_sources);
871 //TimeTaker timer("spreadLight");
872 vmanip.spreadLight(bank, light_sources);
875 //TimeTaker timer("blitBack");
876 vmanip.blitBack(modified_blocks);
878 /*infostream<<"emerge_time="<<emerge_time<<std::endl;
882 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
885 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
886 core::map<v3s16, MapBlock*> & modified_blocks)
888 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
889 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
892 Update information about whether day and night light differ
894 for(core::map<v3s16, MapBlock*>::Iterator
895 i = modified_blocks.getIterator();
896 i.atEnd() == false; i++)
898 MapBlock *block = i.getNode()->getValue();
899 block->updateDayNightDiff();
905 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
906 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
909 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
910 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
913 From this node to nodes underneath:
914 If lighting is sunlight (1.0), unlight neighbours and
919 v3s16 toppos = p + v3s16(0,1,0);
920 v3s16 bottompos = p + v3s16(0,-1,0);
922 bool node_under_sunlight = true;
923 core::map<v3s16, bool> light_sources;
926 If there is a node at top and it doesn't have sunlight,
927 there has not been any sunlight going down.
929 Otherwise there probably is.
932 MapNode topnode = getNode(toppos);
934 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
935 node_under_sunlight = false;
937 catch(InvalidPositionException &e)
943 If the new node is solid and there is grass below, change it to mud
945 if(content_features(n).walkable == true)
948 MapNode bottomnode = getNode(bottompos);
950 if(bottomnode.getContent() == CONTENT_GRASS
951 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
953 bottomnode.setContent(CONTENT_MUD);
954 setNode(bottompos, bottomnode);
957 catch(InvalidPositionException &e)
965 If the new node is mud and it is under sunlight, change it
968 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
970 n.setContent(CONTENT_GRASS);
975 Remove all light that has come out of this node
978 enum LightBank banks[] =
983 for(s32 i=0; i<2; i++)
985 enum LightBank bank = banks[i];
987 u8 lightwas = getNode(p).getLight(bank);
989 // Add the block of the added node to modified_blocks
990 v3s16 blockpos = getNodeBlockPos(p);
991 MapBlock * block = getBlockNoCreate(blockpos);
992 assert(block != NULL);
993 modified_blocks.insert(blockpos, block);
995 assert(isValidPosition(p));
997 // Unlight neighbours of node.
998 // This means setting light of all consequent dimmer nodes
1000 // This also collects the nodes at the border which will spread
1001 // light again into this.
1002 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
1004 n.setLight(bank, 0);
1008 If node lets sunlight through and is under sunlight, it has
1011 if(node_under_sunlight && content_features(n).sunlight_propagates)
1013 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1017 Set the node on the map
1026 NodeMetadata *meta_proto = content_features(n).initial_metadata;
1029 NodeMetadata *meta = meta_proto->clone(m_gamedef);
1030 meta->setOwner(player_name);
1031 setNodeMetadata(p, meta);
1035 If node is under sunlight and doesn't let sunlight through,
1036 take all sunlighted nodes under it and clear light from them
1037 and from where the light has been spread.
1038 TODO: This could be optimized by mass-unlighting instead
1041 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1045 //m_dout<<DTIME<<"y="<<y<<std::endl;
1046 v3s16 n2pos(p.X, y, p.Z);
1050 n2 = getNode(n2pos);
1052 catch(InvalidPositionException &e)
1057 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1059 unLightNeighbors(LIGHTBANK_DAY,
1060 n2pos, n2.getLight(LIGHTBANK_DAY),
1061 light_sources, modified_blocks);
1062 n2.setLight(LIGHTBANK_DAY, 0);
1070 for(s32 i=0; i<2; i++)
1072 enum LightBank bank = banks[i];
1075 Spread light from all nodes that might be capable of doing so
1077 spreadLight(bank, light_sources, modified_blocks);
1081 Update information about whether day and night light differ
1083 for(core::map<v3s16, MapBlock*>::Iterator
1084 i = modified_blocks.getIterator();
1085 i.atEnd() == false; i++)
1087 MapBlock *block = i.getNode()->getValue();
1088 block->updateDayNightDiff();
1092 Add neighboring liquid nodes and the node itself if it is
1093 liquid (=water node was added) to transform queue.
1096 v3s16(0,0,0), // self
1097 v3s16(0,0,1), // back
1098 v3s16(0,1,0), // top
1099 v3s16(1,0,0), // right
1100 v3s16(0,0,-1), // front
1101 v3s16(0,-1,0), // bottom
1102 v3s16(-1,0,0), // left
1104 for(u16 i=0; i<7; i++)
1109 v3s16 p2 = p + dirs[i];
1111 MapNode n2 = getNode(p2);
1112 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1114 m_transforming_liquid.push_back(p2);
1117 }catch(InvalidPositionException &e)
1125 void Map::removeNodeAndUpdate(v3s16 p,
1126 core::map<v3s16, MapBlock*> &modified_blocks)
1128 /*PrintInfo(m_dout);
1129 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1130 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1132 bool node_under_sunlight = true;
1134 v3s16 toppos = p + v3s16(0,1,0);
1136 // Node will be replaced with this
1137 content_t replace_material = CONTENT_AIR;
1140 If there is a node at top and it doesn't have sunlight,
1141 there will be no sunlight going down.
1144 MapNode topnode = getNode(toppos);
1146 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1147 node_under_sunlight = false;
1149 catch(InvalidPositionException &e)
1153 core::map<v3s16, bool> light_sources;
1155 enum LightBank banks[] =
1160 for(s32 i=0; i<2; i++)
1162 enum LightBank bank = banks[i];
1165 Unlight neighbors (in case the node is a light source)
1167 unLightNeighbors(bank, p,
1168 getNode(p).getLight(bank),
1169 light_sources, modified_blocks);
1173 Remove node metadata
1176 removeNodeMetadata(p);
1180 This also clears the lighting.
1184 n.setContent(replace_material);
1187 for(s32 i=0; i<2; i++)
1189 enum LightBank bank = banks[i];
1192 Recalculate lighting
1194 spreadLight(bank, light_sources, modified_blocks);
1197 // Add the block of the removed node to modified_blocks
1198 v3s16 blockpos = getNodeBlockPos(p);
1199 MapBlock * block = getBlockNoCreate(blockpos);
1200 assert(block != NULL);
1201 modified_blocks.insert(blockpos, block);
1204 If the removed node was under sunlight, propagate the
1205 sunlight down from it and then light all neighbors
1206 of the propagated blocks.
1208 if(node_under_sunlight)
1210 s16 ybottom = propagateSunlight(p, modified_blocks);
1211 /*m_dout<<DTIME<<"Node was under sunlight. "
1212 "Propagating sunlight";
1213 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1215 for(; y >= ybottom; y--)
1217 v3s16 p2(p.X, y, p.Z);
1218 /*m_dout<<DTIME<<"lighting neighbors of node ("
1219 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1221 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1226 // Set the lighting of this node to 0
1227 // TODO: Is this needed? Lighting is cleared up there already.
1229 MapNode n = getNode(p);
1230 n.setLight(LIGHTBANK_DAY, 0);
1233 catch(InvalidPositionException &e)
1239 for(s32 i=0; i<2; i++)
1241 enum LightBank bank = banks[i];
1243 // Get the brightest neighbour node and propagate light from it
1244 v3s16 n2p = getBrightestNeighbour(bank, p);
1246 MapNode n2 = getNode(n2p);
1247 lightNeighbors(bank, n2p, modified_blocks);
1249 catch(InvalidPositionException &e)
1255 Update information about whether day and night light differ
1257 for(core::map<v3s16, MapBlock*>::Iterator
1258 i = modified_blocks.getIterator();
1259 i.atEnd() == false; i++)
1261 MapBlock *block = i.getNode()->getValue();
1262 block->updateDayNightDiff();
1266 Add neighboring liquid nodes and this node to transform queue.
1267 (it's vital for the node itself to get updated last.)
1270 v3s16(0,0,1), // back
1271 v3s16(0,1,0), // top
1272 v3s16(1,0,0), // right
1273 v3s16(0,0,-1), // front
1274 v3s16(0,-1,0), // bottom
1275 v3s16(-1,0,0), // left
1276 v3s16(0,0,0), // self
1278 for(u16 i=0; i<7; i++)
1283 v3s16 p2 = p + dirs[i];
1285 MapNode n2 = getNode(p2);
1286 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1288 m_transforming_liquid.push_back(p2);
1291 }catch(InvalidPositionException &e)
1297 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1300 event.type = MEET_ADDNODE;
1304 bool succeeded = true;
1306 core::map<v3s16, MapBlock*> modified_blocks;
1307 std::string st = std::string("");
1308 addNodeAndUpdate(p, n, modified_blocks, st);
1310 // Copy modified_blocks to event
1311 for(core::map<v3s16, MapBlock*>::Iterator
1312 i = modified_blocks.getIterator();
1313 i.atEnd()==false; i++)
1315 event.modified_blocks.insert(i.getNode()->getKey(), false);
1318 catch(InvalidPositionException &e){
1322 dispatchEvent(&event);
1327 bool Map::removeNodeWithEvent(v3s16 p)
1330 event.type = MEET_REMOVENODE;
1333 bool succeeded = true;
1335 core::map<v3s16, MapBlock*> modified_blocks;
1336 removeNodeAndUpdate(p, modified_blocks);
1338 // Copy modified_blocks to event
1339 for(core::map<v3s16, MapBlock*>::Iterator
1340 i = modified_blocks.getIterator();
1341 i.atEnd()==false; i++)
1343 event.modified_blocks.insert(i.getNode()->getKey(), false);
1346 catch(InvalidPositionException &e){
1350 dispatchEvent(&event);
1355 bool Map::dayNightDiffed(v3s16 blockpos)
1358 v3s16 p = blockpos + v3s16(0,0,0);
1359 MapBlock *b = getBlockNoCreate(p);
1360 if(b->dayNightDiffed())
1363 catch(InvalidPositionException &e){}
1366 v3s16 p = blockpos + v3s16(-1,0,0);
1367 MapBlock *b = getBlockNoCreate(p);
1368 if(b->dayNightDiffed())
1371 catch(InvalidPositionException &e){}
1373 v3s16 p = blockpos + v3s16(0,-1,0);
1374 MapBlock *b = getBlockNoCreate(p);
1375 if(b->dayNightDiffed())
1378 catch(InvalidPositionException &e){}
1380 v3s16 p = blockpos + v3s16(0,0,-1);
1381 MapBlock *b = getBlockNoCreate(p);
1382 if(b->dayNightDiffed())
1385 catch(InvalidPositionException &e){}
1388 v3s16 p = blockpos + v3s16(1,0,0);
1389 MapBlock *b = getBlockNoCreate(p);
1390 if(b->dayNightDiffed())
1393 catch(InvalidPositionException &e){}
1395 v3s16 p = blockpos + v3s16(0,1,0);
1396 MapBlock *b = getBlockNoCreate(p);
1397 if(b->dayNightDiffed())
1400 catch(InvalidPositionException &e){}
1402 v3s16 p = blockpos + v3s16(0,0,1);
1403 MapBlock *b = getBlockNoCreate(p);
1404 if(b->dayNightDiffed())
1407 catch(InvalidPositionException &e){}
1413 Updates usage timers
1415 void Map::timerUpdate(float dtime, float unload_timeout,
1416 core::list<v3s16> *unloaded_blocks)
1418 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1420 core::list<v2s16> sector_deletion_queue;
1421 u32 deleted_blocks_count = 0;
1422 u32 saved_blocks_count = 0;
1424 core::map<v2s16, MapSector*>::Iterator si;
1427 si = m_sectors.getIterator();
1428 for(; si.atEnd() == false; si++)
1430 MapSector *sector = si.getNode()->getValue();
1432 bool all_blocks_deleted = true;
1434 core::list<MapBlock*> blocks;
1435 sector->getBlocks(blocks);
1437 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1438 i != blocks.end(); i++)
1440 MapBlock *block = (*i);
1442 block->incrementUsageTimer(dtime);
1444 if(block->getUsageTimer() > unload_timeout)
1446 v3s16 p = block->getPos();
1449 if(block->getModified() != MOD_STATE_CLEAN
1450 && save_before_unloading)
1453 saved_blocks_count++;
1456 // Delete from memory
1457 sector->deleteBlock(block);
1460 unloaded_blocks->push_back(p);
1462 deleted_blocks_count++;
1466 all_blocks_deleted = false;
1470 if(all_blocks_deleted)
1472 sector_deletion_queue.push_back(si.getNode()->getKey());
1477 // Finally delete the empty sectors
1478 deleteSectors(sector_deletion_queue);
1480 if(deleted_blocks_count != 0)
1482 PrintInfo(infostream); // ServerMap/ClientMap:
1483 infostream<<"Unloaded "<<deleted_blocks_count
1484 <<" blocks from memory";
1485 if(save_before_unloading)
1486 infostream<<", of which "<<saved_blocks_count<<" were written";
1487 infostream<<"."<<std::endl;
1491 void Map::deleteSectors(core::list<v2s16> &list)
1493 core::list<v2s16>::Iterator j;
1494 for(j=list.begin(); j!=list.end(); j++)
1496 MapSector *sector = m_sectors[*j];
1497 // If sector is in sector cache, remove it from there
1498 if(m_sector_cache == sector)
1499 m_sector_cache = NULL;
1500 // Remove from map and delete
1501 m_sectors.remove(*j);
1507 void Map::unloadUnusedData(float timeout,
1508 core::list<v3s16> *deleted_blocks)
1510 core::list<v2s16> sector_deletion_queue;
1511 u32 deleted_blocks_count = 0;
1512 u32 saved_blocks_count = 0;
1514 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1515 for(; si.atEnd() == false; si++)
1517 MapSector *sector = si.getNode()->getValue();
1519 bool all_blocks_deleted = true;
1521 core::list<MapBlock*> blocks;
1522 sector->getBlocks(blocks);
1523 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1524 i != blocks.end(); i++)
1526 MapBlock *block = (*i);
1528 if(block->getUsageTimer() > timeout)
1531 if(block->getModified() != MOD_STATE_CLEAN)
1534 saved_blocks_count++;
1536 // Delete from memory
1537 sector->deleteBlock(block);
1538 deleted_blocks_count++;
1542 all_blocks_deleted = false;
1546 if(all_blocks_deleted)
1548 sector_deletion_queue.push_back(si.getNode()->getKey());
1552 deleteSectors(sector_deletion_queue);
1554 infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1555 <<", of which "<<saved_blocks_count<<" were wr."
1558 //return sector_deletion_queue.getSize();
1559 //return deleted_blocks_count;
1563 void Map::PrintInfo(std::ostream &out)
1568 #define WATER_DROP_BOOST 4
1572 NEIGHBOR_SAME_LEVEL,
1575 struct NodeNeighbor {
1581 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1583 DSTACK(__FUNCTION_NAME);
1584 //TimeTaker timer("transformLiquids()");
1587 u32 initial_size = m_transforming_liquid.size();
1589 /*if(initial_size != 0)
1590 infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1592 // list of nodes that due to viscosity have not reached their max level height
1593 UniqueQueue<v3s16> must_reflow;
1595 // List of MapBlocks that will require a lighting update (due to lava)
1596 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1598 while(m_transforming_liquid.size() != 0)
1600 // This should be done here so that it is done when continue is used
1601 if(loopcount >= initial_size * 3)
1606 Get a queued transforming liquid node
1608 v3s16 p0 = m_transforming_liquid.pop_front();
1610 MapNode n0 = getNodeNoEx(p0);
1613 Collect information about current node
1615 s8 liquid_level = -1;
1616 u8 liquid_kind = CONTENT_IGNORE;
1617 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1618 switch (liquid_type) {
1620 liquid_level = LIQUID_LEVEL_SOURCE;
1621 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1623 case LIQUID_FLOWING:
1624 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1625 liquid_kind = n0.getContent();
1628 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1629 // continue with the next node.
1630 if (n0.getContent() != CONTENT_AIR)
1632 liquid_kind = CONTENT_AIR;
1637 Collect information about the environment
1639 const v3s16 *dirs = g_6dirs;
1640 NodeNeighbor sources[6]; // surrounding sources
1641 int num_sources = 0;
1642 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1644 NodeNeighbor airs[6]; // surrounding air
1646 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1647 int num_neutrals = 0;
1648 bool flowing_down = false;
1649 for (u16 i = 0; i < 6; i++) {
1650 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1653 nt = NEIGHBOR_UPPER;
1656 nt = NEIGHBOR_LOWER;
1659 v3s16 npos = p0 + dirs[i];
1660 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1661 switch (content_features(nb.n.getContent()).liquid_type) {
1663 if (nb.n.getContent() == CONTENT_AIR) {
1664 airs[num_airs++] = nb;
1665 // if the current node is a water source the neighbor
1666 // should be enqueded for transformation regardless of whether the
1667 // current node changes or not.
1668 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1669 m_transforming_liquid.push_back(npos);
1670 // if the current node happens to be a flowing node, it will start to flow down here.
1671 if (nb.t == NEIGHBOR_LOWER) {
1672 flowing_down = true;
1675 neutrals[num_neutrals++] = nb;
1679 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1680 if (liquid_kind == CONTENT_AIR)
1681 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1682 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1683 neutrals[num_neutrals++] = nb;
1685 sources[num_sources++] = nb;
1688 case LIQUID_FLOWING:
1689 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1690 if (liquid_kind == CONTENT_AIR)
1691 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1692 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1693 neutrals[num_neutrals++] = nb;
1695 flows[num_flows++] = nb;
1696 if (nb.t == NEIGHBOR_LOWER)
1697 flowing_down = true;
1704 decide on the type (and possibly level) of the current node
1706 content_t new_node_content;
1707 s8 new_node_level = -1;
1708 s8 max_node_level = -1;
1709 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1710 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1711 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1712 // it's perfectly safe to use liquid_kind here to determine the new node content.
1713 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1714 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1715 // liquid_kind is set properly, see above
1716 new_node_content = liquid_kind;
1717 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1719 // no surrounding sources, so get the maximum level that can flow into this node
1720 for (u16 i = 0; i < num_flows; i++) {
1721 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1722 switch (flows[i].t) {
1723 case NEIGHBOR_UPPER:
1724 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1725 max_node_level = LIQUID_LEVEL_MAX;
1726 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1727 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1728 } else if (nb_liquid_level > max_node_level)
1729 max_node_level = nb_liquid_level;
1731 case NEIGHBOR_LOWER:
1733 case NEIGHBOR_SAME_LEVEL:
1734 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1735 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1736 max_node_level = nb_liquid_level - 1;
1742 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1743 if (viscosity > 1 && max_node_level != liquid_level) {
1744 // amount to gain, limited by viscosity
1745 // must be at least 1 in absolute value
1746 s8 level_inc = max_node_level - liquid_level;
1747 if (level_inc < -viscosity || level_inc > viscosity)
1748 new_node_level = liquid_level + level_inc/viscosity;
1749 else if (level_inc < 0)
1750 new_node_level = liquid_level - 1;
1751 else if (level_inc > 0)
1752 new_node_level = liquid_level + 1;
1753 if (new_node_level != max_node_level)
1754 must_reflow.push_back(p0);
1756 new_node_level = max_node_level;
1758 if (new_node_level >= 0)
1759 new_node_content = liquid_kind;
1761 new_node_content = CONTENT_AIR;
1766 check if anything has changed. if not, just continue with the next node.
1768 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1769 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1770 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1776 update the current node
1778 //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1779 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1780 // set level to last 3 bits, flowing down bit to 4th bit
1781 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1783 // set the liquid level and flow bit to 0
1784 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1786 n0.setContent(new_node_content);
1788 v3s16 blockpos = getNodeBlockPos(p0);
1789 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1791 modified_blocks.insert(blockpos, block);
1792 // If node emits light, MapBlock requires lighting update
1793 if(content_features(n0).light_source != 0)
1794 lighting_modified_blocks[block->getPos()] = block;
1798 enqueue neighbors for update if neccessary
1800 switch (content_features(n0.getContent()).liquid_type) {
1802 case LIQUID_FLOWING:
1803 // make sure source flows into all neighboring nodes
1804 for (u16 i = 0; i < num_flows; i++)
1805 if (flows[i].t != NEIGHBOR_UPPER)
1806 m_transforming_liquid.push_back(flows[i].p);
1807 for (u16 i = 0; i < num_airs; i++)
1808 if (airs[i].t != NEIGHBOR_UPPER)
1809 m_transforming_liquid.push_back(airs[i].p);
1812 // this flow has turned to air; neighboring flows might need to do the same
1813 for (u16 i = 0; i < num_flows; i++)
1814 m_transforming_liquid.push_back(flows[i].p);
1818 //infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1819 while (must_reflow.size() > 0)
1820 m_transforming_liquid.push_back(must_reflow.pop_front());
1821 updateLighting(lighting_modified_blocks, modified_blocks);
1824 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1826 v3s16 blockpos = getNodeBlockPos(p);
1827 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1828 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1830 infostream<<"Map::getNodeMetadata(): Need to emerge "
1831 <<PP(blockpos)<<std::endl;
1832 block = emergeBlock(blockpos, false);
1836 infostream<<"WARNING: Map::getNodeMetadata(): Block not found"
1840 NodeMetadata *meta = block->m_node_metadata->get(p_rel);
1844 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1846 v3s16 blockpos = getNodeBlockPos(p);
1847 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1848 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1850 infostream<<"Map::setNodeMetadata(): Need to emerge "
1851 <<PP(blockpos)<<std::endl;
1852 block = emergeBlock(blockpos, false);
1856 infostream<<"WARNING: Map::setNodeMetadata(): Block not found"
1860 block->m_node_metadata->set(p_rel, meta);
1863 void Map::removeNodeMetadata(v3s16 p)
1865 v3s16 blockpos = getNodeBlockPos(p);
1866 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1867 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1870 infostream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1874 block->m_node_metadata->remove(p_rel);
1877 void Map::nodeMetadataStep(float dtime,
1878 core::map<v3s16, MapBlock*> &changed_blocks)
1882 Currently there is no way to ensure that all the necessary
1883 blocks are loaded when this is run. (They might get unloaded)
1884 NOTE: ^- Actually, that might not be so. In a quick test it
1885 reloaded a block with a furnace when I walked back to it from
1888 core::map<v2s16, MapSector*>::Iterator si;
1889 si = m_sectors.getIterator();
1890 for(; si.atEnd() == false; si++)
1892 MapSector *sector = si.getNode()->getValue();
1893 core::list< MapBlock * > sectorblocks;
1894 sector->getBlocks(sectorblocks);
1895 core::list< MapBlock * >::Iterator i;
1896 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1898 MapBlock *block = *i;
1899 bool changed = block->m_node_metadata->step(dtime);
1901 changed_blocks[block->getPos()] = block;
1910 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef):
1911 Map(dout_server, gamedef),
1913 m_map_metadata_changed(true),
1915 m_database_read(NULL),
1916 m_database_write(NULL)
1918 infostream<<__FUNCTION_NAME<<std::endl;
1920 //m_chunksize = 8; // Takes a few seconds
1922 if (g_settings->get("fixed_map_seed").empty())
1924 m_seed = (((u64)(myrand()%0xffff)<<0)
1925 + ((u64)(myrand()%0xffff)<<16)
1926 + ((u64)(myrand()%0xffff)<<32)
1927 + ((u64)(myrand()%0xffff)<<48));
1931 m_seed = g_settings->getU64("fixed_map_seed");
1935 Experimental and debug stuff
1942 Try to load map; if not found, create a new one.
1945 m_savedir = savedir;
1946 m_map_saving_enabled = false;
1950 // If directory exists, check contents and load if possible
1951 if(fs::PathExists(m_savedir))
1953 // If directory is empty, it is safe to save into it.
1954 if(fs::GetDirListing(m_savedir).size() == 0)
1956 infostream<<"Server: Empty save directory is valid."
1958 m_map_saving_enabled = true;
1963 // Load map metadata (seed, chunksize)
1966 catch(FileNotGoodException &e){
1967 infostream<<"WARNING: Could not load map metadata"
1968 //<<" Disabling chunk-based generator."
1974 // Load chunk metadata
1977 catch(FileNotGoodException &e){
1978 infostream<<"WARNING: Could not load chunk metadata."
1979 <<" Disabling chunk-based generator."
1984 /*infostream<<"Server: Successfully loaded chunk "
1985 "metadata and sector (0,0) from "<<savedir<<
1986 ", assuming valid save directory."
1989 infostream<<"Server: Successfully loaded map "
1990 <<"and chunk metadata from "<<savedir
1991 <<", assuming valid save directory."
1994 m_map_saving_enabled = true;
1995 // Map loaded, not creating new one
1999 // If directory doesn't exist, it is safe to save to it
2001 m_map_saving_enabled = true;
2004 catch(std::exception &e)
2006 infostream<<"WARNING: Server: Failed to load map from "<<savedir
2007 <<", exception: "<<e.what()<<std::endl;
2008 infostream<<"Please remove the map or fix it."<<std::endl;
2009 infostream<<"WARNING: Map saving will be disabled."<<std::endl;
2012 infostream<<"Initializing new map."<<std::endl;
2014 // Create zero sector
2015 emergeSector(v2s16(0,0));
2017 // Initially write whole map
2021 ServerMap::~ServerMap()
2023 infostream<<__FUNCTION_NAME<<std::endl;
2027 if(m_map_saving_enabled)
2029 // Save only changed parts
2031 infostream<<"Server: saved map to "<<m_savedir<<std::endl;
2035 infostream<<"Server: map not saved"<<std::endl;
2038 catch(std::exception &e)
2040 infostream<<"Server: Failed to save map to "<<m_savedir
2041 <<", exception: "<<e.what()<<std::endl;
2045 Close database if it was opened
2048 sqlite3_finalize(m_database_read);
2049 if(m_database_write)
2050 sqlite3_finalize(m_database_write);
2052 sqlite3_close(m_database);
2058 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2059 for(; i.atEnd() == false; i++)
2061 MapChunk *chunk = i.getNode()->getValue();
2067 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2069 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2070 if(enable_mapgen_debug_info)
2071 infostream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2072 <<blockpos.Z<<")"<<std::endl;
2074 // Do nothing if not inside limits (+-1 because of neighbors)
2075 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2076 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2082 data->no_op = false;
2083 data->seed = m_seed;
2084 data->blockpos = blockpos;
2087 Create the whole area of this and the neighboring blocks
2090 //TimeTaker timer("initBlockMake() create area");
2092 for(s16 x=-1; x<=1; x++)
2093 for(s16 z=-1; z<=1; z++)
2095 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2096 // Sector metadata is loaded from disk if not already loaded.
2097 ServerMapSector *sector = createSector(sectorpos);
2100 for(s16 y=-1; y<=1; y++)
2102 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2103 //MapBlock *block = createBlock(p);
2104 // 1) get from memory, 2) load from disk
2105 MapBlock *block = emergeBlock(p, false);
2106 // 3) create a blank one
2109 block = createBlock(p);
2112 Block gets sunlight if this is true.
2114 Refer to the map generator heuristics.
2116 bool ug = mapgen::block_is_underground(data->seed, p);
2117 block->setIsUnderground(ug);
2120 // Lighting will not be valid after make_chunk is called
2121 block->setLightingExpired(true);
2122 // Lighting will be calculated
2123 //block->setLightingExpired(false);
2129 Now we have a big empty area.
2131 Make a ManualMapVoxelManipulator that contains this and the
2135 // The area that contains this block and it's neighbors
2136 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2137 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2139 data->vmanip = new ManualMapVoxelManipulator(this);
2140 //data->vmanip->setMap(this);
2144 //TimeTaker timer("initBlockMake() initialEmerge");
2145 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2148 // Data is ready now.
2151 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2152 core::map<v3s16, MapBlock*> &changed_blocks)
2154 v3s16 blockpos = data->blockpos;
2155 /*infostream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2156 <<blockpos.Z<<")"<<std::endl;*/
2160 //infostream<<"finishBlockMake(): no-op"<<std::endl;
2164 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2166 /*infostream<<"Resulting vmanip:"<<std::endl;
2167 data->vmanip.print(infostream);*/
2170 Blit generated stuff to map
2171 NOTE: blitBackAll adds nearly everything to changed_blocks
2175 //TimeTaker timer("finishBlockMake() blitBackAll");
2176 data->vmanip->blitBackAll(&changed_blocks);
2179 if(enable_mapgen_debug_info)
2180 infostream<<"finishBlockMake: changed_blocks.size()="
2181 <<changed_blocks.size()<<std::endl;
2184 Copy transforming liquid information
2186 while(data->transforming_liquid.size() > 0)
2188 v3s16 p = data->transforming_liquid.pop_front();
2189 m_transforming_liquid.push_back(p);
2195 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2199 Set is_underground flag for lighting with sunlight.
2201 Refer to map generator heuristics.
2203 NOTE: This is done in initChunkMake
2205 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2209 Add sunlight to central block.
2210 This makes in-dark-spawning monsters to not flood the whole thing.
2211 Do not spread the light, though.
2213 /*core::map<v3s16, bool> light_sources;
2214 bool black_air_left = false;
2215 block->propagateSunlight(light_sources, true, &black_air_left);*/
2218 NOTE: Lighting and object adding shouldn't really be here, but
2219 lighting is a bit tricky to move properly to makeBlock.
2220 TODO: Do this the right way anyway, that is, move it to makeBlock.
2221 - There needs to be some way for makeBlock to report back if
2222 the lighting update is going further down because of the
2223 new block blocking light
2228 NOTE: This takes ~60ms, TODO: Investigate why
2231 TimeTaker t("finishBlockMake lighting update");
2233 core::map<v3s16, MapBlock*> lighting_update_blocks;
2236 lighting_update_blocks.insert(block->getPos(), block);
2241 v3s16 p = block->getPos()+v3s16(x,1,z);
2242 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2246 // All modified blocks
2247 // NOTE: Should this be done? If this is not done, then the lighting
2248 // of the others will be updated in a different place, one by one, i
2249 // think... or they might not? Well, at least they are left marked as
2250 // "lighting expired"; it seems that is not handled at all anywhere,
2251 // so enabling this will slow it down A LOT because otherwise it
2252 // would not do this at all. This causes the black trees.
2253 for(core::map<v3s16, MapBlock*>::Iterator
2254 i = changed_blocks.getIterator();
2255 i.atEnd() == false; i++)
2257 lighting_update_blocks.insert(i.getNode()->getKey(),
2258 i.getNode()->getValue());
2260 /*// Also force-add all the upmost blocks for proper sunlight
2261 for(s16 x=-1; x<=1; x++)
2262 for(s16 z=-1; z<=1; z++)
2264 v3s16 p = block->getPos()+v3s16(x,1,z);
2265 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2268 updateLighting(lighting_update_blocks, changed_blocks);
2271 Set lighting to non-expired state in all of them.
2272 This is cheating, but it is not fast enough if all of them
2273 would actually be updated.
2275 for(s16 x=-1; x<=1; x++)
2276 for(s16 y=-1; y<=1; y++)
2277 for(s16 z=-1; z<=1; z++)
2279 v3s16 p = block->getPos()+v3s16(x,y,z);
2280 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2283 if(enable_mapgen_debug_info == false)
2284 t.stop(true); // Hide output
2288 Add random objects to block
2290 mapgen::add_random_objects(block);
2293 Go through changed blocks
2295 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2296 i.atEnd() == false; i++)
2298 MapBlock *block = i.getNode()->getValue();
2301 Update day/night difference cache of the MapBlocks
2303 block->updateDayNightDiff();
2305 Set block as modified
2307 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2311 Set central block as generated
2313 block->setGenerated(true);
2316 Save changed parts of map
2317 NOTE: Will be saved later.
2321 /*infostream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2322 <<blockpos.Z<<")"<<std::endl;*/
2324 if(enable_mapgen_debug_info)
2327 Analyze resulting blocks
2329 for(s16 x=-1; x<=1; x++)
2330 for(s16 y=-1; y<=1; y++)
2331 for(s16 z=-1; z<=1; z++)
2333 v3s16 p = block->getPos()+v3s16(x,y,z);
2334 MapBlock *block = getBlockNoCreateNoEx(p);
2336 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2337 infostream<<"Generated "<<spos<<": "
2338 <<analyze_block(block)<<std::endl;
2346 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2348 DSTACKF("%s: p2d=(%d,%d)",
2353 Check if it exists already in memory
2355 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2360 Try to load it from disk (with blocks)
2362 //if(loadSectorFull(p2d) == true)
2365 Try to load metadata from disk
2368 if(loadSectorMeta(p2d) == true)
2370 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2373 infostream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2374 throw InvalidPositionException("");
2380 Do not create over-limit
2382 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2383 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2384 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2385 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2386 throw InvalidPositionException("createSector(): pos. over limit");
2389 Generate blank sector
2392 sector = new ServerMapSector(this, p2d);
2394 // Sector position on map in nodes
2395 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2400 m_sectors.insert(p2d, sector);
2406 This is a quick-hand function for calling makeBlock().
2408 MapBlock * ServerMap::generateBlock(
2410 core::map<v3s16, MapBlock*> &modified_blocks
2413 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2415 /*infostream<<"generateBlock(): "
2416 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2419 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
2421 TimeTaker timer("generateBlock");
2423 //MapBlock *block = original_dummy;
2425 v2s16 p2d(p.X, p.Z);
2426 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2429 Do not generate over-limit
2431 if(blockpos_over_limit(p))
2433 infostream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2434 throw InvalidPositionException("generateBlock(): pos. over limit");
2438 Create block make data
2440 mapgen::BlockMakeData data;
2441 initBlockMake(&data, p);
2447 TimeTaker t("mapgen::make_block()");
2448 mapgen::make_block(&data);
2450 if(enable_mapgen_debug_info == false)
2451 t.stop(true); // Hide output
2455 Blit data back on map, update lighting, add mobs and whatever this does
2457 finishBlockMake(&data, modified_blocks);
2462 MapBlock *block = getBlockNoCreateNoEx(p);
2470 bool erroneus_content = false;
2471 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2472 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2473 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2476 MapNode n = block->getNode(p);
2477 if(n.getContent() == CONTENT_IGNORE)
2479 infostream<<"CONTENT_IGNORE at "
2480 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2482 erroneus_content = true;
2486 if(erroneus_content)
2495 Generate a completely empty block
2499 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2500 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2502 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2506 n.setContent(CONTENT_AIR);
2508 n.setContent(CONTENT_STONE);
2509 block->setNode(v3s16(x0,y0,z0), n);
2515 if(enable_mapgen_debug_info == false)
2516 timer.stop(true); // Hide output
2521 MapBlock * ServerMap::createBlock(v3s16 p)
2523 DSTACKF("%s: p=(%d,%d,%d)",
2524 __FUNCTION_NAME, p.X, p.Y, p.Z);
2527 Do not create over-limit
2529 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2530 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2531 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2532 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2533 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2534 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2535 throw InvalidPositionException("createBlock(): pos. over limit");
2537 v2s16 p2d(p.X, p.Z);
2540 This will create or load a sector if not found in memory.
2541 If block exists on disk, it will be loaded.
2543 NOTE: On old save formats, this will be slow, as it generates
2544 lighting on blocks for them.
2546 ServerMapSector *sector;
2548 sector = (ServerMapSector*)createSector(p2d);
2549 assert(sector->getId() == MAPSECTOR_SERVER);
2551 catch(InvalidPositionException &e)
2553 infostream<<"createBlock: createSector() failed"<<std::endl;
2557 NOTE: This should not be done, or at least the exception
2558 should not be passed on as std::exception, because it
2559 won't be catched at all.
2561 /*catch(std::exception &e)
2563 infostream<<"createBlock: createSector() failed: "
2564 <<e.what()<<std::endl;
2569 Try to get a block from the sector
2572 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2575 if(block->isDummy())
2580 block = sector->createBlankBlock(block_y);
2584 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2586 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2588 p.X, p.Y, p.Z, allow_generate);
2591 MapBlock *block = getBlockNoCreateNoEx(p);
2592 if(block && block->isDummy() == false)
2597 MapBlock *block = loadBlock(p);
2604 core::map<v3s16, MapBlock*> modified_blocks;
2605 MapBlock *block = generateBlock(p, modified_blocks);
2609 event.type = MEET_OTHER;
2612 // Copy modified_blocks to event
2613 for(core::map<v3s16, MapBlock*>::Iterator
2614 i = modified_blocks.getIterator();
2615 i.atEnd()==false; i++)
2617 event.modified_blocks.insert(i.getNode()->getKey(), false);
2621 dispatchEvent(&event);
2630 s16 ServerMap::findGroundLevel(v2s16 p2d)
2634 Uh, just do something random...
2636 // Find existing map from top to down
2639 v3s16 p(p2d.X, max, p2d.Y);
2640 for(; p.Y>min; p.Y--)
2642 MapNode n = getNodeNoEx(p);
2643 if(n.getContent() != CONTENT_IGNORE)
2648 // If this node is not air, go to plan b
2649 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2651 // Search existing walkable and return it
2652 for(; p.Y>min; p.Y--)
2654 MapNode n = getNodeNoEx(p);
2655 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2664 Determine from map generator noise functions
2667 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2670 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2671 //return (s16)level;
2674 void ServerMap::createDatabase() {
2677 e = sqlite3_exec(m_database,
2678 "CREATE TABLE IF NOT EXISTS `blocks` ("
2679 "`pos` INT NOT NULL PRIMARY KEY,"
2682 , NULL, NULL, NULL);
2683 if(e == SQLITE_ABORT)
2684 throw FileNotGoodException("Could not create database structure");
2686 infostream<<"Server: Database structure was created";
2689 void ServerMap::verifyDatabase() {
2694 std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
2695 bool needs_create = false;
2699 Open the database connection
2702 createDirs(m_savedir);
2704 if(!fs::PathExists(dbp))
2705 needs_create = true;
2707 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2708 if(d != SQLITE_OK) {
2709 infostream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2710 throw FileNotGoodException("Cannot open database file");
2716 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2717 if(d != SQLITE_OK) {
2718 infostream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2719 throw FileNotGoodException("Cannot prepare read statement");
2722 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2723 if(d != SQLITE_OK) {
2724 infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2725 throw FileNotGoodException("Cannot prepare write statement");
2728 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
2729 if(d != SQLITE_OK) {
2730 infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2731 throw FileNotGoodException("Cannot prepare read statement");
2734 infostream<<"Server: Database opened"<<std::endl;
2738 bool ServerMap::loadFromFolders() {
2739 if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
2744 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2745 return (sqlite3_int64)pos.Z*16777216 +
2746 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2749 void ServerMap::createDirs(std::string path)
2751 if(fs::CreateAllDirs(path) == false)
2753 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2754 <<"\""<<path<<"\""<<std::endl;
2755 throw BaseException("ServerMap failed to create directory");
2759 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2765 snprintf(cc, 9, "%.4x%.4x",
2766 (unsigned int)pos.X&0xffff,
2767 (unsigned int)pos.Y&0xffff);
2769 return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
2771 snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
2772 (unsigned int)pos.X&0xfff,
2773 (unsigned int)pos.Y&0xfff);
2775 return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
2781 v2s16 ServerMap::getSectorPos(std::string dirname)
2785 size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
2786 assert(spos != std::string::npos);
2787 if(dirname.size() - spos == 8)
2790 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2792 else if(dirname.size() - spos == 3)
2795 r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
2796 // Sign-extend the 12 bit values up to 16 bits...
2797 if(x&0x800) x|=0xF000;
2798 if(y&0x800) y|=0xF000;
2805 v2s16 pos((s16)x, (s16)y);
2809 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2811 v2s16 p2d = getSectorPos(sectordir);
2813 if(blockfile.size() != 4){
2814 throw InvalidFilenameException("Invalid block filename");
2817 int r = sscanf(blockfile.c_str(), "%4x", &y);
2819 throw InvalidFilenameException("Invalid block filename");
2820 return v3s16(p2d.X, y, p2d.Y);
2823 std::string ServerMap::getBlockFilename(v3s16 p)
2826 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2830 void ServerMap::save(bool only_changed)
2832 DSTACK(__FUNCTION_NAME);
2833 if(m_map_saving_enabled == false)
2835 infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
2839 if(only_changed == false)
2840 infostream<<"ServerMap: Saving whole map, this can take time."
2843 if(only_changed == false || m_map_metadata_changed)
2848 u32 sector_meta_count = 0;
2849 u32 block_count = 0;
2850 u32 block_count_all = 0; // Number of blocks in memory
2853 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2854 for(; i.atEnd() == false; i++)
2856 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2857 assert(sector->getId() == MAPSECTOR_SERVER);
2859 if(sector->differs_from_disk || only_changed == false)
2861 saveSectorMeta(sector);
2862 sector_meta_count++;
2864 core::list<MapBlock*> blocks;
2865 sector->getBlocks(blocks);
2866 core::list<MapBlock*>::Iterator j;
2868 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2869 for(j=blocks.begin(); j!=blocks.end(); j++)
2871 MapBlock *block = *j;
2875 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2876 || only_changed == false)
2881 /*infostream<<"ServerMap: Written block ("
2882 <<block->getPos().X<<","
2883 <<block->getPos().Y<<","
2884 <<block->getPos().Z<<")"
2887 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
2893 Only print if something happened or saved whole map
2895 if(only_changed == false || sector_meta_count != 0
2896 || block_count != 0)
2898 infostream<<"ServerMap: Written: "
2899 <<sector_meta_count<<" sector metadata files, "
2900 <<block_count<<" block files"
2901 <<", "<<block_count_all<<" blocks in memory."
2906 static s32 unsignedToSigned(s32 i, s32 max_positive)
2908 if(i < max_positive)
2911 return i - 2*max_positive;
2914 // modulo of a negative number does not work consistently in C
2915 static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
2919 return mod - ((-i) % mod);
2922 v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
2924 s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2926 s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2928 s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
2929 return v3s16(x,y,z);
2932 void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
2934 if(loadFromFolders()){
2935 errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
2936 <<"all blocks that are stored in flat files"<<std::endl;
2942 while(sqlite3_step(m_database_list) == SQLITE_ROW)
2944 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
2945 v3s16 p = getIntegerAsBlock(block_i);
2946 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
2952 void ServerMap::saveMapMeta()
2954 DSTACK(__FUNCTION_NAME);
2956 infostream<<"ServerMap::saveMapMeta(): "
2960 createDirs(m_savedir);
2962 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2963 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
2964 if(os.good() == false)
2966 infostream<<"ERROR: ServerMap::saveMapMeta(): "
2967 <<"could not open"<<fullpath<<std::endl;
2968 throw FileNotGoodException("Cannot open chunk metadata");
2972 params.setU64("seed", m_seed);
2974 params.writeLines(os);
2976 os<<"[end_of_params]\n";
2978 m_map_metadata_changed = false;
2981 void ServerMap::loadMapMeta()
2983 DSTACK(__FUNCTION_NAME);
2985 infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
2988 std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
2989 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2990 if(is.good() == false)
2992 infostream<<"ERROR: ServerMap::loadMapMeta(): "
2993 <<"could not open"<<fullpath<<std::endl;
2994 throw FileNotGoodException("Cannot open map metadata");
3002 throw SerializationError
3003 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3005 std::getline(is, line);
3006 std::string trimmedline = trim(line);
3007 if(trimmedline == "[end_of_params]")
3009 params.parseConfigLine(line);
3012 m_seed = params.getU64("seed");
3014 infostream<<"ServerMap::loadMapMeta(): "<<"seed="<<m_seed<<std::endl;
3017 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3019 DSTACK(__FUNCTION_NAME);
3020 // Format used for writing
3021 u8 version = SER_FMT_VER_HIGHEST;
3023 v2s16 pos = sector->getPos();
3024 std::string dir = getSectorDir(pos);
3027 std::string fullpath = dir + DIR_DELIM + "meta";
3028 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3029 if(o.good() == false)
3030 throw FileNotGoodException("Cannot open sector metafile");
3032 sector->serialize(o, version);
3034 sector->differs_from_disk = false;
3037 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3039 DSTACK(__FUNCTION_NAME);
3041 v2s16 p2d = getSectorPos(sectordir);
3043 ServerMapSector *sector = NULL;
3045 std::string fullpath = sectordir + DIR_DELIM + "meta";
3046 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3047 if(is.good() == false)
3049 // If the directory exists anyway, it probably is in some old
3050 // format. Just go ahead and create the sector.
3051 if(fs::PathExists(sectordir))
3053 /*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
3054 <<fullpath<<" doesn't exist but directory does."
3055 <<" Continuing with a sector with no metadata."
3057 sector = new ServerMapSector(this, p2d);
3058 m_sectors.insert(p2d, sector);
3062 throw FileNotGoodException("Cannot open sector metafile");
3067 sector = ServerMapSector::deSerialize
3068 (is, this, p2d, m_sectors);
3070 saveSectorMeta(sector);
3073 sector->differs_from_disk = false;
3078 bool ServerMap::loadSectorMeta(v2s16 p2d)
3080 DSTACK(__FUNCTION_NAME);
3082 MapSector *sector = NULL;
3084 // The directory layout we're going to load from.
3085 // 1 - original sectors/xxxxzzzz/
3086 // 2 - new sectors2/xxx/zzz/
3087 // If we load from anything but the latest structure, we will
3088 // immediately save to the new one, and remove the old.
3090 std::string sectordir1 = getSectorDir(p2d, 1);
3091 std::string sectordir;
3092 if(fs::PathExists(sectordir1))
3094 sectordir = sectordir1;
3099 sectordir = getSectorDir(p2d, 2);
3103 sector = loadSectorMeta(sectordir, loadlayout != 2);
3105 catch(InvalidFilenameException &e)
3109 catch(FileNotGoodException &e)
3113 catch(std::exception &e)
3122 bool ServerMap::loadSectorFull(v2s16 p2d)
3124 DSTACK(__FUNCTION_NAME);
3126 MapSector *sector = NULL;
3128 // The directory layout we're going to load from.
3129 // 1 - original sectors/xxxxzzzz/
3130 // 2 - new sectors2/xxx/zzz/
3131 // If we load from anything but the latest structure, we will
3132 // immediately save to the new one, and remove the old.
3134 std::string sectordir1 = getSectorDir(p2d, 1);
3135 std::string sectordir;
3136 if(fs::PathExists(sectordir1))
3138 sectordir = sectordir1;
3143 sectordir = getSectorDir(p2d, 2);
3147 sector = loadSectorMeta(sectordir, loadlayout != 2);
3149 catch(InvalidFilenameException &e)
3153 catch(FileNotGoodException &e)
3157 catch(std::exception &e)
3165 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3167 std::vector<fs::DirListNode>::iterator i2;
3168 for(i2=list2.begin(); i2!=list2.end(); i2++)
3174 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3176 catch(InvalidFilenameException &e)
3178 // This catches unknown crap in directory
3184 infostream<<"Sector converted to new layout - deleting "<<
3185 sectordir1<<std::endl;
3186 fs::RecursiveDelete(sectordir1);
3193 void ServerMap::beginSave() {
3195 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3196 infostream<<"WARNING: beginSave() failed, saving might be slow.";
3199 void ServerMap::endSave() {
3201 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3202 infostream<<"WARNING: endSave() failed, map might not have saved.";
3205 void ServerMap::saveBlock(MapBlock *block)
3207 DSTACK(__FUNCTION_NAME);
3209 Dummy blocks are not written
3211 if(block->isDummy())
3213 /*v3s16 p = block->getPos();
3214 infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3215 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3219 // Format used for writing
3220 u8 version = SER_FMT_VER_HIGHEST;
3222 v3s16 p3d = block->getPos();
3226 v2s16 p2d(p3d.X, p3d.Z);
3227 std::string sectordir = getSectorDir(p2d);
3229 createDirs(sectordir);
3231 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
3232 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3233 if(o.good() == false)
3234 throw FileNotGoodException("Cannot open block data");
3237 [0] u8 serialization version
3243 std::ostringstream o(std::ios_base::binary);
3245 o.write((char*)&version, 1);
3248 block->serialize(o, version);
3250 // Write extra data stored on disk
3251 block->serializeDiskExtra(o, version);
3253 // Write block to database
3255 std::string tmp = o.str();
3256 const char *bytes = tmp.c_str();
3258 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3259 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3260 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3261 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3262 int written = sqlite3_step(m_database_write);
3263 if(written != SQLITE_DONE)
3264 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3265 <<sqlite3_errmsg(m_database)<<std::endl;
3266 // Make ready for later reuse
3267 sqlite3_reset(m_database_write);
3269 // We just wrote it to the disk so clear modified flag
3270 block->resetModified();
3273 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3275 DSTACK(__FUNCTION_NAME);
3277 std::string fullpath = sectordir+DIR_DELIM+blockfile;
3280 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3281 if(is.good() == false)
3282 throw FileNotGoodException("Cannot open block file");
3284 v3s16 p3d = getBlockPos(sectordir, blockfile);
3285 v2s16 p2d(p3d.X, p3d.Z);
3287 assert(sector->getPos() == p2d);
3289 u8 version = SER_FMT_VER_INVALID;
3290 is.read((char*)&version, 1);
3293 throw SerializationError("ServerMap::loadBlock(): Failed"
3294 " to read MapBlock version");
3296 /*u32 block_size = MapBlock::serializedLength(version);
3297 SharedBuffer<u8> data(block_size);
3298 is.read((char*)*data, block_size);*/
3300 // This will always return a sector because we're the server
3301 //MapSector *sector = emergeSector(p2d);
3303 MapBlock *block = NULL;
3304 bool created_new = false;
3305 block = sector->getBlockNoCreateNoEx(p3d.Y);
3308 block = sector->createBlankBlockNoInsert(p3d.Y);
3313 block->deSerialize(is, version, m_gamedef);
3315 // Read extra data stored on disk
3316 block->deSerializeDiskExtra(is, version);
3318 // If it's a new block, insert it to the map
3320 sector->insertBlock(block);
3323 Save blocks loaded in old format in new format
3326 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3330 // Should be in database now, so delete the old file
3331 fs::RecursiveDelete(fullpath);
3334 // We just loaded it from the disk, so it's up-to-date.
3335 block->resetModified();
3338 catch(SerializationError &e)
3340 infostream<<"WARNING: Invalid block data on disk "
3341 <<"fullpath="<<fullpath
3342 <<" (SerializationError). "
3343 <<"what()="<<e.what()
3345 //" Ignoring. A new one will be generated.
3348 // TODO: Backup file; name is in fullpath.
3352 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3354 DSTACK(__FUNCTION_NAME);
3357 std::istringstream is(*blob, std::ios_base::binary);
3359 u8 version = SER_FMT_VER_INVALID;
3360 is.read((char*)&version, 1);
3363 throw SerializationError("ServerMap::loadBlock(): Failed"
3364 " to read MapBlock version");
3366 /*u32 block_size = MapBlock::serializedLength(version);
3367 SharedBuffer<u8> data(block_size);
3368 is.read((char*)*data, block_size);*/
3370 // This will always return a sector because we're the server
3371 //MapSector *sector = emergeSector(p2d);
3373 MapBlock *block = NULL;
3374 bool created_new = false;
3375 block = sector->getBlockNoCreateNoEx(p3d.Y);
3378 block = sector->createBlankBlockNoInsert(p3d.Y);
3383 block->deSerialize(is, version, m_gamedef);
3385 // Read extra data stored on disk
3386 block->deSerializeDiskExtra(is, version);
3388 // If it's a new block, insert it to the map
3390 sector->insertBlock(block);
3393 Save blocks loaded in old format in new format
3396 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3401 // We just loaded it from, so it's up-to-date.
3402 block->resetModified();
3405 catch(SerializationError &e)
3407 infostream<<"WARNING: Invalid block data in database "
3408 <<" (SerializationError). "
3409 <<"what()="<<e.what()
3411 //" Ignoring. A new one will be generated.
3414 // TODO: Copy to a backup database.
3418 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3420 DSTACK(__FUNCTION_NAME);
3422 v2s16 p2d(blockpos.X, blockpos.Z);
3424 if(!loadFromFolders()) {
3427 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3428 infostream<<"WARNING: Could not bind block position for load: "
3429 <<sqlite3_errmsg(m_database)<<std::endl;
3430 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3432 Make sure sector is loaded
3434 MapSector *sector = createSector(p2d);
3439 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3440 size_t len = sqlite3_column_bytes(m_database_read, 0);
3442 std::string datastr(data, len);
3444 loadBlock(&datastr, blockpos, sector, false);
3446 sqlite3_step(m_database_read);
3447 // We should never get more than 1 row, so ok to reset
3448 sqlite3_reset(m_database_read);
3450 return getBlockNoCreateNoEx(blockpos);
3452 sqlite3_reset(m_database_read);
3454 // Not found in database, try the files
3457 // The directory layout we're going to load from.
3458 // 1 - original sectors/xxxxzzzz/
3459 // 2 - new sectors2/xxx/zzz/
3460 // If we load from anything but the latest structure, we will
3461 // immediately save to the new one, and remove the old.
3463 std::string sectordir1 = getSectorDir(p2d, 1);
3464 std::string sectordir;
3465 if(fs::PathExists(sectordir1))
3467 sectordir = sectordir1;
3472 sectordir = getSectorDir(p2d, 2);
3476 Make sure sector is loaded
3478 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3482 sector = loadSectorMeta(sectordir, loadlayout != 2);
3484 catch(InvalidFilenameException &e)
3488 catch(FileNotGoodException &e)
3492 catch(std::exception &e)
3499 Make sure file exists
3502 std::string blockfilename = getBlockFilename(blockpos);
3503 if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
3507 Load block and save it to the database
3509 loadBlock(sectordir, blockfilename, sector, true);
3510 return getBlockNoCreateNoEx(blockpos);
3513 void ServerMap::PrintInfo(std::ostream &out)
3524 ClientMap::ClientMap(
3527 MapDrawControl &control,
3528 scene::ISceneNode* parent,
3529 scene::ISceneManager* mgr,
3532 Map(dout_client, gamedef),
3533 scene::ISceneNode(parent, mgr, id),
3536 m_camera_position(0,0,0),
3537 m_camera_direction(0,0,1),
3540 m_camera_mutex.Init();
3541 assert(m_camera_mutex.IsInitialized());
3543 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3544 BS*1000000,BS*1000000,BS*1000000);
3547 ClientMap::~ClientMap()
3549 /*JMutexAutoLock lock(mesh_mutex);
3558 MapSector * ClientMap::emergeSector(v2s16 p2d)
3560 DSTACK(__FUNCTION_NAME);
3561 // Check that it doesn't exist already
3563 return getSectorNoGenerate(p2d);
3565 catch(InvalidPositionException &e)
3570 ClientMapSector *sector = new ClientMapSector(this, p2d);
3573 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3574 m_sectors.insert(p2d, sector);
3581 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3583 DSTACK(__FUNCTION_NAME);
3584 ClientMapSector *sector = NULL;
3586 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3588 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3592 sector = (ClientMapSector*)n->getValue();
3593 assert(sector->getId() == MAPSECTOR_CLIENT);
3597 sector = new ClientMapSector(this, p2d);
3599 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3600 m_sectors.insert(p2d, sector);
3604 sector->deSerialize(is);
3608 void ClientMap::OnRegisterSceneNode()
3612 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3613 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3616 ISceneNode::OnRegisterSceneNode();
3619 static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
3620 float start_off, float end_off, u32 needed_count)
3622 float d0 = (float)BS * p0.getDistanceFrom(p1);
3624 v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
3626 v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
3628 for(float s=start_off; s<d0+end_off; s+=step){
3629 v3f pf = p0f + uf * s;
3630 v3s16 p = floatToInt(pf, BS);
3631 MapNode n = map->getNodeNoEx(p);
3632 bool is_transparent = false;
3633 ContentFeatures &f = content_features(n);
3634 if(f.solidness == 0)
3635 is_transparent = (f.visual_solidness != 2);
3637 is_transparent = (f.solidness != 2);
3638 if(!is_transparent){
3640 if(count >= needed_count)
3648 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3650 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3651 DSTACK(__FUNCTION_NAME);
3653 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3656 if(pass == scene::ESNRP_SOLID)
3657 prefix = "CM: solid: ";
3659 prefix = "CM: transparent: ";
3662 This is called two times per frame, reset on the non-transparent one
3664 if(pass == scene::ESNRP_SOLID)
3666 m_last_drawn_sectors.clear();
3670 Get time for measuring timeout.
3672 Measuring time is very useful for long delays when the
3673 machine is swapping a lot.
3675 int time1 = time(0);
3677 //u32 daynight_ratio = m_client->getDayNightRatio();
3679 m_camera_mutex.Lock();
3680 v3f camera_position = m_camera_position;
3681 v3f camera_direction = m_camera_direction;
3682 f32 camera_fov = m_camera_fov;
3683 m_camera_mutex.Unlock();
3686 Get all blocks and draw all visible ones
3689 v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
3691 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3693 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3694 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3696 // Take a fair amount as we will be dropping more out later
3697 // Umm... these additions are a bit strange but they are needed.
3699 p_nodes_min.X / MAP_BLOCKSIZE - 3,
3700 p_nodes_min.Y / MAP_BLOCKSIZE - 3,
3701 p_nodes_min.Z / MAP_BLOCKSIZE - 3);
3703 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3704 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3705 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3707 u32 vertex_count = 0;
3708 u32 meshbuffer_count = 0;
3710 // For limiting number of mesh updates per frame
3711 u32 mesh_update_count = 0;
3713 // Number of blocks in rendering range
3714 u32 blocks_in_range = 0;
3715 // Number of blocks occlusion culled
3716 u32 blocks_occlusion_culled = 0;
3717 // Number of blocks in rendering range but don't have a mesh
3718 u32 blocks_in_range_without_mesh = 0;
3719 // Blocks that had mesh that would have been drawn according to
3720 // rendering range (if max blocks limit didn't kick in)
3721 u32 blocks_would_have_drawn = 0;
3722 // Blocks that were drawn and had a mesh
3723 u32 blocks_drawn = 0;
3724 // Blocks which had a corresponding meshbuffer for this pass
3725 u32 blocks_had_pass_meshbuf = 0;
3726 // Blocks from which stuff was actually drawn
3727 u32 blocks_without_stuff = 0;
3730 Collect a set of blocks for drawing
3733 core::map<v3s16, MapBlock*> drawset;
3736 ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
3738 for(core::map<v2s16, MapSector*>::Iterator
3739 si = m_sectors.getIterator();
3740 si.atEnd() == false; si++)
3742 MapSector *sector = si.getNode()->getValue();
3743 v2s16 sp = sector->getPos();
3745 if(m_control.range_all == false)
3747 if(sp.X < p_blocks_min.X
3748 || sp.X > p_blocks_max.X
3749 || sp.Y < p_blocks_min.Z
3750 || sp.Y > p_blocks_max.Z)
3754 core::list< MapBlock * > sectorblocks;
3755 sector->getBlocks(sectorblocks);
3758 Loop through blocks in sector
3761 u32 sector_blocks_drawn = 0;
3763 core::list< MapBlock * >::Iterator i;
3764 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3766 MapBlock *block = *i;
3769 Compare block position to camera position, skip
3770 if not seen on display
3773 float range = 100000 * BS;
3774 if(m_control.range_all == false)
3775 range = m_control.wanted_range * BS;
3778 if(isBlockInSight(block->getPos(), camera_position,
3779 camera_direction, camera_fov,
3780 range, &d) == false)
3785 // This is ugly (spherical distance limit?)
3786 /*if(m_control.range_all == false &&
3787 d - 0.5*BS*MAP_BLOCKSIZE > range)
3794 Update expired mesh (used for day/night change)
3796 It doesn't work exactly like it should now with the
3797 tasked mesh update but whatever.
3800 bool mesh_expired = false;
3803 JMutexAutoLock lock(block->mesh_mutex);
3805 mesh_expired = block->getMeshExpired();
3807 // Mesh has not been expired and there is no mesh:
3808 // block has no content
3809 if(block->mesh == NULL && mesh_expired == false){
3810 blocks_in_range_without_mesh++;
3815 f32 faraway = BS*50;
3816 //f32 faraway = m_control.wanted_range * BS;
3819 This has to be done with the mesh_mutex unlocked
3821 // Pretty random but this should work somewhat nicely
3822 if(mesh_expired && (
3823 (mesh_update_count < 3
3824 && (d < faraway || mesh_update_count < 2)
3827 (m_control.range_all && mesh_update_count < 20)
3830 /*if(mesh_expired && mesh_update_count < 6
3831 && (d < faraway || mesh_update_count < 3))*/
3833 mesh_update_count++;
3835 // Mesh has been expired: generate new mesh
3836 //block->updateMesh(daynight_ratio);
3837 m_client->addUpdateMeshTask(block->getPos());
3839 mesh_expired = false;
3847 v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
3848 cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
3850 float stepfac = 1.1;
3851 float startoff = BS*1;
3852 float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
3853 v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
3854 s16 bs2 = MAP_BLOCKSIZE/2 + 1;
3855 u32 needed_count = 1;
3857 isOccluded(this, spn, cpn + v3s16(0,0,0),
3858 step, stepfac, startoff, endoff, needed_count) &&
3859 isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
3860 step, stepfac, startoff, endoff, needed_count) &&
3861 isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
3862 step, stepfac, startoff, endoff, needed_count) &&
3863 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
3864 step, stepfac, startoff, endoff, needed_count) &&
3865 isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
3866 step, stepfac, startoff, endoff, needed_count) &&
3867 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
3868 step, stepfac, startoff, endoff, needed_count) &&
3869 isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
3870 step, stepfac, startoff, endoff, needed_count) &&
3871 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
3872 step, stepfac, startoff, endoff, needed_count) &&
3873 isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
3874 step, stepfac, startoff, endoff, needed_count)
3877 blocks_occlusion_culled++;
3881 // This block is in range. Reset usage timer.
3882 block->resetUsageTimer();
3885 Ignore if mesh doesn't exist
3888 JMutexAutoLock lock(block->mesh_mutex);
3890 scene::SMesh *mesh = block->mesh;
3893 blocks_in_range_without_mesh++;
3898 // Limit block count in case of a sudden increase
3899 blocks_would_have_drawn++;
3900 if(blocks_drawn >= m_control.wanted_max_blocks
3901 && m_control.range_all == false
3902 && d > m_control.wanted_min_range * BS)
3906 drawset[block->getPos()] = block;
3908 sector_blocks_drawn++;
3911 } // foreach sectorblocks
3913 if(sector_blocks_drawn != 0)
3914 m_last_drawn_sectors[sp] = true;
3919 Draw the selected MapBlocks
3923 ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
3925 int timecheck_counter = 0;
3926 for(core::map<v3s16, MapBlock*>::Iterator
3927 i = drawset.getIterator();
3928 i.atEnd() == false; i++)
3931 timecheck_counter++;
3932 if(timecheck_counter > 50)
3934 timecheck_counter = 0;
3935 int time2 = time(0);
3936 if(time2 > time1 + 4)
3938 infostream<<"ClientMap::renderMap(): "
3939 "Rendering takes ages, returning."
3946 MapBlock *block = i.getNode()->getValue();
3949 Draw the faces of the block
3952 JMutexAutoLock lock(block->mesh_mutex);
3954 scene::SMesh *mesh = block->mesh;
3957 u32 c = mesh->getMeshBufferCount();
3958 bool stuff_actually_drawn = false;
3959 for(u32 i=0; i<c; i++)
3961 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3962 const video::SMaterial& material = buf->getMaterial();
3963 video::IMaterialRenderer* rnd =
3964 driver->getMaterialRenderer(material.MaterialType);
3965 bool transparent = (rnd && rnd->isTransparent());
3966 // Render transparent on transparent pass and likewise.
3967 if(transparent == is_transparent_pass)
3969 if(buf->getVertexCount() == 0)
3970 errorstream<<"Block ["<<analyze_block(block)
3971 <<"] contains an empty meshbuf"<<std::endl;
3973 This *shouldn't* hurt too much because Irrlicht
3974 doesn't change opengl textures if the old
3975 material has the same texture.
3977 driver->setMaterial(buf->getMaterial());
3978 driver->drawMeshBuffer(buf);
3979 vertex_count += buf->getVertexCount();
3981 stuff_actually_drawn = true;
3984 if(stuff_actually_drawn)
3985 blocks_had_pass_meshbuf++;
3987 blocks_without_stuff++;
3992 // Log only on solid pass because values are the same
3993 if(pass == scene::ESNRP_SOLID){
3994 g_profiler->avg("CM: blocks in range", blocks_in_range);
3995 g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
3996 if(blocks_in_range != 0)
3997 g_profiler->avg("CM: blocks in range without mesh (frac)",
3998 (float)blocks_in_range_without_mesh/blocks_in_range);
3999 g_profiler->avg("CM: blocks drawn", blocks_drawn);
4002 g_profiler->avg(prefix+"vertices drawn", vertex_count);
4003 if(blocks_had_pass_meshbuf != 0)
4004 g_profiler->avg(prefix+"meshbuffers per block",
4005 (float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
4006 if(blocks_drawn != 0)
4007 g_profiler->avg(prefix+"empty blocks (frac)",
4008 (float)blocks_without_stuff / blocks_drawn);
4010 m_control.blocks_drawn = blocks_drawn;
4011 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
4013 /*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
4014 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
4017 void ClientMap::renderPostFx()
4019 // Sadly ISceneManager has no "post effects" render pass, in that case we
4020 // could just register for that and handle it in renderMap().
4022 m_camera_mutex.Lock();
4023 v3f camera_position = m_camera_position;
4024 m_camera_mutex.Unlock();
4026 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
4028 // - If the player is in a solid node, make everything black.
4029 // - If the player is in liquid, draw a semi-transparent overlay.
4030 ContentFeatures& features = content_features(n);
4031 video::SColor post_effect_color = features.post_effect_color;
4032 if(features.solidness == 2 && g_settings->getBool("free_move") == false)
4034 post_effect_color = video::SColor(255, 0, 0, 0);
4036 if (post_effect_color.getAlpha() != 0)
4038 // Draw a full-screen rectangle
4039 video::IVideoDriver* driver = SceneManager->getVideoDriver();
4040 v2u32 ss = driver->getScreenSize();
4041 core::rect<s32> rect(0,0, ss.X, ss.Y);
4042 driver->draw2DRectangle(post_effect_color, rect);
4046 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
4047 core::map<v3s16, MapBlock*> *affected_blocks)
4049 bool changed = false;
4051 Add it to all blocks touching it
4054 v3s16(0,0,0), // this
4055 v3s16(0,0,1), // back
4056 v3s16(0,1,0), // top
4057 v3s16(1,0,0), // right
4058 v3s16(0,0,-1), // front
4059 v3s16(0,-1,0), // bottom
4060 v3s16(-1,0,0), // left
4062 for(u16 i=0; i<7; i++)
4064 v3s16 p2 = p + dirs[i];
4065 // Block position of neighbor (or requested) node
4066 v3s16 blockpos = getNodeBlockPos(p2);
4067 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4068 if(blockref == NULL)
4070 // Relative position of requested node
4071 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4072 if(blockref->setTempMod(relpos, mod))
4077 if(changed && affected_blocks!=NULL)
4079 for(u16 i=0; i<7; i++)
4081 v3s16 p2 = p + dirs[i];
4082 // Block position of neighbor (or requested) node
4083 v3s16 blockpos = getNodeBlockPos(p2);
4084 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4085 if(blockref == NULL)
4087 affected_blocks->insert(blockpos, blockref);
4093 bool ClientMap::clearTempMod(v3s16 p,
4094 core::map<v3s16, MapBlock*> *affected_blocks)
4096 bool changed = false;
4098 v3s16(0,0,0), // this
4099 v3s16(0,0,1), // back
4100 v3s16(0,1,0), // top
4101 v3s16(1,0,0), // right
4102 v3s16(0,0,-1), // front
4103 v3s16(0,-1,0), // bottom
4104 v3s16(-1,0,0), // left
4106 for(u16 i=0; i<7; i++)
4108 v3s16 p2 = p + dirs[i];
4109 // Block position of neighbor (or requested) node
4110 v3s16 blockpos = getNodeBlockPos(p2);
4111 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4112 if(blockref == NULL)
4114 // Relative position of requested node
4115 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4116 if(blockref->clearTempMod(relpos))
4121 if(changed && affected_blocks!=NULL)
4123 for(u16 i=0; i<7; i++)
4125 v3s16 p2 = p + dirs[i];
4126 // Block position of neighbor (or requested) node
4127 v3s16 blockpos = getNodeBlockPos(p2);
4128 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4129 if(blockref == NULL)
4131 affected_blocks->insert(blockpos, blockref);
4137 void ClientMap::expireMeshes(bool only_daynight_diffed)
4139 TimeTaker timer("expireMeshes()");
4141 core::map<v2s16, MapSector*>::Iterator si;
4142 si = m_sectors.getIterator();
4143 for(; si.atEnd() == false; si++)
4145 MapSector *sector = si.getNode()->getValue();
4147 core::list< MapBlock * > sectorblocks;
4148 sector->getBlocks(sectorblocks);
4150 core::list< MapBlock * >::Iterator i;
4151 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4153 MapBlock *block = *i;
4155 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4161 JMutexAutoLock lock(block->mesh_mutex);
4162 if(block->mesh != NULL)
4164 /*block->mesh->drop();
4165 block->mesh = NULL;*/
4166 block->setMeshExpired(true);
4173 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio,
4174 ITextureSource *tsrc)
4176 assert(mapType() == MAPTYPE_CLIENT);
4179 v3s16 p = blockpos + v3s16(0,0,0);
4180 MapBlock *b = getBlockNoCreate(p);
4181 b->updateMesh(daynight_ratio, tsrc);
4182 //b->setMeshExpired(true);
4184 catch(InvalidPositionException &e){}
4187 v3s16 p = blockpos + v3s16(-1,0,0);
4188 MapBlock *b = getBlockNoCreate(p);
4189 b->updateMesh(daynight_ratio, tsrc);
4190 //b->setMeshExpired(true);
4192 catch(InvalidPositionException &e){}
4194 v3s16 p = blockpos + v3s16(0,-1,0);
4195 MapBlock *b = getBlockNoCreate(p);
4196 b->updateMesh(daynight_ratio, tsrc);
4197 //b->setMeshExpired(true);
4199 catch(InvalidPositionException &e){}
4201 v3s16 p = blockpos + v3s16(0,0,-1);
4202 MapBlock *b = getBlockNoCreate(p);
4203 b->updateMesh(daynight_ratio, tsrc);
4204 //b->setMeshExpired(true);
4206 catch(InvalidPositionException &e){}
4211 Update mesh of block in which the node is, and if the node is at the
4212 leading edge, update the appropriate leading blocks too.
4214 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4222 v3s16 blockposes[4];
4223 for(u32 i=0; i<4; i++)
4225 v3s16 np = nodepos + dirs[i];
4226 blockposes[i] = getNodeBlockPos(np);
4227 // Don't update mesh of block if it has been done already
4228 bool already_updated = false;
4229 for(u32 j=0; j<i; j++)
4231 if(blockposes[j] == blockposes[i])
4233 already_updated = true;
4240 MapBlock *b = getBlockNoCreate(blockposes[i]);
4241 b->updateMesh(daynight_ratio);
4246 void ClientMap::PrintInfo(std::ostream &out)
4257 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4262 MapVoxelManipulator::~MapVoxelManipulator()
4264 /*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4268 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4270 TimeTaker timer1("emerge", &emerge_time);
4272 // Units of these are MapBlocks
4273 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4274 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4276 VoxelArea block_area_nodes
4277 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4279 addArea(block_area_nodes);
4281 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4282 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4283 for(s32 x=p_min.X; x<=p_max.X; x++)
4286 core::map<v3s16, bool>::Node *n;
4287 n = m_loaded_blocks.find(p);
4291 bool block_data_inexistent = false;
4294 TimeTaker timer1("emerge load", &emerge_load_time);
4296 /*infostream<<"Loading block (caller_id="<<caller_id<<")"
4297 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4299 a.print(infostream);
4300 infostream<<std::endl;*/
4302 MapBlock *block = m_map->getBlockNoCreate(p);
4303 if(block->isDummy())
4304 block_data_inexistent = true;
4306 block->copyTo(*this);
4308 catch(InvalidPositionException &e)
4310 block_data_inexistent = true;
4313 if(block_data_inexistent)
4315 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4316 // Fill with VOXELFLAG_INEXISTENT
4317 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4318 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4320 s32 i = m_area.index(a.MinEdge.X,y,z);
4321 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4325 m_loaded_blocks.insert(p, !block_data_inexistent);
4328 //infostream<<"emerge done"<<std::endl;
4332 SUGG: Add an option to only update eg. water and air nodes.
4333 This will make it interfere less with important stuff if
4336 void MapVoxelManipulator::blitBack
4337 (core::map<v3s16, MapBlock*> & modified_blocks)
4339 if(m_area.getExtent() == v3s16(0,0,0))
4342 //TimeTaker timer1("blitBack");
4344 /*infostream<<"blitBack(): m_loaded_blocks.size()="
4345 <<m_loaded_blocks.size()<<std::endl;*/
4348 Initialize block cache
4350 v3s16 blockpos_last;
4351 MapBlock *block = NULL;
4352 bool block_checked_in_modified = false;
4354 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4355 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4356 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4360 u8 f = m_flags[m_area.index(p)];
4361 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4364 MapNode &n = m_data[m_area.index(p)];
4366 v3s16 blockpos = getNodeBlockPos(p);
4371 if(block == NULL || blockpos != blockpos_last){
4372 block = m_map->getBlockNoCreate(blockpos);
4373 blockpos_last = blockpos;
4374 block_checked_in_modified = false;
4377 // Calculate relative position in block
4378 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4380 // Don't continue if nothing has changed here
4381 if(block->getNode(relpos) == n)
4384 //m_map->setNode(m_area.MinEdge + p, n);
4385 block->setNode(relpos, n);
4388 Make sure block is in modified_blocks
4390 if(block_checked_in_modified == false)
4392 modified_blocks[blockpos] = block;
4393 block_checked_in_modified = true;
4396 catch(InvalidPositionException &e)
4402 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4403 MapVoxelManipulator(map),
4404 m_create_area(false)
4408 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4412 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4414 // Just create the area so that it can be pointed to
4415 VoxelManipulator::emerge(a, caller_id);
4418 void ManualMapVoxelManipulator::initialEmerge(
4419 v3s16 blockpos_min, v3s16 blockpos_max)
4421 TimeTaker timer1("initialEmerge", &emerge_time);
4423 // Units of these are MapBlocks
4424 v3s16 p_min = blockpos_min;
4425 v3s16 p_max = blockpos_max;
4427 VoxelArea block_area_nodes
4428 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4430 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4433 infostream<<"initialEmerge: area: ";
4434 block_area_nodes.print(infostream);
4435 infostream<<" ("<<size_MB<<"MB)";
4436 infostream<<std::endl;
4439 addArea(block_area_nodes);
4441 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4442 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4443 for(s32 x=p_min.X; x<=p_max.X; x++)
4446 core::map<v3s16, bool>::Node *n;
4447 n = m_loaded_blocks.find(p);
4451 bool block_data_inexistent = false;
4454 TimeTaker timer1("emerge load", &emerge_load_time);
4456 MapBlock *block = m_map->getBlockNoCreate(p);
4457 if(block->isDummy())
4458 block_data_inexistent = true;
4460 block->copyTo(*this);
4462 catch(InvalidPositionException &e)
4464 block_data_inexistent = true;
4467 if(block_data_inexistent)
4470 Mark area inexistent
4472 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4473 // Fill with VOXELFLAG_INEXISTENT
4474 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4475 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4477 s32 i = m_area.index(a.MinEdge.X,y,z);
4478 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4482 m_loaded_blocks.insert(p, !block_data_inexistent);
4486 void ManualMapVoxelManipulator::blitBackAll(
4487 core::map<v3s16, MapBlock*> * modified_blocks)
4489 if(m_area.getExtent() == v3s16(0,0,0))
4493 Copy data of all blocks
4495 for(core::map<v3s16, bool>::Iterator
4496 i = m_loaded_blocks.getIterator();
4497 i.atEnd() == false; i++)
4499 v3s16 p = i.getNode()->getKey();
4500 bool existed = i.getNode()->getValue();
4501 if(existed == false)
4503 // The Great Bug was found using this
4504 /*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
4505 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4509 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4512 infostream<<"WARNING: "<<__FUNCTION_NAME
4513 <<": got NULL block "
4514 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4519 block->copyFrom(*this);
4522 modified_blocks->insert(p, block);