3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mapsector.h"
30 #include "nodemetadata.h"
31 #include "content_mapnode.h"
34 SQLite format specification:
35 - Initially only replaces sectors/ and sectors2/
37 If map.sqlite does not exist in the save dir
38 or the block was not found in the database
39 the map will try to load from sectors folder.
40 In either case, map.sqlite will be created
41 and all future saves will save there.
43 Structure of map.sqlite:
54 Map::Map(std::ostream &dout):
58 /*m_sector_mutex.Init();
59 assert(m_sector_mutex.IsInitialized());*/
67 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
68 for(; i.atEnd() == false; i++)
70 MapSector *sector = i.getNode()->getValue();
75 void Map::addEventReceiver(MapEventReceiver *event_receiver)
77 m_event_receivers.insert(event_receiver, false);
80 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
82 if(m_event_receivers.find(event_receiver) == NULL)
84 m_event_receivers.remove(event_receiver);
87 void Map::dispatchEvent(MapEditEvent *event)
89 for(core::map<MapEventReceiver*, bool>::Iterator
90 i = m_event_receivers.getIterator();
91 i.atEnd()==false; i++)
93 MapEventReceiver* event_receiver = i.getNode()->getKey();
94 event_receiver->onMapEditEvent(event);
98 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
100 if(m_sector_cache != NULL && p == m_sector_cache_p){
101 MapSector * sector = m_sector_cache;
105 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
110 MapSector *sector = n->getValue();
112 // Cache the last result
113 m_sector_cache_p = p;
114 m_sector_cache = sector;
119 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
121 return getSectorNoGenerateNoExNoLock(p);
124 MapSector * Map::getSectorNoGenerate(v2s16 p)
126 MapSector *sector = getSectorNoGenerateNoEx(p);
128 throw InvalidPositionException();
133 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
135 v2s16 p2d(p3d.X, p3d.Z);
136 MapSector * sector = getSectorNoGenerateNoEx(p2d);
139 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
143 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
145 MapBlock *block = getBlockNoCreateNoEx(p3d);
147 throw InvalidPositionException();
151 bool Map::isNodeUnderground(v3s16 p)
153 v3s16 blockpos = getNodeBlockPos(p);
155 MapBlock * block = getBlockNoCreate(blockpos);
156 return block->getIsUnderground();
158 catch(InvalidPositionException &e)
164 bool Map::isValidPosition(v3s16 p)
166 v3s16 blockpos = getNodeBlockPos(p);
167 MapBlock *block = getBlockNoCreate(blockpos);
168 return (block != NULL);
171 // Returns a CONTENT_IGNORE node if not found
172 MapNode Map::getNodeNoEx(v3s16 p)
174 v3s16 blockpos = getNodeBlockPos(p);
175 MapBlock *block = getBlockNoCreateNoEx(blockpos);
177 return MapNode(CONTENT_IGNORE);
178 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
179 return block->getNodeNoCheck(relpos);
182 // throws InvalidPositionException if not found
183 MapNode Map::getNode(v3s16 p)
185 v3s16 blockpos = getNodeBlockPos(p);
186 MapBlock *block = getBlockNoCreateNoEx(blockpos);
188 throw InvalidPositionException();
189 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
190 return block->getNodeNoCheck(relpos);
193 // throws InvalidPositionException if not found
194 void Map::setNode(v3s16 p, MapNode & n)
196 v3s16 blockpos = getNodeBlockPos(p);
197 MapBlock *block = getBlockNoCreate(blockpos);
198 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
199 block->setNodeNoCheck(relpos, n);
204 Goes recursively through the neighbours of the node.
206 Alters only transparent nodes.
208 If the lighting of the neighbour is lower than the lighting of
209 the node was (before changing it to 0 at the step before), the
210 lighting of the neighbour is set to 0 and then the same stuff
211 repeats for the neighbour.
213 The ending nodes of the routine are stored in light_sources.
214 This is useful when a light is removed. In such case, this
215 routine can be called for the light node and then again for
216 light_sources to re-light the area without the removed light.
218 values of from_nodes are lighting values.
220 void Map::unspreadLight(enum LightBank bank,
221 core::map<v3s16, u8> & from_nodes,
222 core::map<v3s16, bool> & light_sources,
223 core::map<v3s16, MapBlock*> & modified_blocks)
226 v3s16(0,0,1), // back
228 v3s16(1,0,0), // right
229 v3s16(0,0,-1), // front
230 v3s16(0,-1,0), // bottom
231 v3s16(-1,0,0), // left
234 if(from_nodes.size() == 0)
237 u32 blockchangecount = 0;
239 core::map<v3s16, u8> unlighted_nodes;
240 core::map<v3s16, u8>::Iterator j;
241 j = from_nodes.getIterator();
244 Initialize block cache
247 MapBlock *block = NULL;
248 // Cache this a bit, too
249 bool block_checked_in_modified = false;
251 for(; j.atEnd() == false; j++)
253 v3s16 pos = j.getNode()->getKey();
254 v3s16 blockpos = getNodeBlockPos(pos);
256 // Only fetch a new block if the block position has changed
258 if(block == NULL || blockpos != blockpos_last){
259 block = getBlockNoCreate(blockpos);
260 blockpos_last = blockpos;
262 block_checked_in_modified = false;
266 catch(InvalidPositionException &e)
274 // Calculate relative position in block
275 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
277 // Get node straight from the block
278 MapNode n = block->getNode(relpos);
280 u8 oldlight = j.getNode()->getValue();
282 // Loop through 6 neighbors
283 for(u16 i=0; i<6; i++)
285 // Get the position of the neighbor node
286 v3s16 n2pos = pos + dirs[i];
288 // Get the block where the node is located
289 v3s16 blockpos = getNodeBlockPos(n2pos);
293 // Only fetch a new block if the block position has changed
295 if(block == NULL || blockpos != blockpos_last){
296 block = getBlockNoCreate(blockpos);
297 blockpos_last = blockpos;
299 block_checked_in_modified = false;
303 catch(InvalidPositionException &e)
308 // Calculate relative position in block
309 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
310 // Get node straight from the block
311 MapNode n2 = block->getNode(relpos);
313 bool changed = false;
315 //TODO: Optimize output by optimizing light_sources?
318 If the neighbor is dimmer than what was specified
319 as oldlight (the light of the previous node)
321 if(n2.getLight(bank) < oldlight)
324 And the neighbor is transparent and it has some light
326 if(n2.light_propagates() && n2.getLight(bank) != 0)
329 Set light to 0 and add to queue
332 u8 current_light = n2.getLight(bank);
333 n2.setLight(bank, 0);
334 block->setNode(relpos, n2);
336 unlighted_nodes.insert(n2pos, current_light);
340 Remove from light_sources if it is there
341 NOTE: This doesn't happen nearly at all
343 /*if(light_sources.find(n2pos))
345 std::cout<<"Removed from light_sources"<<std::endl;
346 light_sources.remove(n2pos);
351 if(light_sources.find(n2pos) != NULL)
352 light_sources.remove(n2pos);*/
355 light_sources.insert(n2pos, true);
358 // Add to modified_blocks
359 if(changed == true && block_checked_in_modified == false)
361 // If the block is not found in modified_blocks, add.
362 if(modified_blocks.find(blockpos) == NULL)
364 modified_blocks.insert(blockpos, block);
366 block_checked_in_modified = true;
369 catch(InvalidPositionException &e)
376 /*dstream<<"unspreadLight(): Changed block "
377 <<blockchangecount<<" times"
378 <<" for "<<from_nodes.size()<<" nodes"
381 if(unlighted_nodes.size() > 0)
382 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
386 A single-node wrapper of the above
388 void Map::unLightNeighbors(enum LightBank bank,
389 v3s16 pos, u8 lightwas,
390 core::map<v3s16, bool> & light_sources,
391 core::map<v3s16, MapBlock*> & modified_blocks)
393 core::map<v3s16, u8> from_nodes;
394 from_nodes.insert(pos, lightwas);
396 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
400 Lights neighbors of from_nodes, collects all them and then
403 void Map::spreadLight(enum LightBank bank,
404 core::map<v3s16, bool> & from_nodes,
405 core::map<v3s16, MapBlock*> & modified_blocks)
407 const v3s16 dirs[6] = {
408 v3s16(0,0,1), // back
410 v3s16(1,0,0), // right
411 v3s16(0,0,-1), // front
412 v3s16(0,-1,0), // bottom
413 v3s16(-1,0,0), // left
416 if(from_nodes.size() == 0)
419 u32 blockchangecount = 0;
421 core::map<v3s16, bool> lighted_nodes;
422 core::map<v3s16, bool>::Iterator j;
423 j = from_nodes.getIterator();
426 Initialize block cache
429 MapBlock *block = NULL;
430 // Cache this a bit, too
431 bool block_checked_in_modified = false;
433 for(; j.atEnd() == false; j++)
434 //for(; j != from_nodes.end(); j++)
436 v3s16 pos = j.getNode()->getKey();
438 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
439 v3s16 blockpos = getNodeBlockPos(pos);
441 // Only fetch a new block if the block position has changed
443 if(block == NULL || blockpos != blockpos_last){
444 block = getBlockNoCreate(blockpos);
445 blockpos_last = blockpos;
447 block_checked_in_modified = false;
451 catch(InvalidPositionException &e)
459 // Calculate relative position in block
460 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
462 // Get node straight from the block
463 MapNode n = block->getNode(relpos);
465 u8 oldlight = n.getLight(bank);
466 u8 newlight = diminish_light(oldlight);
468 // Loop through 6 neighbors
469 for(u16 i=0; i<6; i++){
470 // Get the position of the neighbor node
471 v3s16 n2pos = pos + dirs[i];
473 // Get the block where the node is located
474 v3s16 blockpos = getNodeBlockPos(n2pos);
478 // Only fetch a new block if the block position has changed
480 if(block == NULL || blockpos != blockpos_last){
481 block = getBlockNoCreate(blockpos);
482 blockpos_last = blockpos;
484 block_checked_in_modified = false;
488 catch(InvalidPositionException &e)
493 // Calculate relative position in block
494 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
495 // Get node straight from the block
496 MapNode n2 = block->getNode(relpos);
498 bool changed = false;
500 If the neighbor is brighter than the current node,
501 add to list (it will light up this node on its turn)
503 if(n2.getLight(bank) > undiminish_light(oldlight))
505 lighted_nodes.insert(n2pos, true);
506 //lighted_nodes.push_back(n2pos);
510 If the neighbor is dimmer than how much light this node
511 would spread on it, add to list
513 if(n2.getLight(bank) < newlight)
515 if(n2.light_propagates())
517 n2.setLight(bank, newlight);
518 block->setNode(relpos, n2);
519 lighted_nodes.insert(n2pos, true);
520 //lighted_nodes.push_back(n2pos);
525 // Add to modified_blocks
526 if(changed == true && block_checked_in_modified == false)
528 // If the block is not found in modified_blocks, add.
529 if(modified_blocks.find(blockpos) == NULL)
531 modified_blocks.insert(blockpos, block);
533 block_checked_in_modified = true;
536 catch(InvalidPositionException &e)
543 /*dstream<<"spreadLight(): Changed block "
544 <<blockchangecount<<" times"
545 <<" for "<<from_nodes.size()<<" nodes"
548 if(lighted_nodes.size() > 0)
549 spreadLight(bank, lighted_nodes, modified_blocks);
553 A single-node source variation of the above.
555 void Map::lightNeighbors(enum LightBank bank,
557 core::map<v3s16, MapBlock*> & modified_blocks)
559 core::map<v3s16, bool> from_nodes;
560 from_nodes.insert(pos, true);
561 spreadLight(bank, from_nodes, modified_blocks);
564 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
567 v3s16(0,0,1), // back
569 v3s16(1,0,0), // right
570 v3s16(0,0,-1), // front
571 v3s16(0,-1,0), // bottom
572 v3s16(-1,0,0), // left
575 u8 brightest_light = 0;
576 v3s16 brightest_pos(0,0,0);
577 bool found_something = false;
579 // Loop through 6 neighbors
580 for(u16 i=0; i<6; i++){
581 // Get the position of the neighbor node
582 v3s16 n2pos = p + dirs[i];
587 catch(InvalidPositionException &e)
591 if(n2.getLight(bank) > brightest_light || found_something == false){
592 brightest_light = n2.getLight(bank);
593 brightest_pos = n2pos;
594 found_something = true;
598 if(found_something == false)
599 throw InvalidPositionException();
601 return brightest_pos;
605 Propagates sunlight down from a node.
606 Starting point gets sunlight.
608 Returns the lowest y value of where the sunlight went.
610 Mud is turned into grass in where the sunlight stops.
612 s16 Map::propagateSunlight(v3s16 start,
613 core::map<v3s16, MapBlock*> & modified_blocks)
618 v3s16 pos(start.X, y, start.Z);
620 v3s16 blockpos = getNodeBlockPos(pos);
623 block = getBlockNoCreate(blockpos);
625 catch(InvalidPositionException &e)
630 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
631 MapNode n = block->getNode(relpos);
633 if(n.sunlight_propagates())
635 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
636 block->setNode(relpos, n);
638 modified_blocks.insert(blockpos, block);
642 /*// Turn mud into grass
643 if(n.getContent() == CONTENT_MUD)
645 n.setContent(CONTENT_GRASS);
646 block->setNode(relpos, n);
647 modified_blocks.insert(blockpos, block);
650 // Sunlight goes no further
657 void Map::updateLighting(enum LightBank bank,
658 core::map<v3s16, MapBlock*> & a_blocks,
659 core::map<v3s16, MapBlock*> & modified_blocks)
661 /*m_dout<<DTIME<<"Map::updateLighting(): "
662 <<a_blocks.size()<<" blocks."<<std::endl;*/
664 //TimeTaker timer("updateLighting");
668 //u32 count_was = modified_blocks.size();
670 core::map<v3s16, MapBlock*> blocks_to_update;
672 core::map<v3s16, bool> light_sources;
674 core::map<v3s16, u8> unlight_from;
676 core::map<v3s16, MapBlock*>::Iterator i;
677 i = a_blocks.getIterator();
678 for(; i.atEnd() == false; i++)
680 MapBlock *block = i.getNode()->getValue();
684 // Don't bother with dummy blocks.
688 v3s16 pos = block->getPos();
689 modified_blocks.insert(pos, block);
691 blocks_to_update.insert(pos, block);
694 Clear all light from block
696 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
697 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
698 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
703 MapNode n = block->getNode(v3s16(x,y,z));
704 u8 oldlight = n.getLight(bank);
706 block->setNode(v3s16(x,y,z), n);
708 // Collect borders for unlighting
709 if(x==0 || x == MAP_BLOCKSIZE-1
710 || y==0 || y == MAP_BLOCKSIZE-1
711 || z==0 || z == MAP_BLOCKSIZE-1)
713 v3s16 p_map = p + v3s16(
716 MAP_BLOCKSIZE*pos.Z);
717 unlight_from.insert(p_map, oldlight);
720 catch(InvalidPositionException &e)
723 This would happen when dealing with a
727 dstream<<"updateLighting(): InvalidPositionException"
732 if(bank == LIGHTBANK_DAY)
734 bool bottom_valid = block->propagateSunlight(light_sources);
736 // If bottom is valid, we're done.
740 else if(bank == LIGHTBANK_NIGHT)
742 // For night lighting, sunlight is not propagated
747 // Invalid lighting bank
751 /*dstream<<"Bottom for sunlight-propagated block ("
752 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
755 // Bottom sunlight is not valid; get the block and loop to it
759 block = getBlockNoCreate(pos);
761 catch(InvalidPositionException &e)
770 Enable this to disable proper lighting for speeding up map
771 generation for testing or whatever
774 //if(g_settings.get(""))
776 core::map<v3s16, MapBlock*>::Iterator i;
777 i = blocks_to_update.getIterator();
778 for(; i.atEnd() == false; i++)
780 MapBlock *block = i.getNode()->getValue();
781 v3s16 p = block->getPos();
782 block->setLightingExpired(false);
790 TimeTaker timer("unspreadLight");
791 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
796 u32 diff = modified_blocks.size() - count_was;
797 count_was = modified_blocks.size();
798 dstream<<"unspreadLight modified "<<diff<<std::endl;
802 TimeTaker timer("spreadLight");
803 spreadLight(bank, light_sources, modified_blocks);
808 u32 diff = modified_blocks.size() - count_was;
809 count_was = modified_blocks.size();
810 dstream<<"spreadLight modified "<<diff<<std::endl;
815 //MapVoxelManipulator vmanip(this);
817 // Make a manual voxel manipulator and load all the blocks
818 // that touch the requested blocks
819 ManualMapVoxelManipulator vmanip(this);
820 core::map<v3s16, MapBlock*>::Iterator i;
821 i = blocks_to_update.getIterator();
822 for(; i.atEnd() == false; i++)
824 MapBlock *block = i.getNode()->getValue();
825 v3s16 p = block->getPos();
827 // Add all surrounding blocks
828 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
831 Add all surrounding blocks that have up-to-date lighting
832 NOTE: This doesn't quite do the job (not everything
833 appropriate is lighted)
835 /*for(s16 z=-1; z<=1; z++)
836 for(s16 y=-1; y<=1; y++)
837 for(s16 x=-1; x<=1; x++)
840 MapBlock *block = getBlockNoCreateNoEx(p);
845 if(block->getLightingExpired())
847 vmanip.initialEmerge(p, p);
850 // Lighting of block will be updated completely
851 block->setLightingExpired(false);
855 //TimeTaker timer("unSpreadLight");
856 vmanip.unspreadLight(bank, unlight_from, light_sources);
859 //TimeTaker timer("spreadLight");
860 vmanip.spreadLight(bank, light_sources);
863 //TimeTaker timer("blitBack");
864 vmanip.blitBack(modified_blocks);
866 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
870 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
873 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
874 core::map<v3s16, MapBlock*> & modified_blocks)
876 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
877 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
880 Update information about whether day and night light differ
882 for(core::map<v3s16, MapBlock*>::Iterator
883 i = modified_blocks.getIterator();
884 i.atEnd() == false; i++)
886 MapBlock *block = i.getNode()->getValue();
887 block->updateDayNightDiff();
893 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
894 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
897 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
898 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
901 From this node to nodes underneath:
902 If lighting is sunlight (1.0), unlight neighbours and
907 v3s16 toppos = p + v3s16(0,1,0);
908 v3s16 bottompos = p + v3s16(0,-1,0);
910 bool node_under_sunlight = true;
911 core::map<v3s16, bool> light_sources;
914 If there is a node at top and it doesn't have sunlight,
915 there has not been any sunlight going down.
917 Otherwise there probably is.
920 MapNode topnode = getNode(toppos);
922 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
923 node_under_sunlight = false;
925 catch(InvalidPositionException &e)
931 If the new node is solid and there is grass below, change it to mud
933 if(content_features(n).walkable == true)
936 MapNode bottomnode = getNode(bottompos);
938 if(bottomnode.getContent() == CONTENT_GRASS
939 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
941 bottomnode.setContent(CONTENT_MUD);
942 setNode(bottompos, bottomnode);
945 catch(InvalidPositionException &e)
953 If the new node is mud and it is under sunlight, change it
956 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
958 n.setContent(CONTENT_GRASS);
963 Remove all light that has come out of this node
966 enum LightBank banks[] =
971 for(s32 i=0; i<2; i++)
973 enum LightBank bank = banks[i];
975 u8 lightwas = getNode(p).getLight(bank);
977 // Add the block of the added node to modified_blocks
978 v3s16 blockpos = getNodeBlockPos(p);
979 MapBlock * block = getBlockNoCreate(blockpos);
980 assert(block != NULL);
981 modified_blocks.insert(blockpos, block);
983 assert(isValidPosition(p));
985 // Unlight neighbours of node.
986 // This means setting light of all consequent dimmer nodes
988 // This also collects the nodes at the border which will spread
989 // light again into this.
990 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
996 If node lets sunlight through and is under sunlight, it has
999 if(node_under_sunlight && content_features(n).sunlight_propagates)
1001 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1005 Set the node on the map
1014 NodeMetadata *meta_proto = content_features(n).initial_metadata;
1017 NodeMetadata *meta = meta_proto->clone();
1018 meta->setOwner(player_name);
1019 setNodeMetadata(p, meta);
1023 If node is under sunlight and doesn't let sunlight through,
1024 take all sunlighted nodes under it and clear light from them
1025 and from where the light has been spread.
1026 TODO: This could be optimized by mass-unlighting instead
1029 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1033 //m_dout<<DTIME<<"y="<<y<<std::endl;
1034 v3s16 n2pos(p.X, y, p.Z);
1038 n2 = getNode(n2pos);
1040 catch(InvalidPositionException &e)
1045 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1047 unLightNeighbors(LIGHTBANK_DAY,
1048 n2pos, n2.getLight(LIGHTBANK_DAY),
1049 light_sources, modified_blocks);
1050 n2.setLight(LIGHTBANK_DAY, 0);
1058 for(s32 i=0; i<2; i++)
1060 enum LightBank bank = banks[i];
1063 Spread light from all nodes that might be capable of doing so
1065 spreadLight(bank, light_sources, modified_blocks);
1069 Update information about whether day and night light differ
1071 for(core::map<v3s16, MapBlock*>::Iterator
1072 i = modified_blocks.getIterator();
1073 i.atEnd() == false; i++)
1075 MapBlock *block = i.getNode()->getValue();
1076 block->updateDayNightDiff();
1080 Add neighboring liquid nodes and the node itself if it is
1081 liquid (=water node was added) to transform queue.
1084 v3s16(0,0,0), // self
1085 v3s16(0,0,1), // back
1086 v3s16(0,1,0), // top
1087 v3s16(1,0,0), // right
1088 v3s16(0,0,-1), // front
1089 v3s16(0,-1,0), // bottom
1090 v3s16(-1,0,0), // left
1092 for(u16 i=0; i<7; i++)
1097 v3s16 p2 = p + dirs[i];
1099 MapNode n2 = getNode(p2);
1100 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1102 m_transforming_liquid.push_back(p2);
1105 }catch(InvalidPositionException &e)
1113 void Map::removeNodeAndUpdate(v3s16 p,
1114 core::map<v3s16, MapBlock*> &modified_blocks)
1116 /*PrintInfo(m_dout);
1117 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1118 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1120 bool node_under_sunlight = true;
1122 v3s16 toppos = p + v3s16(0,1,0);
1124 // Node will be replaced with this
1125 content_t replace_material = CONTENT_AIR;
1128 If there is a node at top and it doesn't have sunlight,
1129 there will be no sunlight going down.
1132 MapNode topnode = getNode(toppos);
1134 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1135 node_under_sunlight = false;
1137 catch(InvalidPositionException &e)
1141 core::map<v3s16, bool> light_sources;
1143 enum LightBank banks[] =
1148 for(s32 i=0; i<2; i++)
1150 enum LightBank bank = banks[i];
1153 Unlight neighbors (in case the node is a light source)
1155 unLightNeighbors(bank, p,
1156 getNode(p).getLight(bank),
1157 light_sources, modified_blocks);
1161 Remove node metadata
1164 removeNodeMetadata(p);
1168 This also clears the lighting.
1172 n.setContent(replace_material);
1175 for(s32 i=0; i<2; i++)
1177 enum LightBank bank = banks[i];
1180 Recalculate lighting
1182 spreadLight(bank, light_sources, modified_blocks);
1185 // Add the block of the removed node to modified_blocks
1186 v3s16 blockpos = getNodeBlockPos(p);
1187 MapBlock * block = getBlockNoCreate(blockpos);
1188 assert(block != NULL);
1189 modified_blocks.insert(blockpos, block);
1192 If the removed node was under sunlight, propagate the
1193 sunlight down from it and then light all neighbors
1194 of the propagated blocks.
1196 if(node_under_sunlight)
1198 s16 ybottom = propagateSunlight(p, modified_blocks);
1199 /*m_dout<<DTIME<<"Node was under sunlight. "
1200 "Propagating sunlight";
1201 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1203 for(; y >= ybottom; y--)
1205 v3s16 p2(p.X, y, p.Z);
1206 /*m_dout<<DTIME<<"lighting neighbors of node ("
1207 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1209 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1214 // Set the lighting of this node to 0
1215 // TODO: Is this needed? Lighting is cleared up there already.
1217 MapNode n = getNode(p);
1218 n.setLight(LIGHTBANK_DAY, 0);
1221 catch(InvalidPositionException &e)
1227 for(s32 i=0; i<2; i++)
1229 enum LightBank bank = banks[i];
1231 // Get the brightest neighbour node and propagate light from it
1232 v3s16 n2p = getBrightestNeighbour(bank, p);
1234 MapNode n2 = getNode(n2p);
1235 lightNeighbors(bank, n2p, modified_blocks);
1237 catch(InvalidPositionException &e)
1243 Update information about whether day and night light differ
1245 for(core::map<v3s16, MapBlock*>::Iterator
1246 i = modified_blocks.getIterator();
1247 i.atEnd() == false; i++)
1249 MapBlock *block = i.getNode()->getValue();
1250 block->updateDayNightDiff();
1254 Add neighboring liquid nodes and this node to transform queue.
1255 (it's vital for the node itself to get updated last.)
1258 v3s16(0,0,1), // back
1259 v3s16(0,1,0), // top
1260 v3s16(1,0,0), // right
1261 v3s16(0,0,-1), // front
1262 v3s16(0,-1,0), // bottom
1263 v3s16(-1,0,0), // left
1264 v3s16(0,0,0), // self
1266 for(u16 i=0; i<7; i++)
1271 v3s16 p2 = p + dirs[i];
1273 MapNode n2 = getNode(p2);
1274 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1276 m_transforming_liquid.push_back(p2);
1279 }catch(InvalidPositionException &e)
1285 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1288 event.type = MEET_ADDNODE;
1292 bool succeeded = true;
1294 core::map<v3s16, MapBlock*> modified_blocks;
1295 std::string st = std::string("");
1296 addNodeAndUpdate(p, n, modified_blocks, st);
1298 // Copy modified_blocks to event
1299 for(core::map<v3s16, MapBlock*>::Iterator
1300 i = modified_blocks.getIterator();
1301 i.atEnd()==false; i++)
1303 event.modified_blocks.insert(i.getNode()->getKey(), false);
1306 catch(InvalidPositionException &e){
1310 dispatchEvent(&event);
1315 bool Map::removeNodeWithEvent(v3s16 p)
1318 event.type = MEET_REMOVENODE;
1321 bool succeeded = true;
1323 core::map<v3s16, MapBlock*> modified_blocks;
1324 removeNodeAndUpdate(p, modified_blocks);
1326 // Copy modified_blocks to event
1327 for(core::map<v3s16, MapBlock*>::Iterator
1328 i = modified_blocks.getIterator();
1329 i.atEnd()==false; i++)
1331 event.modified_blocks.insert(i.getNode()->getKey(), false);
1334 catch(InvalidPositionException &e){
1338 dispatchEvent(&event);
1343 bool Map::dayNightDiffed(v3s16 blockpos)
1346 v3s16 p = blockpos + v3s16(0,0,0);
1347 MapBlock *b = getBlockNoCreate(p);
1348 if(b->dayNightDiffed())
1351 catch(InvalidPositionException &e){}
1354 v3s16 p = blockpos + v3s16(-1,0,0);
1355 MapBlock *b = getBlockNoCreate(p);
1356 if(b->dayNightDiffed())
1359 catch(InvalidPositionException &e){}
1361 v3s16 p = blockpos + v3s16(0,-1,0);
1362 MapBlock *b = getBlockNoCreate(p);
1363 if(b->dayNightDiffed())
1366 catch(InvalidPositionException &e){}
1368 v3s16 p = blockpos + v3s16(0,0,-1);
1369 MapBlock *b = getBlockNoCreate(p);
1370 if(b->dayNightDiffed())
1373 catch(InvalidPositionException &e){}
1376 v3s16 p = blockpos + v3s16(1,0,0);
1377 MapBlock *b = getBlockNoCreate(p);
1378 if(b->dayNightDiffed())
1381 catch(InvalidPositionException &e){}
1383 v3s16 p = blockpos + v3s16(0,1,0);
1384 MapBlock *b = getBlockNoCreate(p);
1385 if(b->dayNightDiffed())
1388 catch(InvalidPositionException &e){}
1390 v3s16 p = blockpos + v3s16(0,0,1);
1391 MapBlock *b = getBlockNoCreate(p);
1392 if(b->dayNightDiffed())
1395 catch(InvalidPositionException &e){}
1401 Updates usage timers
1403 void Map::timerUpdate(float dtime, float unload_timeout,
1404 core::list<v3s16> *unloaded_blocks)
1406 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1408 core::list<v2s16> sector_deletion_queue;
1409 u32 deleted_blocks_count = 0;
1410 u32 saved_blocks_count = 0;
1412 core::map<v2s16, MapSector*>::Iterator si;
1415 si = m_sectors.getIterator();
1416 for(; si.atEnd() == false; si++)
1418 MapSector *sector = si.getNode()->getValue();
1420 bool all_blocks_deleted = true;
1422 core::list<MapBlock*> blocks;
1423 sector->getBlocks(blocks);
1425 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1426 i != blocks.end(); i++)
1428 MapBlock *block = (*i);
1430 block->incrementUsageTimer(dtime);
1432 if(block->getUsageTimer() > unload_timeout)
1434 v3s16 p = block->getPos();
1437 if(block->getModified() != MOD_STATE_CLEAN
1438 && save_before_unloading)
1441 saved_blocks_count++;
1444 // Delete from memory
1445 sector->deleteBlock(block);
1448 unloaded_blocks->push_back(p);
1450 deleted_blocks_count++;
1454 all_blocks_deleted = false;
1458 if(all_blocks_deleted)
1460 sector_deletion_queue.push_back(si.getNode()->getKey());
1465 // Finally delete the empty sectors
1466 deleteSectors(sector_deletion_queue);
1468 if(deleted_blocks_count != 0)
1470 PrintInfo(dstream); // ServerMap/ClientMap:
1471 dstream<<"Unloaded "<<deleted_blocks_count
1472 <<" blocks from memory";
1473 if(save_before_unloading)
1474 dstream<<", of which "<<saved_blocks_count<<" were written";
1475 dstream<<"."<<std::endl;
1479 void Map::deleteSectors(core::list<v2s16> &list)
1481 core::list<v2s16>::Iterator j;
1482 for(j=list.begin(); j!=list.end(); j++)
1484 MapSector *sector = m_sectors[*j];
1485 // If sector is in sector cache, remove it from there
1486 if(m_sector_cache == sector)
1487 m_sector_cache = NULL;
1488 // Remove from map and delete
1489 m_sectors.remove(*j);
1495 void Map::unloadUnusedData(float timeout,
1496 core::list<v3s16> *deleted_blocks)
1498 core::list<v2s16> sector_deletion_queue;
1499 u32 deleted_blocks_count = 0;
1500 u32 saved_blocks_count = 0;
1502 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1503 for(; si.atEnd() == false; si++)
1505 MapSector *sector = si.getNode()->getValue();
1507 bool all_blocks_deleted = true;
1509 core::list<MapBlock*> blocks;
1510 sector->getBlocks(blocks);
1511 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1512 i != blocks.end(); i++)
1514 MapBlock *block = (*i);
1516 if(block->getUsageTimer() > timeout)
1519 if(block->getModified() != MOD_STATE_CLEAN)
1522 saved_blocks_count++;
1524 // Delete from memory
1525 sector->deleteBlock(block);
1526 deleted_blocks_count++;
1530 all_blocks_deleted = false;
1534 if(all_blocks_deleted)
1536 sector_deletion_queue.push_back(si.getNode()->getKey());
1540 deleteSectors(sector_deletion_queue);
1542 dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1543 <<", of which "<<saved_blocks_count<<" were wr."
1546 //return sector_deletion_queue.getSize();
1547 //return deleted_blocks_count;
1551 void Map::PrintInfo(std::ostream &out)
1556 #define WATER_DROP_BOOST 4
1560 NEIGHBOR_SAME_LEVEL,
1563 struct NodeNeighbor {
1569 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1571 DSTACK(__FUNCTION_NAME);
1572 //TimeTaker timer("transformLiquids()");
1575 u32 initial_size = m_transforming_liquid.size();
1577 /*if(initial_size != 0)
1578 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1580 // list of nodes that due to viscosity have not reached their max level height
1581 UniqueQueue<v3s16> must_reflow;
1583 // List of MapBlocks that will require a lighting update (due to lava)
1584 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1586 while(m_transforming_liquid.size() != 0)
1588 // This should be done here so that it is done when continue is used
1589 if(loopcount >= initial_size * 3)
1594 Get a queued transforming liquid node
1596 v3s16 p0 = m_transforming_liquid.pop_front();
1598 MapNode n0 = getNodeNoEx(p0);
1601 Collect information about current node
1603 s8 liquid_level = -1;
1604 u8 liquid_kind = CONTENT_IGNORE;
1605 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1606 switch (liquid_type) {
1608 liquid_level = LIQUID_LEVEL_SOURCE;
1609 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1611 case LIQUID_FLOWING:
1612 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1613 liquid_kind = n0.getContent();
1616 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1617 // continue with the next node.
1618 if (n0.getContent() != CONTENT_AIR)
1620 liquid_kind = CONTENT_AIR;
1625 Collect information about the environment
1627 const v3s16 *dirs = g_6dirs;
1628 NodeNeighbor sources[6]; // surrounding sources
1629 int num_sources = 0;
1630 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1632 NodeNeighbor airs[6]; // surrounding air
1634 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1635 int num_neutrals = 0;
1636 bool flowing_down = false;
1637 for (u16 i = 0; i < 6; i++) {
1638 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1641 nt = NEIGHBOR_UPPER;
1644 nt = NEIGHBOR_LOWER;
1647 v3s16 npos = p0 + dirs[i];
1648 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1649 switch (content_features(nb.n.getContent()).liquid_type) {
1651 if (nb.n.getContent() == CONTENT_AIR) {
1652 airs[num_airs++] = nb;
1653 // if the current node is a water source the neighbor
1654 // should be enqueded for transformation regardless of whether the
1655 // current node changes or not.
1656 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1657 m_transforming_liquid.push_back(npos);
1658 // if the current node happens to be a flowing node, it will start to flow down here.
1659 if (nb.t == NEIGHBOR_LOWER) {
1660 flowing_down = true;
1663 neutrals[num_neutrals++] = nb;
1667 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1668 if (liquid_kind == CONTENT_AIR)
1669 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1670 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1671 neutrals[num_neutrals++] = nb;
1673 sources[num_sources++] = nb;
1676 case LIQUID_FLOWING:
1677 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1678 if (liquid_kind == CONTENT_AIR)
1679 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1680 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1681 neutrals[num_neutrals++] = nb;
1683 flows[num_flows++] = nb;
1684 if (nb.t == NEIGHBOR_LOWER)
1685 flowing_down = true;
1692 decide on the type (and possibly level) of the current node
1694 content_t new_node_content;
1695 s8 new_node_level = -1;
1696 s8 max_node_level = -1;
1697 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1698 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1699 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1700 // it's perfectly safe to use liquid_kind here to determine the new node content.
1701 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1702 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1703 // liquid_kind is set properly, see above
1704 new_node_content = liquid_kind;
1705 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1707 // no surrounding sources, so get the maximum level that can flow into this node
1708 for (u16 i = 0; i < num_flows; i++) {
1709 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1710 switch (flows[i].t) {
1711 case NEIGHBOR_UPPER:
1712 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1713 max_node_level = LIQUID_LEVEL_MAX;
1714 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1715 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1716 } else if (nb_liquid_level > max_node_level)
1717 max_node_level = nb_liquid_level;
1719 case NEIGHBOR_LOWER:
1721 case NEIGHBOR_SAME_LEVEL:
1722 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1723 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1724 max_node_level = nb_liquid_level - 1;
1730 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1731 if (viscosity > 1 && max_node_level != liquid_level) {
1732 // amount to gain, limited by viscosity
1733 // must be at least 1 in absolute value
1734 s8 level_inc = max_node_level - liquid_level;
1735 if (level_inc < -viscosity || level_inc > viscosity)
1736 new_node_level = liquid_level + level_inc/viscosity;
1737 else if (level_inc < 0)
1738 new_node_level = liquid_level - 1;
1739 else if (level_inc > 0)
1740 new_node_level = liquid_level + 1;
1741 if (new_node_level != max_node_level)
1742 must_reflow.push_back(p0);
1744 new_node_level = max_node_level;
1746 if (new_node_level >= 0)
1747 new_node_content = liquid_kind;
1749 new_node_content = CONTENT_AIR;
1754 check if anything has changed. if not, just continue with the next node.
1756 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1757 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1758 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1764 update the current node
1766 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1767 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1768 // set level to last 3 bits, flowing down bit to 4th bit
1769 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1771 // set the liquid level and flow bit to 0
1772 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1774 n0.setContent(new_node_content);
1776 v3s16 blockpos = getNodeBlockPos(p0);
1777 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1779 modified_blocks.insert(blockpos, block);
1780 // If node emits light, MapBlock requires lighting update
1781 if(content_features(n0).light_source != 0)
1782 lighting_modified_blocks[block->getPos()] = block;
1786 enqueue neighbors for update if neccessary
1788 switch (content_features(n0.getContent()).liquid_type) {
1790 case LIQUID_FLOWING:
1791 // make sure source flows into all neighboring nodes
1792 for (u16 i = 0; i < num_flows; i++)
1793 if (flows[i].t != NEIGHBOR_UPPER)
1794 m_transforming_liquid.push_back(flows[i].p);
1795 for (u16 i = 0; i < num_airs; i++)
1796 if (airs[i].t != NEIGHBOR_UPPER)
1797 m_transforming_liquid.push_back(airs[i].p);
1800 // this flow has turned to air; neighboring flows might need to do the same
1801 for (u16 i = 0; i < num_flows; i++)
1802 m_transforming_liquid.push_back(flows[i].p);
1806 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1807 while (must_reflow.size() > 0)
1808 m_transforming_liquid.push_back(must_reflow.pop_front());
1809 updateLighting(lighting_modified_blocks, modified_blocks);
1812 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1814 v3s16 blockpos = getNodeBlockPos(p);
1815 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1816 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1819 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1823 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1827 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1829 v3s16 blockpos = getNodeBlockPos(p);
1830 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1831 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1834 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1838 block->m_node_metadata.set(p_rel, meta);
1841 void Map::removeNodeMetadata(v3s16 p)
1843 v3s16 blockpos = getNodeBlockPos(p);
1844 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1845 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1848 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1852 block->m_node_metadata.remove(p_rel);
1855 void Map::nodeMetadataStep(float dtime,
1856 core::map<v3s16, MapBlock*> &changed_blocks)
1860 Currently there is no way to ensure that all the necessary
1861 blocks are loaded when this is run. (They might get unloaded)
1862 NOTE: ^- Actually, that might not be so. In a quick test it
1863 reloaded a block with a furnace when I walked back to it from
1866 core::map<v2s16, MapSector*>::Iterator si;
1867 si = m_sectors.getIterator();
1868 for(; si.atEnd() == false; si++)
1870 MapSector *sector = si.getNode()->getValue();
1871 core::list< MapBlock * > sectorblocks;
1872 sector->getBlocks(sectorblocks);
1873 core::list< MapBlock * >::Iterator i;
1874 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1876 MapBlock *block = *i;
1877 bool changed = block->m_node_metadata.step(dtime);
1879 changed_blocks[block->getPos()] = block;
1888 ServerMap::ServerMap(std::string savedir):
1891 m_map_metadata_changed(true),
1893 m_database_read(NULL),
1894 m_database_write(NULL)
1896 dstream<<__FUNCTION_NAME<<std::endl;
1898 //m_chunksize = 8; // Takes a few seconds
1900 if (g_settings.get("fixed_map_seed").empty())
1902 m_seed = (((u64)(myrand()%0xffff)<<0)
1903 + ((u64)(myrand()%0xffff)<<16)
1904 + ((u64)(myrand()%0xffff)<<32)
1905 + ((u64)(myrand()%0xffff)<<48));
1909 m_seed = g_settings.getU64("fixed_map_seed");
1913 Experimental and debug stuff
1920 Try to load map; if not found, create a new one.
1923 m_savedir = savedir;
1924 m_map_saving_enabled = false;
1928 // If directory exists, check contents and load if possible
1929 if(fs::PathExists(m_savedir))
1931 // If directory is empty, it is safe to save into it.
1932 if(fs::GetDirListing(m_savedir).size() == 0)
1934 dstream<<DTIME<<"Server: Empty save directory is valid."
1936 m_map_saving_enabled = true;
1941 // Load map metadata (seed, chunksize)
1944 catch(FileNotGoodException &e){
1945 dstream<<DTIME<<"WARNING: Could not load map metadata"
1946 //<<" Disabling chunk-based generator."
1952 // Load chunk metadata
1955 catch(FileNotGoodException &e){
1956 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1957 <<" Disabling chunk-based generator."
1962 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1963 "metadata and sector (0,0) from "<<savedir<<
1964 ", assuming valid save directory."
1967 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1968 <<"and chunk metadata from "<<savedir
1969 <<", assuming valid save directory."
1972 m_map_saving_enabled = true;
1973 // Map loaded, not creating new one
1977 // If directory doesn't exist, it is safe to save to it
1979 m_map_saving_enabled = true;
1982 catch(std::exception &e)
1984 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1985 <<", exception: "<<e.what()<<std::endl;
1986 dstream<<"Please remove the map or fix it."<<std::endl;
1987 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1990 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1992 // Create zero sector
1993 emergeSector(v2s16(0,0));
1995 // Initially write whole map
1999 ServerMap::~ServerMap()
2001 dstream<<__FUNCTION_NAME<<std::endl;
2005 if(m_map_saving_enabled)
2007 // Save only changed parts
2009 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2013 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2016 catch(std::exception &e)
2018 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2019 <<", exception: "<<e.what()<<std::endl;
2023 Close database if it was opened
2026 sqlite3_finalize(m_database_read);
2027 if(m_database_write)
2028 sqlite3_finalize(m_database_write);
2030 sqlite3_close(m_database);
2036 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2037 for(; i.atEnd() == false; i++)
2039 MapChunk *chunk = i.getNode()->getValue();
2045 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2047 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2048 if(enable_mapgen_debug_info)
2049 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2050 <<blockpos.Z<<")"<<std::endl;
2052 // Do nothing if not inside limits (+-1 because of neighbors)
2053 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2054 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2060 data->no_op = false;
2061 data->seed = m_seed;
2062 data->blockpos = blockpos;
2065 Create the whole area of this and the neighboring blocks
2068 //TimeTaker timer("initBlockMake() create area");
2070 for(s16 x=-1; x<=1; x++)
2071 for(s16 z=-1; z<=1; z++)
2073 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2074 // Sector metadata is loaded from disk if not already loaded.
2075 ServerMapSector *sector = createSector(sectorpos);
2078 for(s16 y=-1; y<=1; y++)
2080 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2081 //MapBlock *block = createBlock(p);
2082 // 1) get from memory, 2) load from disk
2083 MapBlock *block = emergeBlock(p, false);
2084 // 3) create a blank one
2087 block = createBlock(p);
2090 Block gets sunlight if this is true.
2092 Refer to the map generator heuristics.
2094 bool ug = mapgen::block_is_underground(data->seed, p);
2095 block->setIsUnderground(ug);
2098 // Lighting will not be valid after make_chunk is called
2099 block->setLightingExpired(true);
2100 // Lighting will be calculated
2101 //block->setLightingExpired(false);
2107 Now we have a big empty area.
2109 Make a ManualMapVoxelManipulator that contains this and the
2113 // The area that contains this block and it's neighbors
2114 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2115 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2117 data->vmanip = new ManualMapVoxelManipulator(this);
2118 //data->vmanip->setMap(this);
2122 //TimeTaker timer("initBlockMake() initialEmerge");
2123 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2126 // Data is ready now.
2129 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2130 core::map<v3s16, MapBlock*> &changed_blocks)
2132 v3s16 blockpos = data->blockpos;
2133 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2134 <<blockpos.Z<<")"<<std::endl;*/
2138 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2142 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2144 /*dstream<<"Resulting vmanip:"<<std::endl;
2145 data->vmanip.print(dstream);*/
2148 Blit generated stuff to map
2149 NOTE: blitBackAll adds nearly everything to changed_blocks
2153 //TimeTaker timer("finishBlockMake() blitBackAll");
2154 data->vmanip->blitBackAll(&changed_blocks);
2157 if(enable_mapgen_debug_info)
2158 dstream<<"finishBlockMake: changed_blocks.size()="
2159 <<changed_blocks.size()<<std::endl;
2162 Copy transforming liquid information
2164 while(data->transforming_liquid.size() > 0)
2166 v3s16 p = data->transforming_liquid.pop_front();
2167 m_transforming_liquid.push_back(p);
2173 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2177 Set is_underground flag for lighting with sunlight.
2179 Refer to map generator heuristics.
2181 NOTE: This is done in initChunkMake
2183 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2187 Add sunlight to central block.
2188 This makes in-dark-spawning monsters to not flood the whole thing.
2189 Do not spread the light, though.
2191 /*core::map<v3s16, bool> light_sources;
2192 bool black_air_left = false;
2193 block->propagateSunlight(light_sources, true, &black_air_left);*/
2196 NOTE: Lighting and object adding shouldn't really be here, but
2197 lighting is a bit tricky to move properly to makeBlock.
2198 TODO: Do this the right way anyway, that is, move it to makeBlock.
2199 - There needs to be some way for makeBlock to report back if
2200 the lighting update is going further down because of the
2201 new block blocking light
2206 NOTE: This takes ~60ms, TODO: Investigate why
2209 TimeTaker t("finishBlockMake lighting update");
2211 core::map<v3s16, MapBlock*> lighting_update_blocks;
2214 lighting_update_blocks.insert(block->getPos(), block);
2219 v3s16 p = block->getPos()+v3s16(x,1,z);
2220 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2224 // All modified blocks
2225 // NOTE: Should this be done? If this is not done, then the lighting
2226 // of the others will be updated in a different place, one by one, i
2227 // think... or they might not? Well, at least they are left marked as
2228 // "lighting expired"; it seems that is not handled at all anywhere,
2229 // so enabling this will slow it down A LOT because otherwise it
2230 // would not do this at all. This causes the black trees.
2231 for(core::map<v3s16, MapBlock*>::Iterator
2232 i = changed_blocks.getIterator();
2233 i.atEnd() == false; i++)
2235 lighting_update_blocks.insert(i.getNode()->getKey(),
2236 i.getNode()->getValue());
2238 /*// Also force-add all the upmost blocks for proper sunlight
2239 for(s16 x=-1; x<=1; x++)
2240 for(s16 z=-1; z<=1; z++)
2242 v3s16 p = block->getPos()+v3s16(x,1,z);
2243 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2246 updateLighting(lighting_update_blocks, changed_blocks);
2249 Set lighting to non-expired state in all of them.
2250 This is cheating, but it is not fast enough if all of them
2251 would actually be updated.
2253 for(s16 x=-1; x<=1; x++)
2254 for(s16 y=-1; y<=1; y++)
2255 for(s16 z=-1; z<=1; z++)
2257 v3s16 p = block->getPos()+v3s16(x,y,z);
2258 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2261 if(enable_mapgen_debug_info == false)
2262 t.stop(true); // Hide output
2266 Add random objects to block
2268 mapgen::add_random_objects(block);
2271 Go through changed blocks
2273 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2274 i.atEnd() == false; i++)
2276 MapBlock *block = i.getNode()->getValue();
2279 Update day/night difference cache of the MapBlocks
2281 block->updateDayNightDiff();
2283 Set block as modified
2285 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2289 Set central block as generated
2291 block->setGenerated(true);
2294 Save changed parts of map
2295 NOTE: Will be saved later.
2299 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2300 <<blockpos.Z<<")"<<std::endl;*/
2302 if(enable_mapgen_debug_info)
2305 Analyze resulting blocks
2307 for(s16 x=-1; x<=1; x++)
2308 for(s16 y=-1; y<=1; y++)
2309 for(s16 z=-1; z<=1; z++)
2311 v3s16 p = block->getPos()+v3s16(x,y,z);
2312 MapBlock *block = getBlockNoCreateNoEx(p);
2314 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2315 dstream<<"Generated "<<spos<<": "
2316 <<analyze_block(block)<<std::endl;
2324 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2326 DSTACKF("%s: p2d=(%d,%d)",
2331 Check if it exists already in memory
2333 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2338 Try to load it from disk (with blocks)
2340 //if(loadSectorFull(p2d) == true)
2343 Try to load metadata from disk
2346 if(loadSectorMeta(p2d) == true)
2348 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2351 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2352 throw InvalidPositionException("");
2358 Do not create over-limit
2360 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2361 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2362 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2363 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2364 throw InvalidPositionException("createSector(): pos. over limit");
2367 Generate blank sector
2370 sector = new ServerMapSector(this, p2d);
2372 // Sector position on map in nodes
2373 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2378 m_sectors.insert(p2d, sector);
2384 This is a quick-hand function for calling makeBlock().
2386 MapBlock * ServerMap::generateBlock(
2388 core::map<v3s16, MapBlock*> &modified_blocks
2391 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2393 /*dstream<<"generateBlock(): "
2394 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2397 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2399 TimeTaker timer("generateBlock");
2401 //MapBlock *block = original_dummy;
2403 v2s16 p2d(p.X, p.Z);
2404 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2407 Do not generate over-limit
2409 if(blockpos_over_limit(p))
2411 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2412 throw InvalidPositionException("generateBlock(): pos. over limit");
2416 Create block make data
2418 mapgen::BlockMakeData data;
2419 initBlockMake(&data, p);
2425 TimeTaker t("mapgen::make_block()");
2426 mapgen::make_block(&data);
2428 if(enable_mapgen_debug_info == false)
2429 t.stop(true); // Hide output
2433 Blit data back on map, update lighting, add mobs and whatever this does
2435 finishBlockMake(&data, modified_blocks);
2440 MapBlock *block = getBlockNoCreateNoEx(p);
2448 bool erroneus_content = false;
2449 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2450 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2451 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2454 MapNode n = block->getNode(p);
2455 if(n.getContent() == CONTENT_IGNORE)
2457 dstream<<"CONTENT_IGNORE at "
2458 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2460 erroneus_content = true;
2464 if(erroneus_content)
2473 Generate a completely empty block
2477 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2478 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2480 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2484 n.setContent(CONTENT_AIR);
2486 n.setContent(CONTENT_STONE);
2487 block->setNode(v3s16(x0,y0,z0), n);
2493 if(enable_mapgen_debug_info == false)
2494 timer.stop(true); // Hide output
2499 MapBlock * ServerMap::createBlock(v3s16 p)
2501 DSTACKF("%s: p=(%d,%d,%d)",
2502 __FUNCTION_NAME, p.X, p.Y, p.Z);
2505 Do not create over-limit
2507 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2508 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2509 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2510 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2511 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2512 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2513 throw InvalidPositionException("createBlock(): pos. over limit");
2515 v2s16 p2d(p.X, p.Z);
2518 This will create or load a sector if not found in memory.
2519 If block exists on disk, it will be loaded.
2521 NOTE: On old save formats, this will be slow, as it generates
2522 lighting on blocks for them.
2524 ServerMapSector *sector;
2526 sector = (ServerMapSector*)createSector(p2d);
2527 assert(sector->getId() == MAPSECTOR_SERVER);
2529 catch(InvalidPositionException &e)
2531 dstream<<"createBlock: createSector() failed"<<std::endl;
2535 NOTE: This should not be done, or at least the exception
2536 should not be passed on as std::exception, because it
2537 won't be catched at all.
2539 /*catch(std::exception &e)
2541 dstream<<"createBlock: createSector() failed: "
2542 <<e.what()<<std::endl;
2547 Try to get a block from the sector
2550 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2553 if(block->isDummy())
2558 block = sector->createBlankBlock(block_y);
2562 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2564 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2566 p.X, p.Y, p.Z, allow_generate);
2569 MapBlock *block = getBlockNoCreateNoEx(p);
2570 if(block && block->isDummy() == false)
2575 MapBlock *block = loadBlock(p);
2582 core::map<v3s16, MapBlock*> modified_blocks;
2583 MapBlock *block = generateBlock(p, modified_blocks);
2587 event.type = MEET_OTHER;
2590 // Copy modified_blocks to event
2591 for(core::map<v3s16, MapBlock*>::Iterator
2592 i = modified_blocks.getIterator();
2593 i.atEnd()==false; i++)
2595 event.modified_blocks.insert(i.getNode()->getKey(), false);
2599 dispatchEvent(&event);
2610 Do not generate over-limit
2612 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2613 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2614 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2615 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2616 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2617 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2618 throw InvalidPositionException("emergeBlock(): pos. over limit");
2620 v2s16 p2d(p.X, p.Z);
2623 This will create or load a sector if not found in memory.
2624 If block exists on disk, it will be loaded.
2626 ServerMapSector *sector;
2628 sector = createSector(p2d);
2629 //sector = emergeSector(p2d, changed_blocks);
2631 catch(InvalidPositionException &e)
2633 dstream<<"emergeBlock: createSector() failed: "
2634 <<e.what()<<std::endl;
2635 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2637 <<"You could try to delete it."<<std::endl;
2640 catch(VersionMismatchException &e)
2642 dstream<<"emergeBlock: createSector() failed: "
2643 <<e.what()<<std::endl;
2644 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2646 <<"You could try to delete it."<<std::endl;
2651 Try to get a block from the sector
2654 bool does_not_exist = false;
2655 bool lighting_expired = false;
2656 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2658 // If not found, try loading from disk
2661 block = loadBlock(p);
2667 does_not_exist = true;
2669 else if(block->isDummy() == true)
2671 does_not_exist = true;
2673 else if(block->getLightingExpired())
2675 lighting_expired = true;
2680 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2685 If block was not found on disk and not going to generate a
2686 new one, make sure there is a dummy block in place.
2688 if(only_from_disk && (does_not_exist || lighting_expired))
2690 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2694 // Create dummy block
2695 block = new MapBlock(this, p, true);
2697 // Add block to sector
2698 sector->insertBlock(block);
2704 //dstream<<"Not found on disk, generating."<<std::endl;
2706 //TimeTaker("emergeBlock() generate");
2708 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2711 If the block doesn't exist, generate the block.
2715 block = generateBlock(p, block, sector, changed_blocks,
2716 lighting_invalidated_blocks);
2719 if(lighting_expired)
2721 lighting_invalidated_blocks.insert(p, block);
2726 Initially update sunlight
2729 core::map<v3s16, bool> light_sources;
2730 bool black_air_left = false;
2731 bool bottom_invalid =
2732 block->propagateSunlight(light_sources, true,
2735 // If sunlight didn't reach everywhere and part of block is
2736 // above ground, lighting has to be properly updated
2737 //if(black_air_left && some_part_underground)
2740 lighting_invalidated_blocks[block->getPos()] = block;
2745 lighting_invalidated_blocks[block->getPos()] = block;
2754 s16 ServerMap::findGroundLevel(v2s16 p2d)
2758 Uh, just do something random...
2760 // Find existing map from top to down
2763 v3s16 p(p2d.X, max, p2d.Y);
2764 for(; p.Y>min; p.Y--)
2766 MapNode n = getNodeNoEx(p);
2767 if(n.getContent() != CONTENT_IGNORE)
2772 // If this node is not air, go to plan b
2773 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2775 // Search existing walkable and return it
2776 for(; p.Y>min; p.Y--)
2778 MapNode n = getNodeNoEx(p);
2779 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2788 Determine from map generator noise functions
2791 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2794 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2795 //return (s16)level;
2798 void ServerMap::createDatabase() {
2801 e = sqlite3_exec(m_database,
2802 "CREATE TABLE IF NOT EXISTS `blocks` ("
2803 "`pos` INT NOT NULL PRIMARY KEY,"
2806 , NULL, NULL, NULL);
2807 if(e == SQLITE_ABORT)
2808 throw FileNotGoodException("Could not create database structure");
2810 dstream<<"Server: Database structure was created";
2813 void ServerMap::verifyDatabase() {
2818 std::string dbp = m_savedir + "/map.sqlite";
2819 bool needs_create = false;
2823 Open the database connection
2826 createDirs(m_savedir);
2828 if(!fs::PathExists(dbp))
2829 needs_create = true;
2831 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2832 if(d != SQLITE_OK) {
2833 dstream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2834 throw FileNotGoodException("Cannot open database file");
2840 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2841 if(d != SQLITE_OK) {
2842 dstream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2843 throw FileNotGoodException("Cannot prepare read statement");
2846 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2847 if(d != SQLITE_OK) {
2848 dstream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2849 throw FileNotGoodException("Cannot prepare write statement");
2852 dstream<<"Server: Database opened"<<std::endl;
2856 bool ServerMap::loadFromFolders() {
2857 if(!m_database && !fs::PathExists(m_savedir + "/map.sqlite"))
2862 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2863 return (sqlite3_int64)pos.Z*16777216 +
2864 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2867 void ServerMap::createDirs(std::string path)
2869 if(fs::CreateAllDirs(path) == false)
2871 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2872 <<"\""<<path<<"\""<<std::endl;
2873 throw BaseException("ServerMap failed to create directory");
2877 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2883 snprintf(cc, 9, "%.4x%.4x",
2884 (unsigned int)pos.X&0xffff,
2885 (unsigned int)pos.Y&0xffff);
2887 return m_savedir + "/sectors/" + cc;
2889 snprintf(cc, 9, "%.3x/%.3x",
2890 (unsigned int)pos.X&0xfff,
2891 (unsigned int)pos.Y&0xfff);
2893 return m_savedir + "/sectors2/" + cc;
2899 v2s16 ServerMap::getSectorPos(std::string dirname)
2903 size_t spos = dirname.rfind('/') + 1;
2904 assert(spos != std::string::npos);
2905 if(dirname.size() - spos == 8)
2908 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2910 else if(dirname.size() - spos == 3)
2913 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2914 // Sign-extend the 12 bit values up to 16 bits...
2915 if(x&0x800) x|=0xF000;
2916 if(y&0x800) y|=0xF000;
2923 v2s16 pos((s16)x, (s16)y);
2927 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2929 v2s16 p2d = getSectorPos(sectordir);
2931 if(blockfile.size() != 4){
2932 throw InvalidFilenameException("Invalid block filename");
2935 int r = sscanf(blockfile.c_str(), "%4x", &y);
2937 throw InvalidFilenameException("Invalid block filename");
2938 return v3s16(p2d.X, y, p2d.Y);
2941 std::string ServerMap::getBlockFilename(v3s16 p)
2944 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2948 void ServerMap::save(bool only_changed)
2950 DSTACK(__FUNCTION_NAME);
2951 if(m_map_saving_enabled == false)
2953 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2957 if(only_changed == false)
2958 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2961 if(only_changed == false || m_map_metadata_changed)
2966 u32 sector_meta_count = 0;
2967 u32 block_count = 0;
2968 u32 block_count_all = 0; // Number of blocks in memory
2971 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2972 for(; i.atEnd() == false; i++)
2974 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2975 assert(sector->getId() == MAPSECTOR_SERVER);
2977 if(sector->differs_from_disk || only_changed == false)
2979 saveSectorMeta(sector);
2980 sector_meta_count++;
2982 core::list<MapBlock*> blocks;
2983 sector->getBlocks(blocks);
2984 core::list<MapBlock*>::Iterator j;
2986 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2987 for(j=blocks.begin(); j!=blocks.end(); j++)
2989 MapBlock *block = *j;
2993 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2994 || only_changed == false)
2999 /*dstream<<"ServerMap: Written block ("
3000 <<block->getPos().X<<","
3001 <<block->getPos().Y<<","
3002 <<block->getPos().Z<<")"
3005 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
3011 Only print if something happened or saved whole map
3013 if(only_changed == false || sector_meta_count != 0
3014 || block_count != 0)
3016 dstream<<DTIME<<"ServerMap: Written: "
3017 <<sector_meta_count<<" sector metadata files, "
3018 <<block_count<<" block files"
3019 <<", "<<block_count_all<<" blocks in memory."
3024 void ServerMap::saveMapMeta()
3026 DSTACK(__FUNCTION_NAME);
3028 dstream<<"INFO: ServerMap::saveMapMeta(): "
3032 createDirs(m_savedir);
3034 std::string fullpath = m_savedir + "/map_meta.txt";
3035 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3036 if(os.good() == false)
3038 dstream<<"ERROR: ServerMap::saveMapMeta(): "
3039 <<"could not open"<<fullpath<<std::endl;
3040 throw FileNotGoodException("Cannot open chunk metadata");
3044 params.setU64("seed", m_seed);
3046 params.writeLines(os);
3048 os<<"[end_of_params]\n";
3050 m_map_metadata_changed = false;
3053 void ServerMap::loadMapMeta()
3055 DSTACK(__FUNCTION_NAME);
3057 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
3060 std::string fullpath = m_savedir + "/map_meta.txt";
3061 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3062 if(is.good() == false)
3064 dstream<<"ERROR: ServerMap::loadMapMeta(): "
3065 <<"could not open"<<fullpath<<std::endl;
3066 throw FileNotGoodException("Cannot open map metadata");
3074 throw SerializationError
3075 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3077 std::getline(is, line);
3078 std::string trimmedline = trim(line);
3079 if(trimmedline == "[end_of_params]")
3081 params.parseConfigLine(line);
3084 m_seed = params.getU64("seed");
3086 dstream<<"INFO: ServerMap::loadMapMeta(): "
3091 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3093 DSTACK(__FUNCTION_NAME);
3094 // Format used for writing
3095 u8 version = SER_FMT_VER_HIGHEST;
3097 v2s16 pos = sector->getPos();
3098 std::string dir = getSectorDir(pos);
3101 std::string fullpath = dir + "/meta";
3102 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3103 if(o.good() == false)
3104 throw FileNotGoodException("Cannot open sector metafile");
3106 sector->serialize(o, version);
3108 sector->differs_from_disk = false;
3111 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3113 DSTACK(__FUNCTION_NAME);
3115 v2s16 p2d = getSectorPos(sectordir);
3117 ServerMapSector *sector = NULL;
3119 std::string fullpath = sectordir + "/meta";
3120 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3121 if(is.good() == false)
3123 // If the directory exists anyway, it probably is in some old
3124 // format. Just go ahead and create the sector.
3125 if(fs::PathExists(sectordir))
3127 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
3128 <<fullpath<<" doesn't exist but directory does."
3129 <<" Continuing with a sector with no metadata."
3131 sector = new ServerMapSector(this, p2d);
3132 m_sectors.insert(p2d, sector);
3136 throw FileNotGoodException("Cannot open sector metafile");
3141 sector = ServerMapSector::deSerialize
3142 (is, this, p2d, m_sectors);
3144 saveSectorMeta(sector);
3147 sector->differs_from_disk = false;
3152 bool ServerMap::loadSectorMeta(v2s16 p2d)
3154 DSTACK(__FUNCTION_NAME);
3156 MapSector *sector = NULL;
3158 // The directory layout we're going to load from.
3159 // 1 - original sectors/xxxxzzzz/
3160 // 2 - new sectors2/xxx/zzz/
3161 // If we load from anything but the latest structure, we will
3162 // immediately save to the new one, and remove the old.
3164 std::string sectordir1 = getSectorDir(p2d, 1);
3165 std::string sectordir;
3166 if(fs::PathExists(sectordir1))
3168 sectordir = sectordir1;
3173 sectordir = getSectorDir(p2d, 2);
3177 sector = loadSectorMeta(sectordir, loadlayout != 2);
3179 catch(InvalidFilenameException &e)
3183 catch(FileNotGoodException &e)
3187 catch(std::exception &e)
3196 bool ServerMap::loadSectorFull(v2s16 p2d)
3198 DSTACK(__FUNCTION_NAME);
3200 MapSector *sector = NULL;
3202 // The directory layout we're going to load from.
3203 // 1 - original sectors/xxxxzzzz/
3204 // 2 - new sectors2/xxx/zzz/
3205 // If we load from anything but the latest structure, we will
3206 // immediately save to the new one, and remove the old.
3208 std::string sectordir1 = getSectorDir(p2d, 1);
3209 std::string sectordir;
3210 if(fs::PathExists(sectordir1))
3212 sectordir = sectordir1;
3217 sectordir = getSectorDir(p2d, 2);
3221 sector = loadSectorMeta(sectordir, loadlayout != 2);
3223 catch(InvalidFilenameException &e)
3227 catch(FileNotGoodException &e)
3231 catch(std::exception &e)
3239 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3241 std::vector<fs::DirListNode>::iterator i2;
3242 for(i2=list2.begin(); i2!=list2.end(); i2++)
3248 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3250 catch(InvalidFilenameException &e)
3252 // This catches unknown crap in directory
3258 dstream<<"Sector converted to new layout - deleting "<<
3259 sectordir1<<std::endl;
3260 fs::RecursiveDelete(sectordir1);
3267 void ServerMap::beginSave() {
3269 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3270 dstream<<"WARNING: beginSave() failed, saving might be slow.";
3273 void ServerMap::endSave() {
3275 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3276 dstream<<"WARNING: endSave() failed, map might not have saved.";
3279 void ServerMap::saveBlock(MapBlock *block)
3281 DSTACK(__FUNCTION_NAME);
3283 Dummy blocks are not written
3285 if(block->isDummy())
3287 /*v3s16 p = block->getPos();
3288 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3289 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3293 // Format used for writing
3294 u8 version = SER_FMT_VER_HIGHEST;
3296 v3s16 p3d = block->getPos();
3300 v2s16 p2d(p3d.X, p3d.Z);
3301 std::string sectordir = getSectorDir(p2d);
3303 createDirs(sectordir);
3305 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3306 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3307 if(o.good() == false)
3308 throw FileNotGoodException("Cannot open block data");
3311 [0] u8 serialization version
3317 std::ostringstream o(std::ios_base::binary);
3319 o.write((char*)&version, 1);
3322 block->serialize(o, version);
3324 // Write extra data stored on disk
3325 block->serializeDiskExtra(o, version);
3327 // Write block to database
3329 std::string tmp = o.str();
3330 const char *bytes = tmp.c_str();
3332 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3333 dstream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3334 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3335 dstream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3336 int written = sqlite3_step(m_database_write);
3337 if(written != SQLITE_DONE)
3338 dstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3339 <<sqlite3_errmsg(m_database)<<std::endl;
3340 // Make ready for later reuse
3341 sqlite3_reset(m_database_write);
3343 // We just wrote it to the disk so clear modified flag
3344 block->resetModified();
3347 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3349 DSTACK(__FUNCTION_NAME);
3351 std::string fullpath = sectordir+"/"+blockfile;
3354 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3355 if(is.good() == false)
3356 throw FileNotGoodException("Cannot open block file");
3358 v3s16 p3d = getBlockPos(sectordir, blockfile);
3359 v2s16 p2d(p3d.X, p3d.Z);
3361 assert(sector->getPos() == p2d);
3363 u8 version = SER_FMT_VER_INVALID;
3364 is.read((char*)&version, 1);
3367 throw SerializationError("ServerMap::loadBlock(): Failed"
3368 " to read MapBlock version");
3370 /*u32 block_size = MapBlock::serializedLength(version);
3371 SharedBuffer<u8> data(block_size);
3372 is.read((char*)*data, block_size);*/
3374 // This will always return a sector because we're the server
3375 //MapSector *sector = emergeSector(p2d);
3377 MapBlock *block = NULL;
3378 bool created_new = false;
3379 block = sector->getBlockNoCreateNoEx(p3d.Y);
3382 block = sector->createBlankBlockNoInsert(p3d.Y);
3387 block->deSerialize(is, version);
3389 // Read extra data stored on disk
3390 block->deSerializeDiskExtra(is, version);
3392 // If it's a new block, insert it to the map
3394 sector->insertBlock(block);
3397 Save blocks loaded in old format in new format
3400 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3404 // Should be in database now, so delete the old file
3405 fs::RecursiveDelete(fullpath);
3408 // We just loaded it from the disk, so it's up-to-date.
3409 block->resetModified();
3412 catch(SerializationError &e)
3414 dstream<<"WARNING: Invalid block data on disk "
3415 <<"fullpath="<<fullpath
3416 <<" (SerializationError). "
3417 <<"what()="<<e.what()
3419 //" Ignoring. A new one will be generated.
3422 // TODO: Backup file; name is in fullpath.
3426 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3428 DSTACK(__FUNCTION_NAME);
3431 std::istringstream is(*blob, std::ios_base::binary);
3433 u8 version = SER_FMT_VER_INVALID;
3434 is.read((char*)&version, 1);
3437 throw SerializationError("ServerMap::loadBlock(): Failed"
3438 " to read MapBlock version");
3440 /*u32 block_size = MapBlock::serializedLength(version);
3441 SharedBuffer<u8> data(block_size);
3442 is.read((char*)*data, block_size);*/
3444 // This will always return a sector because we're the server
3445 //MapSector *sector = emergeSector(p2d);
3447 MapBlock *block = NULL;
3448 bool created_new = false;
3449 block = sector->getBlockNoCreateNoEx(p3d.Y);
3452 block = sector->createBlankBlockNoInsert(p3d.Y);
3457 block->deSerialize(is, version);
3459 // Read extra data stored on disk
3460 block->deSerializeDiskExtra(is, version);
3462 // If it's a new block, insert it to the map
3464 sector->insertBlock(block);
3467 Save blocks loaded in old format in new format
3470 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3475 // We just loaded it from, so it's up-to-date.
3476 block->resetModified();
3479 catch(SerializationError &e)
3481 dstream<<"WARNING: Invalid block data in database "
3482 <<" (SerializationError). "
3483 <<"what()="<<e.what()
3485 //" Ignoring. A new one will be generated.
3488 // TODO: Copy to a backup database.
3492 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3494 DSTACK(__FUNCTION_NAME);
3496 v2s16 p2d(blockpos.X, blockpos.Z);
3498 if(!loadFromFolders()) {
3501 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3502 dstream<<"WARNING: Could not bind block position for load: "
3503 <<sqlite3_errmsg(m_database)<<std::endl;
3504 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3506 Make sure sector is loaded
3508 MapSector *sector = createSector(p2d);
3513 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3514 size_t len = sqlite3_column_bytes(m_database_read, 0);
3516 std::string datastr(data, len);
3518 loadBlock(&datastr, blockpos, sector, false);
3520 sqlite3_step(m_database_read);
3521 // We should never get more than 1 row, so ok to reset
3522 sqlite3_reset(m_database_read);
3524 return getBlockNoCreateNoEx(blockpos);
3526 sqlite3_reset(m_database_read);
3528 // Not found in database, try the files
3531 // The directory layout we're going to load from.
3532 // 1 - original sectors/xxxxzzzz/
3533 // 2 - new sectors2/xxx/zzz/
3534 // If we load from anything but the latest structure, we will
3535 // immediately save to the new one, and remove the old.
3537 std::string sectordir1 = getSectorDir(p2d, 1);
3538 std::string sectordir;
3539 if(fs::PathExists(sectordir1))
3541 sectordir = sectordir1;
3546 sectordir = getSectorDir(p2d, 2);
3550 Make sure sector is loaded
3552 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3556 sector = loadSectorMeta(sectordir, loadlayout != 2);
3558 catch(InvalidFilenameException &e)
3562 catch(FileNotGoodException &e)
3566 catch(std::exception &e)
3573 Make sure file exists
3576 std::string blockfilename = getBlockFilename(blockpos);
3577 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3581 Load block and save it to the database
3583 loadBlock(sectordir, blockfilename, sector, true);
3584 return getBlockNoCreateNoEx(blockpos);
3587 void ServerMap::PrintInfo(std::ostream &out)
3598 ClientMap::ClientMap(
3600 MapDrawControl &control,
3601 scene::ISceneNode* parent,
3602 scene::ISceneManager* mgr,
3606 scene::ISceneNode(parent, mgr, id),
3609 m_camera_position(0,0,0),
3610 m_camera_direction(0,0,1),
3613 m_camera_mutex.Init();
3614 assert(m_camera_mutex.IsInitialized());
3616 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3617 BS*1000000,BS*1000000,BS*1000000);
3620 ClientMap::~ClientMap()
3622 /*JMutexAutoLock lock(mesh_mutex);
3631 MapSector * ClientMap::emergeSector(v2s16 p2d)
3633 DSTACK(__FUNCTION_NAME);
3634 // Check that it doesn't exist already
3636 return getSectorNoGenerate(p2d);
3638 catch(InvalidPositionException &e)
3643 ClientMapSector *sector = new ClientMapSector(this, p2d);
3646 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3647 m_sectors.insert(p2d, sector);
3654 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3656 DSTACK(__FUNCTION_NAME);
3657 ClientMapSector *sector = NULL;
3659 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3661 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3665 sector = (ClientMapSector*)n->getValue();
3666 assert(sector->getId() == MAPSECTOR_CLIENT);
3670 sector = new ClientMapSector(this, p2d);
3672 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3673 m_sectors.insert(p2d, sector);
3677 sector->deSerialize(is);
3681 void ClientMap::OnRegisterSceneNode()
3685 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3686 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3689 ISceneNode::OnRegisterSceneNode();
3692 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3694 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3695 DSTACK(__FUNCTION_NAME);
3697 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3700 This is called two times per frame, reset on the non-transparent one
3702 if(pass == scene::ESNRP_SOLID)
3704 m_last_drawn_sectors.clear();
3708 Get time for measuring timeout.
3710 Measuring time is very useful for long delays when the
3711 machine is swapping a lot.
3713 int time1 = time(0);
3715 //u32 daynight_ratio = m_client->getDayNightRatio();
3717 m_camera_mutex.Lock();
3718 v3f camera_position = m_camera_position;
3719 v3f camera_direction = m_camera_direction;
3720 f32 camera_fov = m_camera_fov;
3721 m_camera_mutex.Unlock();
3724 Get all blocks and draw all visible ones
3727 v3s16 cam_pos_nodes(
3728 camera_position.X / BS,
3729 camera_position.Y / BS,
3730 camera_position.Z / BS);
3732 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3734 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3735 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3737 // Take a fair amount as we will be dropping more out later
3739 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3740 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3741 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3743 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3744 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3745 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3747 u32 vertex_count = 0;
3749 // For limiting number of mesh updates per frame
3750 u32 mesh_update_count = 0;
3752 u32 blocks_would_have_drawn = 0;
3753 u32 blocks_drawn = 0;
3755 int timecheck_counter = 0;
3756 core::map<v2s16, MapSector*>::Iterator si;
3757 si = m_sectors.getIterator();
3758 for(; si.atEnd() == false; si++)
3761 timecheck_counter++;
3762 if(timecheck_counter > 50)
3764 timecheck_counter = 0;
3765 int time2 = time(0);
3766 if(time2 > time1 + 4)
3768 dstream<<"ClientMap::renderMap(): "
3769 "Rendering takes ages, returning."
3776 MapSector *sector = si.getNode()->getValue();
3777 v2s16 sp = sector->getPos();
3779 if(m_control.range_all == false)
3781 if(sp.X < p_blocks_min.X
3782 || sp.X > p_blocks_max.X
3783 || sp.Y < p_blocks_min.Z
3784 || sp.Y > p_blocks_max.Z)
3788 core::list< MapBlock * > sectorblocks;
3789 sector->getBlocks(sectorblocks);
3795 u32 sector_blocks_drawn = 0;
3797 core::list< MapBlock * >::Iterator i;
3798 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3800 MapBlock *block = *i;
3803 Compare block position to camera position, skip
3804 if not seen on display
3807 float range = 100000 * BS;
3808 if(m_control.range_all == false)
3809 range = m_control.wanted_range * BS;
3812 if(isBlockInSight(block->getPos(), camera_position,
3813 camera_direction, camera_fov,
3814 range, &d) == false)
3819 // Okay, this block will be drawn. Reset usage timer.
3820 block->resetUsageTimer();
3822 // This is ugly (spherical distance limit?)
3823 /*if(m_control.range_all == false &&
3824 d - 0.5*BS*MAP_BLOCKSIZE > range)
3829 Update expired mesh (used for day/night change)
3831 It doesn't work exactly like it should now with the
3832 tasked mesh update but whatever.
3835 bool mesh_expired = false;
3838 JMutexAutoLock lock(block->mesh_mutex);
3840 mesh_expired = block->getMeshExpired();
3842 // Mesh has not been expired and there is no mesh:
3843 // block has no content
3844 if(block->mesh == NULL && mesh_expired == false)
3848 f32 faraway = BS*50;
3849 //f32 faraway = m_control.wanted_range * BS;
3852 This has to be done with the mesh_mutex unlocked
3854 // Pretty random but this should work somewhat nicely
3855 if(mesh_expired && (
3856 (mesh_update_count < 3
3857 && (d < faraway || mesh_update_count < 2)
3860 (m_control.range_all && mesh_update_count < 20)
3863 /*if(mesh_expired && mesh_update_count < 6
3864 && (d < faraway || mesh_update_count < 3))*/
3866 mesh_update_count++;
3868 // Mesh has been expired: generate new mesh
3869 //block->updateMesh(daynight_ratio);
3870 m_client->addUpdateMeshTask(block->getPos());
3872 mesh_expired = false;
3877 Draw the faces of the block
3880 JMutexAutoLock lock(block->mesh_mutex);
3882 scene::SMesh *mesh = block->mesh;
3887 blocks_would_have_drawn++;
3888 if(blocks_drawn >= m_control.wanted_max_blocks
3889 && m_control.range_all == false
3890 && d > m_control.wanted_min_range * BS)
3894 sector_blocks_drawn++;
3896 u32 c = mesh->getMeshBufferCount();
3898 for(u32 i=0; i<c; i++)
3900 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3901 const video::SMaterial& material = buf->getMaterial();
3902 video::IMaterialRenderer* rnd =
3903 driver->getMaterialRenderer(material.MaterialType);
3904 bool transparent = (rnd && rnd->isTransparent());
3905 // Render transparent on transparent pass and likewise.
3906 if(transparent == is_transparent_pass)
3909 This *shouldn't* hurt too much because Irrlicht
3910 doesn't change opengl textures if the old
3911 material is set again.
3913 driver->setMaterial(buf->getMaterial());
3914 driver->drawMeshBuffer(buf);
3915 vertex_count += buf->getVertexCount();
3919 } // foreach sectorblocks
3921 if(sector_blocks_drawn != 0)
3923 m_last_drawn_sectors[sp] = true;
3927 m_control.blocks_drawn = blocks_drawn;
3928 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3930 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3931 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3934 void ClientMap::renderPostFx()
3936 // Sadly ISceneManager has no "post effects" render pass, in that case we
3937 // could just register for that and handle it in renderMap().
3939 m_camera_mutex.Lock();
3940 v3f camera_position = m_camera_position;
3941 m_camera_mutex.Unlock();
3943 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
3945 // - If the player is in a solid node, make everything black.
3946 // - If the player is in liquid, draw a semi-transparent overlay.
3947 ContentFeatures& features = content_features(n);
3948 video::SColor post_effect_color = features.post_effect_color;
3949 if(features.solidness == 2 && g_settings.getBool("free_move") == false)
3951 post_effect_color = video::SColor(255, 0, 0, 0);
3953 if (post_effect_color.getAlpha() != 0)
3955 // Draw a full-screen rectangle
3956 video::IVideoDriver* driver = SceneManager->getVideoDriver();
3957 v2u32 ss = driver->getScreenSize();
3958 core::rect<s32> rect(0,0, ss.X, ss.Y);
3959 driver->draw2DRectangle(post_effect_color, rect);
3963 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3964 core::map<v3s16, MapBlock*> *affected_blocks)
3966 bool changed = false;
3968 Add it to all blocks touching it
3971 v3s16(0,0,0), // this
3972 v3s16(0,0,1), // back
3973 v3s16(0,1,0), // top
3974 v3s16(1,0,0), // right
3975 v3s16(0,0,-1), // front
3976 v3s16(0,-1,0), // bottom
3977 v3s16(-1,0,0), // left
3979 for(u16 i=0; i<7; i++)
3981 v3s16 p2 = p + dirs[i];
3982 // Block position of neighbor (or requested) node
3983 v3s16 blockpos = getNodeBlockPos(p2);
3984 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3985 if(blockref == NULL)
3987 // Relative position of requested node
3988 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3989 if(blockref->setTempMod(relpos, mod))
3994 if(changed && affected_blocks!=NULL)
3996 for(u16 i=0; i<7; i++)
3998 v3s16 p2 = p + dirs[i];
3999 // Block position of neighbor (or requested) node
4000 v3s16 blockpos = getNodeBlockPos(p2);
4001 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4002 if(blockref == NULL)
4004 affected_blocks->insert(blockpos, blockref);
4010 bool ClientMap::clearTempMod(v3s16 p,
4011 core::map<v3s16, MapBlock*> *affected_blocks)
4013 bool changed = false;
4015 v3s16(0,0,0), // this
4016 v3s16(0,0,1), // back
4017 v3s16(0,1,0), // top
4018 v3s16(1,0,0), // right
4019 v3s16(0,0,-1), // front
4020 v3s16(0,-1,0), // bottom
4021 v3s16(-1,0,0), // left
4023 for(u16 i=0; i<7; i++)
4025 v3s16 p2 = p + dirs[i];
4026 // Block position of neighbor (or requested) node
4027 v3s16 blockpos = getNodeBlockPos(p2);
4028 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4029 if(blockref == NULL)
4031 // Relative position of requested node
4032 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4033 if(blockref->clearTempMod(relpos))
4038 if(changed && affected_blocks!=NULL)
4040 for(u16 i=0; i<7; i++)
4042 v3s16 p2 = p + dirs[i];
4043 // Block position of neighbor (or requested) node
4044 v3s16 blockpos = getNodeBlockPos(p2);
4045 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4046 if(blockref == NULL)
4048 affected_blocks->insert(blockpos, blockref);
4054 void ClientMap::expireMeshes(bool only_daynight_diffed)
4056 TimeTaker timer("expireMeshes()");
4058 core::map<v2s16, MapSector*>::Iterator si;
4059 si = m_sectors.getIterator();
4060 for(; si.atEnd() == false; si++)
4062 MapSector *sector = si.getNode()->getValue();
4064 core::list< MapBlock * > sectorblocks;
4065 sector->getBlocks(sectorblocks);
4067 core::list< MapBlock * >::Iterator i;
4068 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4070 MapBlock *block = *i;
4072 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4078 JMutexAutoLock lock(block->mesh_mutex);
4079 if(block->mesh != NULL)
4081 /*block->mesh->drop();
4082 block->mesh = NULL;*/
4083 block->setMeshExpired(true);
4090 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4092 assert(mapType() == MAPTYPE_CLIENT);
4095 v3s16 p = blockpos + v3s16(0,0,0);
4096 MapBlock *b = getBlockNoCreate(p);
4097 b->updateMesh(daynight_ratio);
4098 //b->setMeshExpired(true);
4100 catch(InvalidPositionException &e){}
4103 v3s16 p = blockpos + v3s16(-1,0,0);
4104 MapBlock *b = getBlockNoCreate(p);
4105 b->updateMesh(daynight_ratio);
4106 //b->setMeshExpired(true);
4108 catch(InvalidPositionException &e){}
4110 v3s16 p = blockpos + v3s16(0,-1,0);
4111 MapBlock *b = getBlockNoCreate(p);
4112 b->updateMesh(daynight_ratio);
4113 //b->setMeshExpired(true);
4115 catch(InvalidPositionException &e){}
4117 v3s16 p = blockpos + v3s16(0,0,-1);
4118 MapBlock *b = getBlockNoCreate(p);
4119 b->updateMesh(daynight_ratio);
4120 //b->setMeshExpired(true);
4122 catch(InvalidPositionException &e){}
4127 Update mesh of block in which the node is, and if the node is at the
4128 leading edge, update the appropriate leading blocks too.
4130 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4138 v3s16 blockposes[4];
4139 for(u32 i=0; i<4; i++)
4141 v3s16 np = nodepos + dirs[i];
4142 blockposes[i] = getNodeBlockPos(np);
4143 // Don't update mesh of block if it has been done already
4144 bool already_updated = false;
4145 for(u32 j=0; j<i; j++)
4147 if(blockposes[j] == blockposes[i])
4149 already_updated = true;
4156 MapBlock *b = getBlockNoCreate(blockposes[i]);
4157 b->updateMesh(daynight_ratio);
4162 void ClientMap::PrintInfo(std::ostream &out)
4173 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4178 MapVoxelManipulator::~MapVoxelManipulator()
4180 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4184 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4186 TimeTaker timer1("emerge", &emerge_time);
4188 // Units of these are MapBlocks
4189 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4190 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4192 VoxelArea block_area_nodes
4193 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4195 addArea(block_area_nodes);
4197 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4198 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4199 for(s32 x=p_min.X; x<=p_max.X; x++)
4202 core::map<v3s16, bool>::Node *n;
4203 n = m_loaded_blocks.find(p);
4207 bool block_data_inexistent = false;
4210 TimeTaker timer1("emerge load", &emerge_load_time);
4212 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4213 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4216 dstream<<std::endl;*/
4218 MapBlock *block = m_map->getBlockNoCreate(p);
4219 if(block->isDummy())
4220 block_data_inexistent = true;
4222 block->copyTo(*this);
4224 catch(InvalidPositionException &e)
4226 block_data_inexistent = true;
4229 if(block_data_inexistent)
4231 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4232 // Fill with VOXELFLAG_INEXISTENT
4233 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4234 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4236 s32 i = m_area.index(a.MinEdge.X,y,z);
4237 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4241 m_loaded_blocks.insert(p, !block_data_inexistent);
4244 //dstream<<"emerge done"<<std::endl;
4248 SUGG: Add an option to only update eg. water and air nodes.
4249 This will make it interfere less with important stuff if
4252 void MapVoxelManipulator::blitBack
4253 (core::map<v3s16, MapBlock*> & modified_blocks)
4255 if(m_area.getExtent() == v3s16(0,0,0))
4258 //TimeTaker timer1("blitBack");
4260 /*dstream<<"blitBack(): m_loaded_blocks.size()="
4261 <<m_loaded_blocks.size()<<std::endl;*/
4264 Initialize block cache
4266 v3s16 blockpos_last;
4267 MapBlock *block = NULL;
4268 bool block_checked_in_modified = false;
4270 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4271 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4272 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4276 u8 f = m_flags[m_area.index(p)];
4277 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4280 MapNode &n = m_data[m_area.index(p)];
4282 v3s16 blockpos = getNodeBlockPos(p);
4287 if(block == NULL || blockpos != blockpos_last){
4288 block = m_map->getBlockNoCreate(blockpos);
4289 blockpos_last = blockpos;
4290 block_checked_in_modified = false;
4293 // Calculate relative position in block
4294 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4296 // Don't continue if nothing has changed here
4297 if(block->getNode(relpos) == n)
4300 //m_map->setNode(m_area.MinEdge + p, n);
4301 block->setNode(relpos, n);
4304 Make sure block is in modified_blocks
4306 if(block_checked_in_modified == false)
4308 modified_blocks[blockpos] = block;
4309 block_checked_in_modified = true;
4312 catch(InvalidPositionException &e)
4318 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4319 MapVoxelManipulator(map),
4320 m_create_area(false)
4324 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4328 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4330 // Just create the area so that it can be pointed to
4331 VoxelManipulator::emerge(a, caller_id);
4334 void ManualMapVoxelManipulator::initialEmerge(
4335 v3s16 blockpos_min, v3s16 blockpos_max)
4337 TimeTaker timer1("initialEmerge", &emerge_time);
4339 // Units of these are MapBlocks
4340 v3s16 p_min = blockpos_min;
4341 v3s16 p_max = blockpos_max;
4343 VoxelArea block_area_nodes
4344 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4346 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4349 dstream<<"initialEmerge: area: ";
4350 block_area_nodes.print(dstream);
4351 dstream<<" ("<<size_MB<<"MB)";
4355 addArea(block_area_nodes);
4357 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4358 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4359 for(s32 x=p_min.X; x<=p_max.X; x++)
4362 core::map<v3s16, bool>::Node *n;
4363 n = m_loaded_blocks.find(p);
4367 bool block_data_inexistent = false;
4370 TimeTaker timer1("emerge load", &emerge_load_time);
4372 MapBlock *block = m_map->getBlockNoCreate(p);
4373 if(block->isDummy())
4374 block_data_inexistent = true;
4376 block->copyTo(*this);
4378 catch(InvalidPositionException &e)
4380 block_data_inexistent = true;
4383 if(block_data_inexistent)
4386 Mark area inexistent
4388 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4389 // Fill with VOXELFLAG_INEXISTENT
4390 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4391 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4393 s32 i = m_area.index(a.MinEdge.X,y,z);
4394 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4398 m_loaded_blocks.insert(p, !block_data_inexistent);
4402 void ManualMapVoxelManipulator::blitBackAll(
4403 core::map<v3s16, MapBlock*> * modified_blocks)
4405 if(m_area.getExtent() == v3s16(0,0,0))
4409 Copy data of all blocks
4411 for(core::map<v3s16, bool>::Iterator
4412 i = m_loaded_blocks.getIterator();
4413 i.atEnd() == false; i++)
4415 v3s16 p = i.getNode()->getKey();
4416 bool existed = i.getNode()->getValue();
4417 if(existed == false)
4419 // The Great Bug was found using this
4420 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4421 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4425 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4428 dstream<<"WARNING: "<<__FUNCTION_NAME
4429 <<": got NULL block "
4430 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4435 block->copyFrom(*this);
4438 modified_blocks->insert(p, block);