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"
33 SQLite format specification:
34 - Initially only replaces sectors/ and sectors2/
36 If map.sqlite does not exist in the save dir
37 or the block was not found in the database
38 the map will try to load from sectors folder.
39 In either case, map.sqlite will be created
40 and all future saves will save there.
42 Structure of map.sqlite:
53 Map::Map(std::ostream &dout):
57 /*m_sector_mutex.Init();
58 assert(m_sector_mutex.IsInitialized());*/
66 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
67 for(; i.atEnd() == false; i++)
69 MapSector *sector = i.getNode()->getValue();
74 void Map::addEventReceiver(MapEventReceiver *event_receiver)
76 m_event_receivers.insert(event_receiver, false);
79 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
81 if(m_event_receivers.find(event_receiver) == NULL)
83 m_event_receivers.remove(event_receiver);
86 void Map::dispatchEvent(MapEditEvent *event)
88 for(core::map<MapEventReceiver*, bool>::Iterator
89 i = m_event_receivers.getIterator();
90 i.atEnd()==false; i++)
92 MapEventReceiver* event_receiver = i.getNode()->getKey();
93 event_receiver->onMapEditEvent(event);
97 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
99 if(m_sector_cache != NULL && p == m_sector_cache_p){
100 MapSector * sector = m_sector_cache;
104 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
109 MapSector *sector = n->getValue();
111 // Cache the last result
112 m_sector_cache_p = p;
113 m_sector_cache = sector;
118 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
120 return getSectorNoGenerateNoExNoLock(p);
123 MapSector * Map::getSectorNoGenerate(v2s16 p)
125 MapSector *sector = getSectorNoGenerateNoEx(p);
127 throw InvalidPositionException();
132 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
134 v2s16 p2d(p3d.X, p3d.Z);
135 MapSector * sector = getSectorNoGenerateNoEx(p2d);
138 MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
142 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
144 MapBlock *block = getBlockNoCreateNoEx(p3d);
146 throw InvalidPositionException();
150 bool Map::isNodeUnderground(v3s16 p)
152 v3s16 blockpos = getNodeBlockPos(p);
154 MapBlock * block = getBlockNoCreate(blockpos);
155 return block->getIsUnderground();
157 catch(InvalidPositionException &e)
163 bool Map::isValidPosition(v3s16 p)
165 v3s16 blockpos = getNodeBlockPos(p);
166 MapBlock *block = getBlockNoCreate(blockpos);
167 return (block != NULL);
170 // Returns a CONTENT_IGNORE node if not found
171 MapNode Map::getNodeNoEx(v3s16 p)
173 v3s16 blockpos = getNodeBlockPos(p);
174 MapBlock *block = getBlockNoCreateNoEx(blockpos);
176 return MapNode(CONTENT_IGNORE);
177 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
178 return block->getNodeNoCheck(relpos);
181 // throws InvalidPositionException if not found
182 MapNode Map::getNode(v3s16 p)
184 v3s16 blockpos = getNodeBlockPos(p);
185 MapBlock *block = getBlockNoCreateNoEx(blockpos);
187 throw InvalidPositionException();
188 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
189 return block->getNodeNoCheck(relpos);
192 // throws InvalidPositionException if not found
193 void Map::setNode(v3s16 p, MapNode & n)
195 v3s16 blockpos = getNodeBlockPos(p);
196 MapBlock *block = getBlockNoCreate(blockpos);
197 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
198 block->setNodeNoCheck(relpos, n);
203 Goes recursively through the neighbours of the node.
205 Alters only transparent nodes.
207 If the lighting of the neighbour is lower than the lighting of
208 the node was (before changing it to 0 at the step before), the
209 lighting of the neighbour is set to 0 and then the same stuff
210 repeats for the neighbour.
212 The ending nodes of the routine are stored in light_sources.
213 This is useful when a light is removed. In such case, this
214 routine can be called for the light node and then again for
215 light_sources to re-light the area without the removed light.
217 values of from_nodes are lighting values.
219 void Map::unspreadLight(enum LightBank bank,
220 core::map<v3s16, u8> & from_nodes,
221 core::map<v3s16, bool> & light_sources,
222 core::map<v3s16, MapBlock*> & modified_blocks)
225 v3s16(0,0,1), // back
227 v3s16(1,0,0), // right
228 v3s16(0,0,-1), // front
229 v3s16(0,-1,0), // bottom
230 v3s16(-1,0,0), // left
233 if(from_nodes.size() == 0)
236 u32 blockchangecount = 0;
238 core::map<v3s16, u8> unlighted_nodes;
239 core::map<v3s16, u8>::Iterator j;
240 j = from_nodes.getIterator();
243 Initialize block cache
246 MapBlock *block = NULL;
247 // Cache this a bit, too
248 bool block_checked_in_modified = false;
250 for(; j.atEnd() == false; j++)
252 v3s16 pos = j.getNode()->getKey();
253 v3s16 blockpos = getNodeBlockPos(pos);
255 // Only fetch a new block if the block position has changed
257 if(block == NULL || blockpos != blockpos_last){
258 block = getBlockNoCreate(blockpos);
259 blockpos_last = blockpos;
261 block_checked_in_modified = false;
265 catch(InvalidPositionException &e)
273 // Calculate relative position in block
274 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
276 // Get node straight from the block
277 MapNode n = block->getNode(relpos);
279 u8 oldlight = j.getNode()->getValue();
281 // Loop through 6 neighbors
282 for(u16 i=0; i<6; i++)
284 // Get the position of the neighbor node
285 v3s16 n2pos = pos + dirs[i];
287 // Get the block where the node is located
288 v3s16 blockpos = getNodeBlockPos(n2pos);
292 // Only fetch a new block if the block position has changed
294 if(block == NULL || blockpos != blockpos_last){
295 block = getBlockNoCreate(blockpos);
296 blockpos_last = blockpos;
298 block_checked_in_modified = false;
302 catch(InvalidPositionException &e)
307 // Calculate relative position in block
308 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
309 // Get node straight from the block
310 MapNode n2 = block->getNode(relpos);
312 bool changed = false;
314 //TODO: Optimize output by optimizing light_sources?
317 If the neighbor is dimmer than what was specified
318 as oldlight (the light of the previous node)
320 if(n2.getLight(bank) < oldlight)
323 And the neighbor is transparent and it has some light
325 if(n2.light_propagates() && n2.getLight(bank) != 0)
328 Set light to 0 and add to queue
331 u8 current_light = n2.getLight(bank);
332 n2.setLight(bank, 0);
333 block->setNode(relpos, n2);
335 unlighted_nodes.insert(n2pos, current_light);
339 Remove from light_sources if it is there
340 NOTE: This doesn't happen nearly at all
342 /*if(light_sources.find(n2pos))
344 std::cout<<"Removed from light_sources"<<std::endl;
345 light_sources.remove(n2pos);
350 if(light_sources.find(n2pos) != NULL)
351 light_sources.remove(n2pos);*/
354 light_sources.insert(n2pos, true);
357 // Add to modified_blocks
358 if(changed == true && block_checked_in_modified == false)
360 // If the block is not found in modified_blocks, add.
361 if(modified_blocks.find(blockpos) == NULL)
363 modified_blocks.insert(blockpos, block);
365 block_checked_in_modified = true;
368 catch(InvalidPositionException &e)
375 /*dstream<<"unspreadLight(): Changed block "
376 <<blockchangecount<<" times"
377 <<" for "<<from_nodes.size()<<" nodes"
380 if(unlighted_nodes.size() > 0)
381 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
385 A single-node wrapper of the above
387 void Map::unLightNeighbors(enum LightBank bank,
388 v3s16 pos, u8 lightwas,
389 core::map<v3s16, bool> & light_sources,
390 core::map<v3s16, MapBlock*> & modified_blocks)
392 core::map<v3s16, u8> from_nodes;
393 from_nodes.insert(pos, lightwas);
395 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
399 Lights neighbors of from_nodes, collects all them and then
402 void Map::spreadLight(enum LightBank bank,
403 core::map<v3s16, bool> & from_nodes,
404 core::map<v3s16, MapBlock*> & modified_blocks)
406 const v3s16 dirs[6] = {
407 v3s16(0,0,1), // back
409 v3s16(1,0,0), // right
410 v3s16(0,0,-1), // front
411 v3s16(0,-1,0), // bottom
412 v3s16(-1,0,0), // left
415 if(from_nodes.size() == 0)
418 u32 blockchangecount = 0;
420 core::map<v3s16, bool> lighted_nodes;
421 core::map<v3s16, bool>::Iterator j;
422 j = from_nodes.getIterator();
425 Initialize block cache
428 MapBlock *block = NULL;
429 // Cache this a bit, too
430 bool block_checked_in_modified = false;
432 for(; j.atEnd() == false; j++)
433 //for(; j != from_nodes.end(); j++)
435 v3s16 pos = j.getNode()->getKey();
437 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
438 v3s16 blockpos = getNodeBlockPos(pos);
440 // Only fetch a new block if the block position has changed
442 if(block == NULL || blockpos != blockpos_last){
443 block = getBlockNoCreate(blockpos);
444 blockpos_last = blockpos;
446 block_checked_in_modified = false;
450 catch(InvalidPositionException &e)
458 // Calculate relative position in block
459 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
461 // Get node straight from the block
462 MapNode n = block->getNode(relpos);
464 u8 oldlight = n.getLight(bank);
465 u8 newlight = diminish_light(oldlight);
467 // Loop through 6 neighbors
468 for(u16 i=0; i<6; i++){
469 // Get the position of the neighbor node
470 v3s16 n2pos = pos + dirs[i];
472 // Get the block where the node is located
473 v3s16 blockpos = getNodeBlockPos(n2pos);
477 // Only fetch a new block if the block position has changed
479 if(block == NULL || blockpos != blockpos_last){
480 block = getBlockNoCreate(blockpos);
481 blockpos_last = blockpos;
483 block_checked_in_modified = false;
487 catch(InvalidPositionException &e)
492 // Calculate relative position in block
493 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
494 // Get node straight from the block
495 MapNode n2 = block->getNode(relpos);
497 bool changed = false;
499 If the neighbor is brighter than the current node,
500 add to list (it will light up this node on its turn)
502 if(n2.getLight(bank) > undiminish_light(oldlight))
504 lighted_nodes.insert(n2pos, true);
505 //lighted_nodes.push_back(n2pos);
509 If the neighbor is dimmer than how much light this node
510 would spread on it, add to list
512 if(n2.getLight(bank) < newlight)
514 if(n2.light_propagates())
516 n2.setLight(bank, newlight);
517 block->setNode(relpos, n2);
518 lighted_nodes.insert(n2pos, true);
519 //lighted_nodes.push_back(n2pos);
524 // Add to modified_blocks
525 if(changed == true && block_checked_in_modified == false)
527 // If the block is not found in modified_blocks, add.
528 if(modified_blocks.find(blockpos) == NULL)
530 modified_blocks.insert(blockpos, block);
532 block_checked_in_modified = true;
535 catch(InvalidPositionException &e)
542 /*dstream<<"spreadLight(): Changed block "
543 <<blockchangecount<<" times"
544 <<" for "<<from_nodes.size()<<" nodes"
547 if(lighted_nodes.size() > 0)
548 spreadLight(bank, lighted_nodes, modified_blocks);
552 A single-node source variation of the above.
554 void Map::lightNeighbors(enum LightBank bank,
556 core::map<v3s16, MapBlock*> & modified_blocks)
558 core::map<v3s16, bool> from_nodes;
559 from_nodes.insert(pos, true);
560 spreadLight(bank, from_nodes, modified_blocks);
563 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
566 v3s16(0,0,1), // back
568 v3s16(1,0,0), // right
569 v3s16(0,0,-1), // front
570 v3s16(0,-1,0), // bottom
571 v3s16(-1,0,0), // left
574 u8 brightest_light = 0;
575 v3s16 brightest_pos(0,0,0);
576 bool found_something = false;
578 // Loop through 6 neighbors
579 for(u16 i=0; i<6; i++){
580 // Get the position of the neighbor node
581 v3s16 n2pos = p + dirs[i];
586 catch(InvalidPositionException &e)
590 if(n2.getLight(bank) > brightest_light || found_something == false){
591 brightest_light = n2.getLight(bank);
592 brightest_pos = n2pos;
593 found_something = true;
597 if(found_something == false)
598 throw InvalidPositionException();
600 return brightest_pos;
604 Propagates sunlight down from a node.
605 Starting point gets sunlight.
607 Returns the lowest y value of where the sunlight went.
609 Mud is turned into grass in where the sunlight stops.
611 s16 Map::propagateSunlight(v3s16 start,
612 core::map<v3s16, MapBlock*> & modified_blocks)
617 v3s16 pos(start.X, y, start.Z);
619 v3s16 blockpos = getNodeBlockPos(pos);
622 block = getBlockNoCreate(blockpos);
624 catch(InvalidPositionException &e)
629 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
630 MapNode n = block->getNode(relpos);
632 if(n.sunlight_propagates())
634 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
635 block->setNode(relpos, n);
637 modified_blocks.insert(blockpos, block);
641 /*// Turn mud into grass
642 if(n.getContent() == CONTENT_MUD)
644 n.setContent(CONTENT_GRASS);
645 block->setNode(relpos, n);
646 modified_blocks.insert(blockpos, block);
649 // Sunlight goes no further
656 void Map::updateLighting(enum LightBank bank,
657 core::map<v3s16, MapBlock*> & a_blocks,
658 core::map<v3s16, MapBlock*> & modified_blocks)
660 /*m_dout<<DTIME<<"Map::updateLighting(): "
661 <<a_blocks.size()<<" blocks."<<std::endl;*/
663 //TimeTaker timer("updateLighting");
667 //u32 count_was = modified_blocks.size();
669 core::map<v3s16, MapBlock*> blocks_to_update;
671 core::map<v3s16, bool> light_sources;
673 core::map<v3s16, u8> unlight_from;
675 core::map<v3s16, MapBlock*>::Iterator i;
676 i = a_blocks.getIterator();
677 for(; i.atEnd() == false; i++)
679 MapBlock *block = i.getNode()->getValue();
683 // Don't bother with dummy blocks.
687 v3s16 pos = block->getPos();
688 modified_blocks.insert(pos, block);
690 blocks_to_update.insert(pos, block);
693 Clear all light from block
695 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
696 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
697 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
702 MapNode n = block->getNode(v3s16(x,y,z));
703 u8 oldlight = n.getLight(bank);
705 block->setNode(v3s16(x,y,z), n);
707 // Collect borders for unlighting
708 if(x==0 || x == MAP_BLOCKSIZE-1
709 || y==0 || y == MAP_BLOCKSIZE-1
710 || z==0 || z == MAP_BLOCKSIZE-1)
712 v3s16 p_map = p + v3s16(
715 MAP_BLOCKSIZE*pos.Z);
716 unlight_from.insert(p_map, oldlight);
719 catch(InvalidPositionException &e)
722 This would happen when dealing with a
726 dstream<<"updateLighting(): InvalidPositionException"
731 if(bank == LIGHTBANK_DAY)
733 bool bottom_valid = block->propagateSunlight(light_sources);
735 // If bottom is valid, we're done.
739 else if(bank == LIGHTBANK_NIGHT)
741 // For night lighting, sunlight is not propagated
746 // Invalid lighting bank
750 /*dstream<<"Bottom for sunlight-propagated block ("
751 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
754 // Bottom sunlight is not valid; get the block and loop to it
758 block = getBlockNoCreate(pos);
760 catch(InvalidPositionException &e)
769 Enable this to disable proper lighting for speeding up map
770 generation for testing or whatever
773 //if(g_settings.get(""))
775 core::map<v3s16, MapBlock*>::Iterator i;
776 i = blocks_to_update.getIterator();
777 for(; i.atEnd() == false; i++)
779 MapBlock *block = i.getNode()->getValue();
780 v3s16 p = block->getPos();
781 block->setLightingExpired(false);
789 TimeTaker timer("unspreadLight");
790 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
795 u32 diff = modified_blocks.size() - count_was;
796 count_was = modified_blocks.size();
797 dstream<<"unspreadLight modified "<<diff<<std::endl;
801 TimeTaker timer("spreadLight");
802 spreadLight(bank, light_sources, modified_blocks);
807 u32 diff = modified_blocks.size() - count_was;
808 count_was = modified_blocks.size();
809 dstream<<"spreadLight modified "<<diff<<std::endl;
814 //MapVoxelManipulator vmanip(this);
816 // Make a manual voxel manipulator and load all the blocks
817 // that touch the requested blocks
818 ManualMapVoxelManipulator vmanip(this);
819 core::map<v3s16, MapBlock*>::Iterator i;
820 i = blocks_to_update.getIterator();
821 for(; i.atEnd() == false; i++)
823 MapBlock *block = i.getNode()->getValue();
824 v3s16 p = block->getPos();
826 // Add all surrounding blocks
827 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
830 Add all surrounding blocks that have up-to-date lighting
831 NOTE: This doesn't quite do the job (not everything
832 appropriate is lighted)
834 /*for(s16 z=-1; z<=1; z++)
835 for(s16 y=-1; y<=1; y++)
836 for(s16 x=-1; x<=1; x++)
839 MapBlock *block = getBlockNoCreateNoEx(p);
844 if(block->getLightingExpired())
846 vmanip.initialEmerge(p, p);
849 // Lighting of block will be updated completely
850 block->setLightingExpired(false);
854 //TimeTaker timer("unSpreadLight");
855 vmanip.unspreadLight(bank, unlight_from, light_sources);
858 //TimeTaker timer("spreadLight");
859 vmanip.spreadLight(bank, light_sources);
862 //TimeTaker timer("blitBack");
863 vmanip.blitBack(modified_blocks);
865 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
869 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
872 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
873 core::map<v3s16, MapBlock*> & modified_blocks)
875 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
876 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
879 Update information about whether day and night light differ
881 for(core::map<v3s16, MapBlock*>::Iterator
882 i = modified_blocks.getIterator();
883 i.atEnd() == false; i++)
885 MapBlock *block = i.getNode()->getValue();
886 block->updateDayNightDiff();
892 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
893 core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
896 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
897 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
900 From this node to nodes underneath:
901 If lighting is sunlight (1.0), unlight neighbours and
906 v3s16 toppos = p + v3s16(0,1,0);
907 v3s16 bottompos = p + v3s16(0,-1,0);
909 bool node_under_sunlight = true;
910 core::map<v3s16, bool> light_sources;
913 If there is a node at top and it doesn't have sunlight,
914 there has not been any sunlight going down.
916 Otherwise there probably is.
919 MapNode topnode = getNode(toppos);
921 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
922 node_under_sunlight = false;
924 catch(InvalidPositionException &e)
930 If the new node is solid and there is grass below, change it to mud
932 if(content_features(n).walkable == true)
935 MapNode bottomnode = getNode(bottompos);
937 if(bottomnode.getContent() == CONTENT_GRASS
938 || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
940 bottomnode.setContent(CONTENT_MUD);
941 setNode(bottompos, bottomnode);
944 catch(InvalidPositionException &e)
952 If the new node is mud and it is under sunlight, change it
955 if(n.getContent() == CONTENT_MUD && node_under_sunlight)
957 n.setContent(CONTENT_GRASS);
962 Remove all light that has come out of this node
965 enum LightBank banks[] =
970 for(s32 i=0; i<2; i++)
972 enum LightBank bank = banks[i];
974 u8 lightwas = getNode(p).getLight(bank);
976 // Add the block of the added node to modified_blocks
977 v3s16 blockpos = getNodeBlockPos(p);
978 MapBlock * block = getBlockNoCreate(blockpos);
979 assert(block != NULL);
980 modified_blocks.insert(blockpos, block);
982 assert(isValidPosition(p));
984 // Unlight neighbours of node.
985 // This means setting light of all consequent dimmer nodes
987 // This also collects the nodes at the border which will spread
988 // light again into this.
989 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
995 If node lets sunlight through and is under sunlight, it has
998 if(node_under_sunlight && content_features(n).sunlight_propagates)
1000 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
1004 Set the node on the map
1013 NodeMetadata *meta_proto = content_features(n).initial_metadata;
1016 NodeMetadata *meta = meta_proto->clone();
1017 meta->setOwner(player_name);
1018 setNodeMetadata(p, meta);
1022 If node is under sunlight and doesn't let sunlight through,
1023 take all sunlighted nodes under it and clear light from them
1024 and from where the light has been spread.
1025 TODO: This could be optimized by mass-unlighting instead
1028 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1032 //m_dout<<DTIME<<"y="<<y<<std::endl;
1033 v3s16 n2pos(p.X, y, p.Z);
1037 n2 = getNode(n2pos);
1039 catch(InvalidPositionException &e)
1044 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1046 unLightNeighbors(LIGHTBANK_DAY,
1047 n2pos, n2.getLight(LIGHTBANK_DAY),
1048 light_sources, modified_blocks);
1049 n2.setLight(LIGHTBANK_DAY, 0);
1057 for(s32 i=0; i<2; i++)
1059 enum LightBank bank = banks[i];
1062 Spread light from all nodes that might be capable of doing so
1064 spreadLight(bank, light_sources, modified_blocks);
1068 Update information about whether day and night light differ
1070 for(core::map<v3s16, MapBlock*>::Iterator
1071 i = modified_blocks.getIterator();
1072 i.atEnd() == false; i++)
1074 MapBlock *block = i.getNode()->getValue();
1075 block->updateDayNightDiff();
1079 Add neighboring liquid nodes and the node itself if it is
1080 liquid (=water node was added) to transform queue.
1083 v3s16(0,0,0), // self
1084 v3s16(0,0,1), // back
1085 v3s16(0,1,0), // top
1086 v3s16(1,0,0), // right
1087 v3s16(0,0,-1), // front
1088 v3s16(0,-1,0), // bottom
1089 v3s16(-1,0,0), // left
1091 for(u16 i=0; i<7; i++)
1096 v3s16 p2 = p + dirs[i];
1098 MapNode n2 = getNode(p2);
1099 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1101 m_transforming_liquid.push_back(p2);
1104 }catch(InvalidPositionException &e)
1112 void Map::removeNodeAndUpdate(v3s16 p,
1113 core::map<v3s16, MapBlock*> &modified_blocks)
1115 /*PrintInfo(m_dout);
1116 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1117 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1119 bool node_under_sunlight = true;
1121 v3s16 toppos = p + v3s16(0,1,0);
1123 // Node will be replaced with this
1124 content_t replace_material = CONTENT_AIR;
1127 If there is a node at top and it doesn't have sunlight,
1128 there will be no sunlight going down.
1131 MapNode topnode = getNode(toppos);
1133 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1134 node_under_sunlight = false;
1136 catch(InvalidPositionException &e)
1140 core::map<v3s16, bool> light_sources;
1142 enum LightBank banks[] =
1147 for(s32 i=0; i<2; i++)
1149 enum LightBank bank = banks[i];
1152 Unlight neighbors (in case the node is a light source)
1154 unLightNeighbors(bank, p,
1155 getNode(p).getLight(bank),
1156 light_sources, modified_blocks);
1160 Remove node metadata
1163 removeNodeMetadata(p);
1167 This also clears the lighting.
1171 n.setContent(replace_material);
1174 for(s32 i=0; i<2; i++)
1176 enum LightBank bank = banks[i];
1179 Recalculate lighting
1181 spreadLight(bank, light_sources, modified_blocks);
1184 // Add the block of the removed node to modified_blocks
1185 v3s16 blockpos = getNodeBlockPos(p);
1186 MapBlock * block = getBlockNoCreate(blockpos);
1187 assert(block != NULL);
1188 modified_blocks.insert(blockpos, block);
1191 If the removed node was under sunlight, propagate the
1192 sunlight down from it and then light all neighbors
1193 of the propagated blocks.
1195 if(node_under_sunlight)
1197 s16 ybottom = propagateSunlight(p, modified_blocks);
1198 /*m_dout<<DTIME<<"Node was under sunlight. "
1199 "Propagating sunlight";
1200 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1202 for(; y >= ybottom; y--)
1204 v3s16 p2(p.X, y, p.Z);
1205 /*m_dout<<DTIME<<"lighting neighbors of node ("
1206 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1208 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1213 // Set the lighting of this node to 0
1214 // TODO: Is this needed? Lighting is cleared up there already.
1216 MapNode n = getNode(p);
1217 n.setLight(LIGHTBANK_DAY, 0);
1220 catch(InvalidPositionException &e)
1226 for(s32 i=0; i<2; i++)
1228 enum LightBank bank = banks[i];
1230 // Get the brightest neighbour node and propagate light from it
1231 v3s16 n2p = getBrightestNeighbour(bank, p);
1233 MapNode n2 = getNode(n2p);
1234 lightNeighbors(bank, n2p, modified_blocks);
1236 catch(InvalidPositionException &e)
1242 Update information about whether day and night light differ
1244 for(core::map<v3s16, MapBlock*>::Iterator
1245 i = modified_blocks.getIterator();
1246 i.atEnd() == false; i++)
1248 MapBlock *block = i.getNode()->getValue();
1249 block->updateDayNightDiff();
1253 Add neighboring liquid nodes and this node to transform queue.
1254 (it's vital for the node itself to get updated last.)
1257 v3s16(0,0,1), // back
1258 v3s16(0,1,0), // top
1259 v3s16(1,0,0), // right
1260 v3s16(0,0,-1), // front
1261 v3s16(0,-1,0), // bottom
1262 v3s16(-1,0,0), // left
1263 v3s16(0,0,0), // self
1265 for(u16 i=0; i<7; i++)
1270 v3s16 p2 = p + dirs[i];
1272 MapNode n2 = getNode(p2);
1273 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1275 m_transforming_liquid.push_back(p2);
1278 }catch(InvalidPositionException &e)
1284 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1287 event.type = MEET_ADDNODE;
1291 bool succeeded = true;
1293 core::map<v3s16, MapBlock*> modified_blocks;
1294 std::string st = std::string("");
1295 addNodeAndUpdate(p, n, modified_blocks, st);
1297 // Copy modified_blocks to event
1298 for(core::map<v3s16, MapBlock*>::Iterator
1299 i = modified_blocks.getIterator();
1300 i.atEnd()==false; i++)
1302 event.modified_blocks.insert(i.getNode()->getKey(), false);
1305 catch(InvalidPositionException &e){
1309 dispatchEvent(&event);
1314 bool Map::removeNodeWithEvent(v3s16 p)
1317 event.type = MEET_REMOVENODE;
1320 bool succeeded = true;
1322 core::map<v3s16, MapBlock*> modified_blocks;
1323 removeNodeAndUpdate(p, modified_blocks);
1325 // Copy modified_blocks to event
1326 for(core::map<v3s16, MapBlock*>::Iterator
1327 i = modified_blocks.getIterator();
1328 i.atEnd()==false; i++)
1330 event.modified_blocks.insert(i.getNode()->getKey(), false);
1333 catch(InvalidPositionException &e){
1337 dispatchEvent(&event);
1342 bool Map::dayNightDiffed(v3s16 blockpos)
1345 v3s16 p = blockpos + v3s16(0,0,0);
1346 MapBlock *b = getBlockNoCreate(p);
1347 if(b->dayNightDiffed())
1350 catch(InvalidPositionException &e){}
1353 v3s16 p = blockpos + v3s16(-1,0,0);
1354 MapBlock *b = getBlockNoCreate(p);
1355 if(b->dayNightDiffed())
1358 catch(InvalidPositionException &e){}
1360 v3s16 p = blockpos + v3s16(0,-1,0);
1361 MapBlock *b = getBlockNoCreate(p);
1362 if(b->dayNightDiffed())
1365 catch(InvalidPositionException &e){}
1367 v3s16 p = blockpos + v3s16(0,0,-1);
1368 MapBlock *b = getBlockNoCreate(p);
1369 if(b->dayNightDiffed())
1372 catch(InvalidPositionException &e){}
1375 v3s16 p = blockpos + v3s16(1,0,0);
1376 MapBlock *b = getBlockNoCreate(p);
1377 if(b->dayNightDiffed())
1380 catch(InvalidPositionException &e){}
1382 v3s16 p = blockpos + v3s16(0,1,0);
1383 MapBlock *b = getBlockNoCreate(p);
1384 if(b->dayNightDiffed())
1387 catch(InvalidPositionException &e){}
1389 v3s16 p = blockpos + v3s16(0,0,1);
1390 MapBlock *b = getBlockNoCreate(p);
1391 if(b->dayNightDiffed())
1394 catch(InvalidPositionException &e){}
1400 Updates usage timers
1402 void Map::timerUpdate(float dtime, float unload_timeout,
1403 core::list<v3s16> *unloaded_blocks)
1405 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1407 core::list<v2s16> sector_deletion_queue;
1408 u32 deleted_blocks_count = 0;
1409 u32 saved_blocks_count = 0;
1411 core::map<v2s16, MapSector*>::Iterator si;
1414 si = m_sectors.getIterator();
1415 for(; si.atEnd() == false; si++)
1417 MapSector *sector = si.getNode()->getValue();
1419 bool all_blocks_deleted = true;
1421 core::list<MapBlock*> blocks;
1422 sector->getBlocks(blocks);
1424 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1425 i != blocks.end(); i++)
1427 MapBlock *block = (*i);
1429 block->incrementUsageTimer(dtime);
1431 if(block->getUsageTimer() > unload_timeout)
1433 v3s16 p = block->getPos();
1436 if(block->getModified() != MOD_STATE_CLEAN
1437 && save_before_unloading)
1440 saved_blocks_count++;
1443 // Delete from memory
1444 sector->deleteBlock(block);
1447 unloaded_blocks->push_back(p);
1449 deleted_blocks_count++;
1453 all_blocks_deleted = false;
1457 if(all_blocks_deleted)
1459 sector_deletion_queue.push_back(si.getNode()->getKey());
1464 // Finally delete the empty sectors
1465 deleteSectors(sector_deletion_queue);
1467 if(deleted_blocks_count != 0)
1469 PrintInfo(dstream); // ServerMap/ClientMap:
1470 dstream<<"Unloaded "<<deleted_blocks_count
1471 <<" blocks from memory";
1472 if(save_before_unloading)
1473 dstream<<", of which "<<saved_blocks_count<<" were written";
1474 dstream<<"."<<std::endl;
1478 void Map::deleteSectors(core::list<v2s16> &list)
1480 core::list<v2s16>::Iterator j;
1481 for(j=list.begin(); j!=list.end(); j++)
1483 MapSector *sector = m_sectors[*j];
1484 // If sector is in sector cache, remove it from there
1485 if(m_sector_cache == sector)
1486 m_sector_cache = NULL;
1487 // Remove from map and delete
1488 m_sectors.remove(*j);
1494 void Map::unloadUnusedData(float timeout,
1495 core::list<v3s16> *deleted_blocks)
1497 core::list<v2s16> sector_deletion_queue;
1498 u32 deleted_blocks_count = 0;
1499 u32 saved_blocks_count = 0;
1501 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1502 for(; si.atEnd() == false; si++)
1504 MapSector *sector = si.getNode()->getValue();
1506 bool all_blocks_deleted = true;
1508 core::list<MapBlock*> blocks;
1509 sector->getBlocks(blocks);
1510 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1511 i != blocks.end(); i++)
1513 MapBlock *block = (*i);
1515 if(block->getUsageTimer() > timeout)
1518 if(block->getModified() != MOD_STATE_CLEAN)
1521 saved_blocks_count++;
1523 // Delete from memory
1524 sector->deleteBlock(block);
1525 deleted_blocks_count++;
1529 all_blocks_deleted = false;
1533 if(all_blocks_deleted)
1535 sector_deletion_queue.push_back(si.getNode()->getKey());
1539 deleteSectors(sector_deletion_queue);
1541 dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1542 <<", of which "<<saved_blocks_count<<" were wr."
1545 //return sector_deletion_queue.getSize();
1546 //return deleted_blocks_count;
1550 void Map::PrintInfo(std::ostream &out)
1555 #define WATER_DROP_BOOST 4
1559 NEIGHBOR_SAME_LEVEL,
1562 struct NodeNeighbor {
1568 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1570 DSTACK(__FUNCTION_NAME);
1571 //TimeTaker timer("transformLiquids()");
1574 u32 initial_size = m_transforming_liquid.size();
1576 /*if(initial_size != 0)
1577 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1579 // list of nodes that due to viscosity have not reached their max level height
1580 UniqueQueue<v3s16> must_reflow;
1582 // List of MapBlocks that will require a lighting update (due to lava)
1583 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1585 while(m_transforming_liquid.size() != 0)
1587 // This should be done here so that it is done when continue is used
1588 if(loopcount >= initial_size * 3)
1593 Get a queued transforming liquid node
1595 v3s16 p0 = m_transforming_liquid.pop_front();
1597 MapNode n0 = getNodeNoEx(p0);
1600 Collect information about current node
1602 s8 liquid_level = -1;
1603 u8 liquid_kind = CONTENT_IGNORE;
1604 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1605 switch (liquid_type) {
1607 liquid_level = LIQUID_LEVEL_SOURCE;
1608 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1610 case LIQUID_FLOWING:
1611 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1612 liquid_kind = n0.getContent();
1615 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1616 // continue with the next node.
1617 if (n0.getContent() != CONTENT_AIR)
1619 liquid_kind = CONTENT_AIR;
1624 Collect information about the environment
1626 const v3s16 *dirs = g_6dirs;
1627 NodeNeighbor sources[6]; // surrounding sources
1628 int num_sources = 0;
1629 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1631 NodeNeighbor airs[6]; // surrounding air
1633 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1634 int num_neutrals = 0;
1635 bool flowing_down = false;
1636 for (u16 i = 0; i < 6; i++) {
1637 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1640 nt = NEIGHBOR_UPPER;
1643 nt = NEIGHBOR_LOWER;
1646 v3s16 npos = p0 + dirs[i];
1647 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1648 switch (content_features(nb.n.getContent()).liquid_type) {
1650 if (nb.n.getContent() == CONTENT_AIR) {
1651 airs[num_airs++] = nb;
1652 // if the current node is a water source the neighbor
1653 // should be enqueded for transformation regardless of whether the
1654 // current node changes or not.
1655 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1656 m_transforming_liquid.push_back(npos);
1657 // if the current node happens to be a flowing node, it will start to flow down here.
1658 if (nb.t == NEIGHBOR_LOWER) {
1659 flowing_down = true;
1662 neutrals[num_neutrals++] = nb;
1666 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1667 if (liquid_kind == CONTENT_AIR)
1668 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1669 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1670 neutrals[num_neutrals++] = nb;
1672 sources[num_sources++] = nb;
1675 case LIQUID_FLOWING:
1676 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1677 if (liquid_kind == CONTENT_AIR)
1678 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1679 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1680 neutrals[num_neutrals++] = nb;
1682 flows[num_flows++] = nb;
1683 if (nb.t == NEIGHBOR_LOWER)
1684 flowing_down = true;
1691 decide on the type (and possibly level) of the current node
1693 content_t new_node_content;
1694 s8 new_node_level = -1;
1695 s8 max_node_level = -1;
1696 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1697 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1698 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1699 // it's perfectly safe to use liquid_kind here to determine the new node content.
1700 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1701 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1702 // liquid_kind is set properly, see above
1703 new_node_content = liquid_kind;
1704 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1706 // no surrounding sources, so get the maximum level that can flow into this node
1707 for (u16 i = 0; i < num_flows; i++) {
1708 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1709 switch (flows[i].t) {
1710 case NEIGHBOR_UPPER:
1711 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1712 max_node_level = LIQUID_LEVEL_MAX;
1713 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1714 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1715 } else if (nb_liquid_level > max_node_level)
1716 max_node_level = nb_liquid_level;
1718 case NEIGHBOR_LOWER:
1720 case NEIGHBOR_SAME_LEVEL:
1721 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1722 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1723 max_node_level = nb_liquid_level - 1;
1729 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1730 if (viscosity > 1 && max_node_level != liquid_level) {
1731 // amount to gain, limited by viscosity
1732 // must be at least 1 in absolute value
1733 s8 level_inc = max_node_level - liquid_level;
1734 if (level_inc < -viscosity || level_inc > viscosity)
1735 new_node_level = liquid_level + level_inc/viscosity;
1736 else if (level_inc < 0)
1737 new_node_level = liquid_level - 1;
1738 else if (level_inc > 0)
1739 new_node_level = liquid_level + 1;
1740 if (new_node_level != max_node_level)
1741 must_reflow.push_back(p0);
1743 new_node_level = max_node_level;
1745 if (new_node_level >= 0)
1746 new_node_content = liquid_kind;
1748 new_node_content = CONTENT_AIR;
1753 check if anything has changed. if not, just continue with the next node.
1755 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1756 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1757 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1763 update the current node
1765 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1766 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1767 // set level to last 3 bits, flowing down bit to 4th bit
1768 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1770 // set the liquid level and flow bit to 0
1771 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1773 n0.setContent(new_node_content);
1775 v3s16 blockpos = getNodeBlockPos(p0);
1776 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1778 modified_blocks.insert(blockpos, block);
1779 // If node emits light, MapBlock requires lighting update
1780 if(content_features(n0).light_source != 0)
1781 lighting_modified_blocks[block->getPos()] = block;
1785 enqueue neighbors for update if neccessary
1787 switch (content_features(n0.getContent()).liquid_type) {
1789 case LIQUID_FLOWING:
1790 // make sure source flows into all neighboring nodes
1791 for (u16 i = 0; i < num_flows; i++)
1792 if (flows[i].t != NEIGHBOR_UPPER)
1793 m_transforming_liquid.push_back(flows[i].p);
1794 for (u16 i = 0; i < num_airs; i++)
1795 if (airs[i].t != NEIGHBOR_UPPER)
1796 m_transforming_liquid.push_back(airs[i].p);
1799 // this flow has turned to air; neighboring flows might need to do the same
1800 for (u16 i = 0; i < num_flows; i++)
1801 m_transforming_liquid.push_back(flows[i].p);
1805 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1806 while (must_reflow.size() > 0)
1807 m_transforming_liquid.push_back(must_reflow.pop_front());
1808 updateLighting(lighting_modified_blocks, modified_blocks);
1811 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1813 v3s16 blockpos = getNodeBlockPos(p);
1814 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1815 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1818 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1822 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1826 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1828 v3s16 blockpos = getNodeBlockPos(p);
1829 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1830 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1833 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1837 block->m_node_metadata.set(p_rel, meta);
1840 void Map::removeNodeMetadata(v3s16 p)
1842 v3s16 blockpos = getNodeBlockPos(p);
1843 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1844 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1847 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1851 block->m_node_metadata.remove(p_rel);
1854 void Map::nodeMetadataStep(float dtime,
1855 core::map<v3s16, MapBlock*> &changed_blocks)
1859 Currently there is no way to ensure that all the necessary
1860 blocks are loaded when this is run. (They might get unloaded)
1861 NOTE: ^- Actually, that might not be so. In a quick test it
1862 reloaded a block with a furnace when I walked back to it from
1865 core::map<v2s16, MapSector*>::Iterator si;
1866 si = m_sectors.getIterator();
1867 for(; si.atEnd() == false; si++)
1869 MapSector *sector = si.getNode()->getValue();
1870 core::list< MapBlock * > sectorblocks;
1871 sector->getBlocks(sectorblocks);
1872 core::list< MapBlock * >::Iterator i;
1873 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1875 MapBlock *block = *i;
1876 bool changed = block->m_node_metadata.step(dtime);
1878 changed_blocks[block->getPos()] = block;
1887 ServerMap::ServerMap(std::string savedir):
1890 m_map_metadata_changed(true),
1892 m_database_read(NULL),
1893 m_database_write(NULL)
1895 dstream<<__FUNCTION_NAME<<std::endl;
1897 //m_chunksize = 8; // Takes a few seconds
1899 if (g_settings.get("fixed_map_seed").empty())
1901 m_seed = (((u64)(myrand()%0xffff)<<0)
1902 + ((u64)(myrand()%0xffff)<<16)
1903 + ((u64)(myrand()%0xffff)<<32)
1904 + ((u64)(myrand()%0xffff)<<48));
1908 m_seed = g_settings.getU64("fixed_map_seed");
1912 Experimental and debug stuff
1919 Try to load map; if not found, create a new one.
1922 m_savedir = savedir;
1923 m_map_saving_enabled = false;
1927 // If directory exists, check contents and load if possible
1928 if(fs::PathExists(m_savedir))
1930 // If directory is empty, it is safe to save into it.
1931 if(fs::GetDirListing(m_savedir).size() == 0)
1933 dstream<<DTIME<<"Server: Empty save directory is valid."
1935 m_map_saving_enabled = true;
1940 // Load map metadata (seed, chunksize)
1943 catch(FileNotGoodException &e){
1944 dstream<<DTIME<<"WARNING: Could not load map metadata"
1945 //<<" Disabling chunk-based generator."
1951 // Load chunk metadata
1954 catch(FileNotGoodException &e){
1955 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1956 <<" Disabling chunk-based generator."
1961 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1962 "metadata and sector (0,0) from "<<savedir<<
1963 ", assuming valid save directory."
1966 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1967 <<"and chunk metadata from "<<savedir
1968 <<", assuming valid save directory."
1971 m_map_saving_enabled = true;
1972 // Map loaded, not creating new one
1976 // If directory doesn't exist, it is safe to save to it
1978 m_map_saving_enabled = true;
1981 catch(std::exception &e)
1983 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1984 <<", exception: "<<e.what()<<std::endl;
1985 dstream<<"Please remove the map or fix it."<<std::endl;
1986 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1989 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1991 // Create zero sector
1992 emergeSector(v2s16(0,0));
1994 // Initially write whole map
1998 ServerMap::~ServerMap()
2000 dstream<<__FUNCTION_NAME<<std::endl;
2004 if(m_map_saving_enabled)
2006 // Save only changed parts
2008 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2012 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2015 catch(std::exception &e)
2017 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2018 <<", exception: "<<e.what()<<std::endl;
2022 Close database if it was opened
2025 sqlite3_finalize(m_database_read);
2026 if(m_database_write)
2027 sqlite3_finalize(m_database_write);
2029 sqlite3_close(m_database);
2035 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2036 for(; i.atEnd() == false; i++)
2038 MapChunk *chunk = i.getNode()->getValue();
2044 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2046 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2047 if(enable_mapgen_debug_info)
2048 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2049 <<blockpos.Z<<")"<<std::endl;
2051 // Do nothing if not inside limits (+-1 because of neighbors)
2052 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2053 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2059 data->no_op = false;
2060 data->seed = m_seed;
2061 data->blockpos = blockpos;
2064 Create the whole area of this and the neighboring blocks
2067 //TimeTaker timer("initBlockMake() create area");
2069 for(s16 x=-1; x<=1; x++)
2070 for(s16 z=-1; z<=1; z++)
2072 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2073 // Sector metadata is loaded from disk if not already loaded.
2074 ServerMapSector *sector = createSector(sectorpos);
2077 for(s16 y=-1; y<=1; y++)
2079 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2080 //MapBlock *block = createBlock(p);
2081 // 1) get from memory, 2) load from disk
2082 MapBlock *block = emergeBlock(p, false);
2083 // 3) create a blank one
2086 block = createBlock(p);
2089 Block gets sunlight if this is true.
2091 Refer to the map generator heuristics.
2093 bool ug = mapgen::block_is_underground(data->seed, p);
2094 block->setIsUnderground(ug);
2097 // Lighting will not be valid after make_chunk is called
2098 block->setLightingExpired(true);
2099 // Lighting will be calculated
2100 //block->setLightingExpired(false);
2106 Now we have a big empty area.
2108 Make a ManualMapVoxelManipulator that contains this and the
2112 // The area that contains this block and it's neighbors
2113 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2114 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2116 data->vmanip = new ManualMapVoxelManipulator(this);
2117 //data->vmanip->setMap(this);
2121 //TimeTaker timer("initBlockMake() initialEmerge");
2122 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2125 // Data is ready now.
2128 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2129 core::map<v3s16, MapBlock*> &changed_blocks)
2131 v3s16 blockpos = data->blockpos;
2132 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2133 <<blockpos.Z<<")"<<std::endl;*/
2137 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2141 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2143 /*dstream<<"Resulting vmanip:"<<std::endl;
2144 data->vmanip.print(dstream);*/
2147 Blit generated stuff to map
2148 NOTE: blitBackAll adds nearly everything to changed_blocks
2152 //TimeTaker timer("finishBlockMake() blitBackAll");
2153 data->vmanip->blitBackAll(&changed_blocks);
2156 if(enable_mapgen_debug_info)
2157 dstream<<"finishBlockMake: changed_blocks.size()="
2158 <<changed_blocks.size()<<std::endl;
2161 Copy transforming liquid information
2163 while(data->transforming_liquid.size() > 0)
2165 v3s16 p = data->transforming_liquid.pop_front();
2166 m_transforming_liquid.push_back(p);
2172 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2176 Set is_underground flag for lighting with sunlight.
2178 Refer to map generator heuristics.
2180 NOTE: This is done in initChunkMake
2182 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2186 Add sunlight to central block.
2187 This makes in-dark-spawning monsters to not flood the whole thing.
2188 Do not spread the light, though.
2190 /*core::map<v3s16, bool> light_sources;
2191 bool black_air_left = false;
2192 block->propagateSunlight(light_sources, true, &black_air_left);*/
2195 NOTE: Lighting and object adding shouldn't really be here, but
2196 lighting is a bit tricky to move properly to makeBlock.
2197 TODO: Do this the right way anyway, that is, move it to makeBlock.
2198 - There needs to be some way for makeBlock to report back if
2199 the lighting update is going further down because of the
2200 new block blocking light
2205 NOTE: This takes ~60ms, TODO: Investigate why
2208 TimeTaker t("finishBlockMake lighting update");
2210 core::map<v3s16, MapBlock*> lighting_update_blocks;
2213 lighting_update_blocks.insert(block->getPos(), block);
2218 v3s16 p = block->getPos()+v3s16(x,1,z);
2219 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2223 // All modified blocks
2224 // NOTE: Should this be done? If this is not done, then the lighting
2225 // of the others will be updated in a different place, one by one, i
2226 // think... or they might not? Well, at least they are left marked as
2227 // "lighting expired"; it seems that is not handled at all anywhere,
2228 // so enabling this will slow it down A LOT because otherwise it
2229 // would not do this at all. This causes the black trees.
2230 for(core::map<v3s16, MapBlock*>::Iterator
2231 i = changed_blocks.getIterator();
2232 i.atEnd() == false; i++)
2234 lighting_update_blocks.insert(i.getNode()->getKey(),
2235 i.getNode()->getValue());
2237 /*// Also force-add all the upmost blocks for proper sunlight
2238 for(s16 x=-1; x<=1; x++)
2239 for(s16 z=-1; z<=1; z++)
2241 v3s16 p = block->getPos()+v3s16(x,1,z);
2242 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2245 updateLighting(lighting_update_blocks, changed_blocks);
2248 Set lighting to non-expired state in all of them.
2249 This is cheating, but it is not fast enough if all of them
2250 would actually be updated.
2252 for(s16 x=-1; x<=1; x++)
2253 for(s16 y=-1; y<=1; y++)
2254 for(s16 z=-1; z<=1; z++)
2256 v3s16 p = block->getPos()+v3s16(x,y,z);
2257 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2260 if(enable_mapgen_debug_info == false)
2261 t.stop(true); // Hide output
2265 Add random objects to block
2267 mapgen::add_random_objects(block);
2270 Go through changed blocks
2272 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2273 i.atEnd() == false; i++)
2275 MapBlock *block = i.getNode()->getValue();
2278 Update day/night difference cache of the MapBlocks
2280 block->updateDayNightDiff();
2282 Set block as modified
2284 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2288 Set central block as generated
2290 block->setGenerated(true);
2293 Save changed parts of map
2294 NOTE: Will be saved later.
2298 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2299 <<blockpos.Z<<")"<<std::endl;*/
2301 if(enable_mapgen_debug_info)
2304 Analyze resulting blocks
2306 for(s16 x=-1; x<=1; x++)
2307 for(s16 y=-1; y<=1; y++)
2308 for(s16 z=-1; z<=1; z++)
2310 v3s16 p = block->getPos()+v3s16(x,y,z);
2311 MapBlock *block = getBlockNoCreateNoEx(p);
2313 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2314 dstream<<"Generated "<<spos<<": "
2315 <<analyze_block(block)<<std::endl;
2323 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2325 DSTACKF("%s: p2d=(%d,%d)",
2330 Check if it exists already in memory
2332 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2337 Try to load it from disk (with blocks)
2339 //if(loadSectorFull(p2d) == true)
2342 Try to load metadata from disk
2345 if(loadSectorMeta(p2d) == true)
2347 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2350 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2351 throw InvalidPositionException("");
2357 Do not create over-limit
2359 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2360 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2361 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2362 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2363 throw InvalidPositionException("createSector(): pos. over limit");
2366 Generate blank sector
2369 sector = new ServerMapSector(this, p2d);
2371 // Sector position on map in nodes
2372 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2377 m_sectors.insert(p2d, sector);
2383 This is a quick-hand function for calling makeBlock().
2385 MapBlock * ServerMap::generateBlock(
2387 core::map<v3s16, MapBlock*> &modified_blocks
2390 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2392 /*dstream<<"generateBlock(): "
2393 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2396 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2398 TimeTaker timer("generateBlock");
2400 //MapBlock *block = original_dummy;
2402 v2s16 p2d(p.X, p.Z);
2403 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2406 Do not generate over-limit
2408 if(blockpos_over_limit(p))
2410 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2411 throw InvalidPositionException("generateBlock(): pos. over limit");
2415 Create block make data
2417 mapgen::BlockMakeData data;
2418 initBlockMake(&data, p);
2424 TimeTaker t("mapgen::make_block()");
2425 mapgen::make_block(&data);
2427 if(enable_mapgen_debug_info == false)
2428 t.stop(true); // Hide output
2432 Blit data back on map, update lighting, add mobs and whatever this does
2434 finishBlockMake(&data, modified_blocks);
2439 MapBlock *block = getBlockNoCreateNoEx(p);
2447 bool erroneus_content = false;
2448 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2449 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2450 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2453 MapNode n = block->getNode(p);
2454 if(n.getContent() == CONTENT_IGNORE)
2456 dstream<<"CONTENT_IGNORE at "
2457 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2459 erroneus_content = true;
2463 if(erroneus_content)
2472 Generate a completely empty block
2476 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2477 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2479 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2483 n.setContent(CONTENT_AIR);
2485 n.setContent(CONTENT_STONE);
2486 block->setNode(v3s16(x0,y0,z0), n);
2492 if(enable_mapgen_debug_info == false)
2493 timer.stop(true); // Hide output
2498 MapBlock * ServerMap::createBlock(v3s16 p)
2500 DSTACKF("%s: p=(%d,%d,%d)",
2501 __FUNCTION_NAME, p.X, p.Y, p.Z);
2504 Do not create over-limit
2506 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2507 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2508 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2509 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2510 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2511 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2512 throw InvalidPositionException("createBlock(): pos. over limit");
2514 v2s16 p2d(p.X, p.Z);
2517 This will create or load a sector if not found in memory.
2518 If block exists on disk, it will be loaded.
2520 NOTE: On old save formats, this will be slow, as it generates
2521 lighting on blocks for them.
2523 ServerMapSector *sector;
2525 sector = (ServerMapSector*)createSector(p2d);
2526 assert(sector->getId() == MAPSECTOR_SERVER);
2528 catch(InvalidPositionException &e)
2530 dstream<<"createBlock: createSector() failed"<<std::endl;
2534 NOTE: This should not be done, or at least the exception
2535 should not be passed on as std::exception, because it
2536 won't be catched at all.
2538 /*catch(std::exception &e)
2540 dstream<<"createBlock: createSector() failed: "
2541 <<e.what()<<std::endl;
2546 Try to get a block from the sector
2549 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2552 if(block->isDummy())
2557 block = sector->createBlankBlock(block_y);
2561 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2563 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2565 p.X, p.Y, p.Z, allow_generate);
2568 MapBlock *block = getBlockNoCreateNoEx(p);
2569 if(block && block->isDummy() == false)
2574 MapBlock *block = loadBlock(p);
2581 core::map<v3s16, MapBlock*> modified_blocks;
2582 MapBlock *block = generateBlock(p, modified_blocks);
2586 event.type = MEET_OTHER;
2589 // Copy modified_blocks to event
2590 for(core::map<v3s16, MapBlock*>::Iterator
2591 i = modified_blocks.getIterator();
2592 i.atEnd()==false; i++)
2594 event.modified_blocks.insert(i.getNode()->getKey(), false);
2598 dispatchEvent(&event);
2609 Do not generate over-limit
2611 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2612 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2613 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2614 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2615 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2616 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2617 throw InvalidPositionException("emergeBlock(): pos. over limit");
2619 v2s16 p2d(p.X, p.Z);
2622 This will create or load a sector if not found in memory.
2623 If block exists on disk, it will be loaded.
2625 ServerMapSector *sector;
2627 sector = createSector(p2d);
2628 //sector = emergeSector(p2d, changed_blocks);
2630 catch(InvalidPositionException &e)
2632 dstream<<"emergeBlock: createSector() failed: "
2633 <<e.what()<<std::endl;
2634 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2636 <<"You could try to delete it."<<std::endl;
2639 catch(VersionMismatchException &e)
2641 dstream<<"emergeBlock: createSector() failed: "
2642 <<e.what()<<std::endl;
2643 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2645 <<"You could try to delete it."<<std::endl;
2650 Try to get a block from the sector
2653 bool does_not_exist = false;
2654 bool lighting_expired = false;
2655 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2657 // If not found, try loading from disk
2660 block = loadBlock(p);
2666 does_not_exist = true;
2668 else if(block->isDummy() == true)
2670 does_not_exist = true;
2672 else if(block->getLightingExpired())
2674 lighting_expired = true;
2679 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2684 If block was not found on disk and not going to generate a
2685 new one, make sure there is a dummy block in place.
2687 if(only_from_disk && (does_not_exist || lighting_expired))
2689 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2693 // Create dummy block
2694 block = new MapBlock(this, p, true);
2696 // Add block to sector
2697 sector->insertBlock(block);
2703 //dstream<<"Not found on disk, generating."<<std::endl;
2705 //TimeTaker("emergeBlock() generate");
2707 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2710 If the block doesn't exist, generate the block.
2714 block = generateBlock(p, block, sector, changed_blocks,
2715 lighting_invalidated_blocks);
2718 if(lighting_expired)
2720 lighting_invalidated_blocks.insert(p, block);
2725 Initially update sunlight
2728 core::map<v3s16, bool> light_sources;
2729 bool black_air_left = false;
2730 bool bottom_invalid =
2731 block->propagateSunlight(light_sources, true,
2734 // If sunlight didn't reach everywhere and part of block is
2735 // above ground, lighting has to be properly updated
2736 //if(black_air_left && some_part_underground)
2739 lighting_invalidated_blocks[block->getPos()] = block;
2744 lighting_invalidated_blocks[block->getPos()] = block;
2753 s16 ServerMap::findGroundLevel(v2s16 p2d)
2757 Uh, just do something random...
2759 // Find existing map from top to down
2762 v3s16 p(p2d.X, max, p2d.Y);
2763 for(; p.Y>min; p.Y--)
2765 MapNode n = getNodeNoEx(p);
2766 if(n.getContent() != CONTENT_IGNORE)
2771 // If this node is not air, go to plan b
2772 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2774 // Search existing walkable and return it
2775 for(; p.Y>min; p.Y--)
2777 MapNode n = getNodeNoEx(p);
2778 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2787 Determine from map generator noise functions
2790 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2793 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2794 //return (s16)level;
2797 void ServerMap::createDatabase() {
2800 e = sqlite3_exec(m_database,
2801 "CREATE TABLE IF NOT EXISTS `blocks` ("
2802 "`pos` INT NOT NULL PRIMARY KEY,"
2805 , NULL, NULL, NULL);
2806 if(e == SQLITE_ABORT)
2807 throw FileNotGoodException("Could not create database structure");
2809 dstream<<"Server: Database structure was created";
2812 void ServerMap::verifyDatabase() {
2817 std::string dbp = m_savedir + "/map.sqlite";
2818 bool needs_create = false;
2822 Open the database connection
2825 createDirs(m_savedir);
2827 if(!fs::PathExists(dbp))
2828 needs_create = true;
2830 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2831 if(d != SQLITE_OK) {
2832 dstream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2833 throw FileNotGoodException("Cannot open database file");
2839 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2840 if(d != SQLITE_OK) {
2841 dstream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2842 throw FileNotGoodException("Cannot prepare read statement");
2845 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2846 if(d != SQLITE_OK) {
2847 dstream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2848 throw FileNotGoodException("Cannot prepare write statement");
2851 dstream<<"Server: Database opened"<<std::endl;
2855 bool ServerMap::loadFromFolders() {
2856 if(!m_database && !fs::PathExists(m_savedir + "/map.sqlite"))
2861 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2862 return (sqlite3_int64)pos.Z*16777216 +
2863 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2866 void ServerMap::createDirs(std::string path)
2868 if(fs::CreateAllDirs(path) == false)
2870 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2871 <<"\""<<path<<"\""<<std::endl;
2872 throw BaseException("ServerMap failed to create directory");
2876 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2882 snprintf(cc, 9, "%.4x%.4x",
2883 (unsigned int)pos.X&0xffff,
2884 (unsigned int)pos.Y&0xffff);
2886 return m_savedir + "/sectors/" + cc;
2888 snprintf(cc, 9, "%.3x/%.3x",
2889 (unsigned int)pos.X&0xfff,
2890 (unsigned int)pos.Y&0xfff);
2892 return m_savedir + "/sectors2/" + cc;
2898 v2s16 ServerMap::getSectorPos(std::string dirname)
2902 size_t spos = dirname.rfind('/') + 1;
2903 assert(spos != std::string::npos);
2904 if(dirname.size() - spos == 8)
2907 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2909 else if(dirname.size() - spos == 3)
2912 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2913 // Sign-extend the 12 bit values up to 16 bits...
2914 if(x&0x800) x|=0xF000;
2915 if(y&0x800) y|=0xF000;
2922 v2s16 pos((s16)x, (s16)y);
2926 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2928 v2s16 p2d = getSectorPos(sectordir);
2930 if(blockfile.size() != 4){
2931 throw InvalidFilenameException("Invalid block filename");
2934 int r = sscanf(blockfile.c_str(), "%4x", &y);
2936 throw InvalidFilenameException("Invalid block filename");
2937 return v3s16(p2d.X, y, p2d.Y);
2940 std::string ServerMap::getBlockFilename(v3s16 p)
2943 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2947 void ServerMap::save(bool only_changed)
2949 DSTACK(__FUNCTION_NAME);
2950 if(m_map_saving_enabled == false)
2952 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2956 if(only_changed == false)
2957 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2960 if(only_changed == false || m_map_metadata_changed)
2965 u32 sector_meta_count = 0;
2966 u32 block_count = 0;
2967 u32 block_count_all = 0; // Number of blocks in memory
2970 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2971 for(; i.atEnd() == false; i++)
2973 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2974 assert(sector->getId() == MAPSECTOR_SERVER);
2976 if(sector->differs_from_disk || only_changed == false)
2978 saveSectorMeta(sector);
2979 sector_meta_count++;
2981 core::list<MapBlock*> blocks;
2982 sector->getBlocks(blocks);
2983 core::list<MapBlock*>::Iterator j;
2985 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2986 for(j=blocks.begin(); j!=blocks.end(); j++)
2988 MapBlock *block = *j;
2992 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2993 || only_changed == false)
2998 /*dstream<<"ServerMap: Written block ("
2999 <<block->getPos().X<<","
3000 <<block->getPos().Y<<","
3001 <<block->getPos().Z<<")"
3004 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
3010 Only print if something happened or saved whole map
3012 if(only_changed == false || sector_meta_count != 0
3013 || block_count != 0)
3015 dstream<<DTIME<<"ServerMap: Written: "
3016 <<sector_meta_count<<" sector metadata files, "
3017 <<block_count<<" block files"
3018 <<", "<<block_count_all<<" blocks in memory."
3023 void ServerMap::saveMapMeta()
3025 DSTACK(__FUNCTION_NAME);
3027 dstream<<"INFO: ServerMap::saveMapMeta(): "
3031 createDirs(m_savedir);
3033 std::string fullpath = m_savedir + "/map_meta.txt";
3034 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3035 if(os.good() == false)
3037 dstream<<"ERROR: ServerMap::saveMapMeta(): "
3038 <<"could not open"<<fullpath<<std::endl;
3039 throw FileNotGoodException("Cannot open chunk metadata");
3043 params.setU64("seed", m_seed);
3045 params.writeLines(os);
3047 os<<"[end_of_params]\n";
3049 m_map_metadata_changed = false;
3052 void ServerMap::loadMapMeta()
3054 DSTACK(__FUNCTION_NAME);
3056 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
3059 std::string fullpath = m_savedir + "/map_meta.txt";
3060 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3061 if(is.good() == false)
3063 dstream<<"ERROR: ServerMap::loadMapMeta(): "
3064 <<"could not open"<<fullpath<<std::endl;
3065 throw FileNotGoodException("Cannot open map metadata");
3073 throw SerializationError
3074 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3076 std::getline(is, line);
3077 std::string trimmedline = trim(line);
3078 if(trimmedline == "[end_of_params]")
3080 params.parseConfigLine(line);
3083 m_seed = params.getU64("seed");
3085 dstream<<"INFO: ServerMap::loadMapMeta(): "
3090 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3092 DSTACK(__FUNCTION_NAME);
3093 // Format used for writing
3094 u8 version = SER_FMT_VER_HIGHEST;
3096 v2s16 pos = sector->getPos();
3097 std::string dir = getSectorDir(pos);
3100 std::string fullpath = dir + "/meta";
3101 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3102 if(o.good() == false)
3103 throw FileNotGoodException("Cannot open sector metafile");
3105 sector->serialize(o, version);
3107 sector->differs_from_disk = false;
3110 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3112 DSTACK(__FUNCTION_NAME);
3114 v2s16 p2d = getSectorPos(sectordir);
3116 ServerMapSector *sector = NULL;
3118 std::string fullpath = sectordir + "/meta";
3119 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3120 if(is.good() == false)
3122 // If the directory exists anyway, it probably is in some old
3123 // format. Just go ahead and create the sector.
3124 if(fs::PathExists(sectordir))
3126 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
3127 <<fullpath<<" doesn't exist but directory does."
3128 <<" Continuing with a sector with no metadata."
3130 sector = new ServerMapSector(this, p2d);
3131 m_sectors.insert(p2d, sector);
3135 throw FileNotGoodException("Cannot open sector metafile");
3140 sector = ServerMapSector::deSerialize
3141 (is, this, p2d, m_sectors);
3143 saveSectorMeta(sector);
3146 sector->differs_from_disk = false;
3151 bool ServerMap::loadSectorMeta(v2s16 p2d)
3153 DSTACK(__FUNCTION_NAME);
3155 MapSector *sector = NULL;
3157 // The directory layout we're going to load from.
3158 // 1 - original sectors/xxxxzzzz/
3159 // 2 - new sectors2/xxx/zzz/
3160 // If we load from anything but the latest structure, we will
3161 // immediately save to the new one, and remove the old.
3163 std::string sectordir1 = getSectorDir(p2d, 1);
3164 std::string sectordir;
3165 if(fs::PathExists(sectordir1))
3167 sectordir = sectordir1;
3172 sectordir = getSectorDir(p2d, 2);
3176 sector = loadSectorMeta(sectordir, loadlayout != 2);
3178 catch(InvalidFilenameException &e)
3182 catch(FileNotGoodException &e)
3186 catch(std::exception &e)
3195 bool ServerMap::loadSectorFull(v2s16 p2d)
3197 DSTACK(__FUNCTION_NAME);
3199 MapSector *sector = NULL;
3201 // The directory layout we're going to load from.
3202 // 1 - original sectors/xxxxzzzz/
3203 // 2 - new sectors2/xxx/zzz/
3204 // If we load from anything but the latest structure, we will
3205 // immediately save to the new one, and remove the old.
3207 std::string sectordir1 = getSectorDir(p2d, 1);
3208 std::string sectordir;
3209 if(fs::PathExists(sectordir1))
3211 sectordir = sectordir1;
3216 sectordir = getSectorDir(p2d, 2);
3220 sector = loadSectorMeta(sectordir, loadlayout != 2);
3222 catch(InvalidFilenameException &e)
3226 catch(FileNotGoodException &e)
3230 catch(std::exception &e)
3238 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3240 std::vector<fs::DirListNode>::iterator i2;
3241 for(i2=list2.begin(); i2!=list2.end(); i2++)
3247 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3249 catch(InvalidFilenameException &e)
3251 // This catches unknown crap in directory
3257 dstream<<"Sector converted to new layout - deleting "<<
3258 sectordir1<<std::endl;
3259 fs::RecursiveDelete(sectordir1);
3266 void ServerMap::beginSave() {
3268 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3269 dstream<<"WARNING: beginSave() failed, saving might be slow.";
3272 void ServerMap::endSave() {
3274 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3275 dstream<<"WARNING: endSave() failed, map might not have saved.";
3278 void ServerMap::saveBlock(MapBlock *block)
3280 DSTACK(__FUNCTION_NAME);
3282 Dummy blocks are not written
3284 if(block->isDummy())
3286 /*v3s16 p = block->getPos();
3287 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3288 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3292 // Format used for writing
3293 u8 version = SER_FMT_VER_HIGHEST;
3295 v3s16 p3d = block->getPos();
3299 v2s16 p2d(p3d.X, p3d.Z);
3300 std::string sectordir = getSectorDir(p2d);
3302 createDirs(sectordir);
3304 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3305 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3306 if(o.good() == false)
3307 throw FileNotGoodException("Cannot open block data");
3310 [0] u8 serialization version
3316 std::ostringstream o(std::ios_base::binary);
3318 o.write((char*)&version, 1);
3321 block->serialize(o, version);
3323 // Write extra data stored on disk
3324 block->serializeDiskExtra(o, version);
3326 // Write block to database
3328 std::string tmp = o.str();
3329 const char *bytes = tmp.c_str();
3331 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3332 dstream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3333 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3334 dstream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3335 int written = sqlite3_step(m_database_write);
3336 if(written != SQLITE_DONE)
3337 dstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3338 <<sqlite3_errmsg(m_database)<<std::endl;
3339 // Make ready for later reuse
3340 sqlite3_reset(m_database_write);
3342 // We just wrote it to the disk so clear modified flag
3343 block->resetModified();
3346 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3348 DSTACK(__FUNCTION_NAME);
3350 std::string fullpath = sectordir+"/"+blockfile;
3353 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3354 if(is.good() == false)
3355 throw FileNotGoodException("Cannot open block file");
3357 v3s16 p3d = getBlockPos(sectordir, blockfile);
3358 v2s16 p2d(p3d.X, p3d.Z);
3360 assert(sector->getPos() == p2d);
3362 u8 version = SER_FMT_VER_INVALID;
3363 is.read((char*)&version, 1);
3366 throw SerializationError("ServerMap::loadBlock(): Failed"
3367 " to read MapBlock version");
3369 /*u32 block_size = MapBlock::serializedLength(version);
3370 SharedBuffer<u8> data(block_size);
3371 is.read((char*)*data, block_size);*/
3373 // This will always return a sector because we're the server
3374 //MapSector *sector = emergeSector(p2d);
3376 MapBlock *block = NULL;
3377 bool created_new = false;
3378 block = sector->getBlockNoCreateNoEx(p3d.Y);
3381 block = sector->createBlankBlockNoInsert(p3d.Y);
3386 block->deSerialize(is, version);
3388 // Read extra data stored on disk
3389 block->deSerializeDiskExtra(is, version);
3391 // If it's a new block, insert it to the map
3393 sector->insertBlock(block);
3396 Save blocks loaded in old format in new format
3399 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3403 // Should be in database now, so delete the old file
3404 fs::RecursiveDelete(fullpath);
3407 // We just loaded it from the disk, so it's up-to-date.
3408 block->resetModified();
3411 catch(SerializationError &e)
3413 dstream<<"WARNING: Invalid block data on disk "
3414 <<"fullpath="<<fullpath
3415 <<" (SerializationError). "
3416 <<"what()="<<e.what()
3418 //" Ignoring. A new one will be generated.
3421 // TODO: Backup file; name is in fullpath.
3425 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3427 DSTACK(__FUNCTION_NAME);
3430 std::istringstream is(*blob, std::ios_base::binary);
3432 u8 version = SER_FMT_VER_INVALID;
3433 is.read((char*)&version, 1);
3436 throw SerializationError("ServerMap::loadBlock(): Failed"
3437 " to read MapBlock version");
3439 /*u32 block_size = MapBlock::serializedLength(version);
3440 SharedBuffer<u8> data(block_size);
3441 is.read((char*)*data, block_size);*/
3443 // This will always return a sector because we're the server
3444 //MapSector *sector = emergeSector(p2d);
3446 MapBlock *block = NULL;
3447 bool created_new = false;
3448 block = sector->getBlockNoCreateNoEx(p3d.Y);
3451 block = sector->createBlankBlockNoInsert(p3d.Y);
3456 block->deSerialize(is, version);
3458 // Read extra data stored on disk
3459 block->deSerializeDiskExtra(is, version);
3461 // If it's a new block, insert it to the map
3463 sector->insertBlock(block);
3466 Save blocks loaded in old format in new format
3469 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3474 // We just loaded it from, so it's up-to-date.
3475 block->resetModified();
3478 catch(SerializationError &e)
3480 dstream<<"WARNING: Invalid block data in database "
3481 <<" (SerializationError). "
3482 <<"what()="<<e.what()
3484 //" Ignoring. A new one will be generated.
3487 // TODO: Copy to a backup database.
3491 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3493 DSTACK(__FUNCTION_NAME);
3495 v2s16 p2d(blockpos.X, blockpos.Z);
3497 if(!loadFromFolders()) {
3500 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3501 dstream<<"WARNING: Could not bind block position for load: "
3502 <<sqlite3_errmsg(m_database)<<std::endl;
3503 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3505 Make sure sector is loaded
3507 MapSector *sector = createSector(p2d);
3512 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3513 size_t len = sqlite3_column_bytes(m_database_read, 0);
3515 std::string datastr(data, len);
3517 loadBlock(&datastr, blockpos, sector, false);
3519 sqlite3_step(m_database_read);
3520 // We should never get more than 1 row, so ok to reset
3521 sqlite3_reset(m_database_read);
3523 return getBlockNoCreateNoEx(blockpos);
3525 sqlite3_reset(m_database_read);
3527 // Not found in database, try the files
3530 // The directory layout we're going to load from.
3531 // 1 - original sectors/xxxxzzzz/
3532 // 2 - new sectors2/xxx/zzz/
3533 // If we load from anything but the latest structure, we will
3534 // immediately save to the new one, and remove the old.
3536 std::string sectordir1 = getSectorDir(p2d, 1);
3537 std::string sectordir;
3538 if(fs::PathExists(sectordir1))
3540 sectordir = sectordir1;
3545 sectordir = getSectorDir(p2d, 2);
3549 Make sure sector is loaded
3551 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3555 sector = loadSectorMeta(sectordir, loadlayout != 2);
3557 catch(InvalidFilenameException &e)
3561 catch(FileNotGoodException &e)
3565 catch(std::exception &e)
3572 Make sure file exists
3575 std::string blockfilename = getBlockFilename(blockpos);
3576 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3580 Load block and save it to the database
3582 loadBlock(sectordir, blockfilename, sector, true);
3583 return getBlockNoCreateNoEx(blockpos);
3586 void ServerMap::PrintInfo(std::ostream &out)
3597 ClientMap::ClientMap(
3599 MapDrawControl &control,
3600 scene::ISceneNode* parent,
3601 scene::ISceneManager* mgr,
3605 scene::ISceneNode(parent, mgr, id),
3608 m_camera_position(0,0,0),
3609 m_camera_direction(0,0,1),
3612 m_camera_mutex.Init();
3613 assert(m_camera_mutex.IsInitialized());
3615 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3616 BS*1000000,BS*1000000,BS*1000000);
3619 ClientMap::~ClientMap()
3621 /*JMutexAutoLock lock(mesh_mutex);
3630 MapSector * ClientMap::emergeSector(v2s16 p2d)
3632 DSTACK(__FUNCTION_NAME);
3633 // Check that it doesn't exist already
3635 return getSectorNoGenerate(p2d);
3637 catch(InvalidPositionException &e)
3642 ClientMapSector *sector = new ClientMapSector(this, p2d);
3645 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3646 m_sectors.insert(p2d, sector);
3653 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3655 DSTACK(__FUNCTION_NAME);
3656 ClientMapSector *sector = NULL;
3658 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3660 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3664 sector = (ClientMapSector*)n->getValue();
3665 assert(sector->getId() == MAPSECTOR_CLIENT);
3669 sector = new ClientMapSector(this, p2d);
3671 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3672 m_sectors.insert(p2d, sector);
3676 sector->deSerialize(is);
3680 void ClientMap::OnRegisterSceneNode()
3684 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3685 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3688 ISceneNode::OnRegisterSceneNode();
3691 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3693 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3694 DSTACK(__FUNCTION_NAME);
3696 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3699 This is called two times per frame, reset on the non-transparent one
3701 if(pass == scene::ESNRP_SOLID)
3703 m_last_drawn_sectors.clear();
3707 Get time for measuring timeout.
3709 Measuring time is very useful for long delays when the
3710 machine is swapping a lot.
3712 int time1 = time(0);
3714 //u32 daynight_ratio = m_client->getDayNightRatio();
3716 m_camera_mutex.Lock();
3717 v3f camera_position = m_camera_position;
3718 v3f camera_direction = m_camera_direction;
3719 f32 camera_fov = m_camera_fov;
3720 m_camera_mutex.Unlock();
3723 Get all blocks and draw all visible ones
3726 v3s16 cam_pos_nodes(
3727 camera_position.X / BS,
3728 camera_position.Y / BS,
3729 camera_position.Z / BS);
3731 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3733 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3734 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3736 // Take a fair amount as we will be dropping more out later
3738 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3739 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3740 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3742 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3743 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3744 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3746 u32 vertex_count = 0;
3748 // For limiting number of mesh updates per frame
3749 u32 mesh_update_count = 0;
3751 u32 blocks_would_have_drawn = 0;
3752 u32 blocks_drawn = 0;
3754 int timecheck_counter = 0;
3755 core::map<v2s16, MapSector*>::Iterator si;
3756 si = m_sectors.getIterator();
3757 for(; si.atEnd() == false; si++)
3760 timecheck_counter++;
3761 if(timecheck_counter > 50)
3763 timecheck_counter = 0;
3764 int time2 = time(0);
3765 if(time2 > time1 + 4)
3767 dstream<<"ClientMap::renderMap(): "
3768 "Rendering takes ages, returning."
3775 MapSector *sector = si.getNode()->getValue();
3776 v2s16 sp = sector->getPos();
3778 if(m_control.range_all == false)
3780 if(sp.X < p_blocks_min.X
3781 || sp.X > p_blocks_max.X
3782 || sp.Y < p_blocks_min.Z
3783 || sp.Y > p_blocks_max.Z)
3787 core::list< MapBlock * > sectorblocks;
3788 sector->getBlocks(sectorblocks);
3794 u32 sector_blocks_drawn = 0;
3796 core::list< MapBlock * >::Iterator i;
3797 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3799 MapBlock *block = *i;
3802 Compare block position to camera position, skip
3803 if not seen on display
3806 float range = 100000 * BS;
3807 if(m_control.range_all == false)
3808 range = m_control.wanted_range * BS;
3811 if(isBlockInSight(block->getPos(), camera_position,
3812 camera_direction, camera_fov,
3813 range, &d) == false)
3818 // Okay, this block will be drawn. Reset usage timer.
3819 block->resetUsageTimer();
3821 // This is ugly (spherical distance limit?)
3822 /*if(m_control.range_all == false &&
3823 d - 0.5*BS*MAP_BLOCKSIZE > range)
3828 Update expired mesh (used for day/night change)
3830 It doesn't work exactly like it should now with the
3831 tasked mesh update but whatever.
3834 bool mesh_expired = false;
3837 JMutexAutoLock lock(block->mesh_mutex);
3839 mesh_expired = block->getMeshExpired();
3841 // Mesh has not been expired and there is no mesh:
3842 // block has no content
3843 if(block->mesh == NULL && mesh_expired == false)
3847 f32 faraway = BS*50;
3848 //f32 faraway = m_control.wanted_range * BS;
3851 This has to be done with the mesh_mutex unlocked
3853 // Pretty random but this should work somewhat nicely
3854 if(mesh_expired && (
3855 (mesh_update_count < 3
3856 && (d < faraway || mesh_update_count < 2)
3859 (m_control.range_all && mesh_update_count < 20)
3862 /*if(mesh_expired && mesh_update_count < 6
3863 && (d < faraway || mesh_update_count < 3))*/
3865 mesh_update_count++;
3867 // Mesh has been expired: generate new mesh
3868 //block->updateMesh(daynight_ratio);
3869 m_client->addUpdateMeshTask(block->getPos());
3871 mesh_expired = false;
3876 Draw the faces of the block
3879 JMutexAutoLock lock(block->mesh_mutex);
3881 scene::SMesh *mesh = block->mesh;
3886 blocks_would_have_drawn++;
3887 if(blocks_drawn >= m_control.wanted_max_blocks
3888 && m_control.range_all == false
3889 && d > m_control.wanted_min_range * BS)
3893 sector_blocks_drawn++;
3895 u32 c = mesh->getMeshBufferCount();
3897 for(u32 i=0; i<c; i++)
3899 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3900 const video::SMaterial& material = buf->getMaterial();
3901 video::IMaterialRenderer* rnd =
3902 driver->getMaterialRenderer(material.MaterialType);
3903 bool transparent = (rnd && rnd->isTransparent());
3904 // Render transparent on transparent pass and likewise.
3905 if(transparent == is_transparent_pass)
3908 This *shouldn't* hurt too much because Irrlicht
3909 doesn't change opengl textures if the old
3910 material is set again.
3912 driver->setMaterial(buf->getMaterial());
3913 driver->drawMeshBuffer(buf);
3914 vertex_count += buf->getVertexCount();
3918 } // foreach sectorblocks
3920 if(sector_blocks_drawn != 0)
3922 m_last_drawn_sectors[sp] = true;
3926 m_control.blocks_drawn = blocks_drawn;
3927 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3929 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3930 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3933 void ClientMap::renderPostFx()
3935 // Sadly ISceneManager has no "post effects" render pass, in that case we
3936 // could just register for that and handle it in renderMap().
3938 m_camera_mutex.Lock();
3939 v3f camera_position = m_camera_position;
3940 m_camera_mutex.Unlock();
3942 MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
3944 // - If the player is in a solid node, make everything black.
3945 // - If the player is in liquid, draw a semi-transparent overlay.
3946 ContentFeatures& features = content_features(n);
3947 video::SColor post_effect_color = features.post_effect_color;
3948 if(features.solidness == 2 && g_settings.getBool("free_move") == false)
3950 post_effect_color = video::SColor(255, 0, 0, 0);
3952 if (post_effect_color.getAlpha() != 0)
3954 // Draw a full-screen rectangle
3955 video::IVideoDriver* driver = SceneManager->getVideoDriver();
3956 v2u32 ss = driver->getScreenSize();
3957 core::rect<s32> rect(0,0, ss.X, ss.Y);
3958 driver->draw2DRectangle(post_effect_color, rect);
3962 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3963 core::map<v3s16, MapBlock*> *affected_blocks)
3965 bool changed = false;
3967 Add it to all blocks touching it
3970 v3s16(0,0,0), // this
3971 v3s16(0,0,1), // back
3972 v3s16(0,1,0), // top
3973 v3s16(1,0,0), // right
3974 v3s16(0,0,-1), // front
3975 v3s16(0,-1,0), // bottom
3976 v3s16(-1,0,0), // left
3978 for(u16 i=0; i<7; i++)
3980 v3s16 p2 = p + dirs[i];
3981 // Block position of neighbor (or requested) node
3982 v3s16 blockpos = getNodeBlockPos(p2);
3983 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3984 if(blockref == NULL)
3986 // Relative position of requested node
3987 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3988 if(blockref->setTempMod(relpos, mod))
3993 if(changed && affected_blocks!=NULL)
3995 for(u16 i=0; i<7; i++)
3997 v3s16 p2 = p + dirs[i];
3998 // Block position of neighbor (or requested) node
3999 v3s16 blockpos = getNodeBlockPos(p2);
4000 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4001 if(blockref == NULL)
4003 affected_blocks->insert(blockpos, blockref);
4009 bool ClientMap::clearTempMod(v3s16 p,
4010 core::map<v3s16, MapBlock*> *affected_blocks)
4012 bool changed = false;
4014 v3s16(0,0,0), // this
4015 v3s16(0,0,1), // back
4016 v3s16(0,1,0), // top
4017 v3s16(1,0,0), // right
4018 v3s16(0,0,-1), // front
4019 v3s16(0,-1,0), // bottom
4020 v3s16(-1,0,0), // left
4022 for(u16 i=0; i<7; i++)
4024 v3s16 p2 = p + dirs[i];
4025 // Block position of neighbor (or requested) node
4026 v3s16 blockpos = getNodeBlockPos(p2);
4027 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4028 if(blockref == NULL)
4030 // Relative position of requested node
4031 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
4032 if(blockref->clearTempMod(relpos))
4037 if(changed && affected_blocks!=NULL)
4039 for(u16 i=0; i<7; i++)
4041 v3s16 p2 = p + dirs[i];
4042 // Block position of neighbor (or requested) node
4043 v3s16 blockpos = getNodeBlockPos(p2);
4044 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4045 if(blockref == NULL)
4047 affected_blocks->insert(blockpos, blockref);
4053 void ClientMap::expireMeshes(bool only_daynight_diffed)
4055 TimeTaker timer("expireMeshes()");
4057 core::map<v2s16, MapSector*>::Iterator si;
4058 si = m_sectors.getIterator();
4059 for(; si.atEnd() == false; si++)
4061 MapSector *sector = si.getNode()->getValue();
4063 core::list< MapBlock * > sectorblocks;
4064 sector->getBlocks(sectorblocks);
4066 core::list< MapBlock * >::Iterator i;
4067 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4069 MapBlock *block = *i;
4071 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4077 JMutexAutoLock lock(block->mesh_mutex);
4078 if(block->mesh != NULL)
4080 /*block->mesh->drop();
4081 block->mesh = NULL;*/
4082 block->setMeshExpired(true);
4089 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4091 assert(mapType() == MAPTYPE_CLIENT);
4094 v3s16 p = blockpos + v3s16(0,0,0);
4095 MapBlock *b = getBlockNoCreate(p);
4096 b->updateMesh(daynight_ratio);
4097 //b->setMeshExpired(true);
4099 catch(InvalidPositionException &e){}
4102 v3s16 p = blockpos + v3s16(-1,0,0);
4103 MapBlock *b = getBlockNoCreate(p);
4104 b->updateMesh(daynight_ratio);
4105 //b->setMeshExpired(true);
4107 catch(InvalidPositionException &e){}
4109 v3s16 p = blockpos + v3s16(0,-1,0);
4110 MapBlock *b = getBlockNoCreate(p);
4111 b->updateMesh(daynight_ratio);
4112 //b->setMeshExpired(true);
4114 catch(InvalidPositionException &e){}
4116 v3s16 p = blockpos + v3s16(0,0,-1);
4117 MapBlock *b = getBlockNoCreate(p);
4118 b->updateMesh(daynight_ratio);
4119 //b->setMeshExpired(true);
4121 catch(InvalidPositionException &e){}
4126 Update mesh of block in which the node is, and if the node is at the
4127 leading edge, update the appropriate leading blocks too.
4129 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4137 v3s16 blockposes[4];
4138 for(u32 i=0; i<4; i++)
4140 v3s16 np = nodepos + dirs[i];
4141 blockposes[i] = getNodeBlockPos(np);
4142 // Don't update mesh of block if it has been done already
4143 bool already_updated = false;
4144 for(u32 j=0; j<i; j++)
4146 if(blockposes[j] == blockposes[i])
4148 already_updated = true;
4155 MapBlock *b = getBlockNoCreate(blockposes[i]);
4156 b->updateMesh(daynight_ratio);
4161 void ClientMap::PrintInfo(std::ostream &out)
4172 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4177 MapVoxelManipulator::~MapVoxelManipulator()
4179 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4183 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4185 TimeTaker timer1("emerge", &emerge_time);
4187 // Units of these are MapBlocks
4188 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4189 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4191 VoxelArea block_area_nodes
4192 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4194 addArea(block_area_nodes);
4196 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4197 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4198 for(s32 x=p_min.X; x<=p_max.X; x++)
4201 core::map<v3s16, bool>::Node *n;
4202 n = m_loaded_blocks.find(p);
4206 bool block_data_inexistent = false;
4209 TimeTaker timer1("emerge load", &emerge_load_time);
4211 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4212 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4215 dstream<<std::endl;*/
4217 MapBlock *block = m_map->getBlockNoCreate(p);
4218 if(block->isDummy())
4219 block_data_inexistent = true;
4221 block->copyTo(*this);
4223 catch(InvalidPositionException &e)
4225 block_data_inexistent = true;
4228 if(block_data_inexistent)
4230 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4231 // Fill with VOXELFLAG_INEXISTENT
4232 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4233 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4235 s32 i = m_area.index(a.MinEdge.X,y,z);
4236 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4240 m_loaded_blocks.insert(p, !block_data_inexistent);
4243 //dstream<<"emerge done"<<std::endl;
4247 SUGG: Add an option to only update eg. water and air nodes.
4248 This will make it interfere less with important stuff if
4251 void MapVoxelManipulator::blitBack
4252 (core::map<v3s16, MapBlock*> & modified_blocks)
4254 if(m_area.getExtent() == v3s16(0,0,0))
4257 //TimeTaker timer1("blitBack");
4259 /*dstream<<"blitBack(): m_loaded_blocks.size()="
4260 <<m_loaded_blocks.size()<<std::endl;*/
4263 Initialize block cache
4265 v3s16 blockpos_last;
4266 MapBlock *block = NULL;
4267 bool block_checked_in_modified = false;
4269 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4270 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4271 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4275 u8 f = m_flags[m_area.index(p)];
4276 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4279 MapNode &n = m_data[m_area.index(p)];
4281 v3s16 blockpos = getNodeBlockPos(p);
4286 if(block == NULL || blockpos != blockpos_last){
4287 block = m_map->getBlockNoCreate(blockpos);
4288 blockpos_last = blockpos;
4289 block_checked_in_modified = false;
4292 // Calculate relative position in block
4293 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4295 // Don't continue if nothing has changed here
4296 if(block->getNode(relpos) == n)
4299 //m_map->setNode(m_area.MinEdge + p, n);
4300 block->setNode(relpos, n);
4303 Make sure block is in modified_blocks
4305 if(block_checked_in_modified == false)
4307 modified_blocks[blockpos] = block;
4308 block_checked_in_modified = true;
4311 catch(InvalidPositionException &e)
4317 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4318 MapVoxelManipulator(map),
4319 m_create_area(false)
4323 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4327 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4329 // Just create the area so that it can be pointed to
4330 VoxelManipulator::emerge(a, caller_id);
4333 void ManualMapVoxelManipulator::initialEmerge(
4334 v3s16 blockpos_min, v3s16 blockpos_max)
4336 TimeTaker timer1("initialEmerge", &emerge_time);
4338 // Units of these are MapBlocks
4339 v3s16 p_min = blockpos_min;
4340 v3s16 p_max = blockpos_max;
4342 VoxelArea block_area_nodes
4343 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4345 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4348 dstream<<"initialEmerge: area: ";
4349 block_area_nodes.print(dstream);
4350 dstream<<" ("<<size_MB<<"MB)";
4354 addArea(block_area_nodes);
4356 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4357 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4358 for(s32 x=p_min.X; x<=p_max.X; x++)
4361 core::map<v3s16, bool>::Node *n;
4362 n = m_loaded_blocks.find(p);
4366 bool block_data_inexistent = false;
4369 TimeTaker timer1("emerge load", &emerge_load_time);
4371 MapBlock *block = m_map->getBlockNoCreate(p);
4372 if(block->isDummy())
4373 block_data_inexistent = true;
4375 block->copyTo(*this);
4377 catch(InvalidPositionException &e)
4379 block_data_inexistent = true;
4382 if(block_data_inexistent)
4385 Mark area inexistent
4387 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4388 // Fill with VOXELFLAG_INEXISTENT
4389 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4390 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4392 s32 i = m_area.index(a.MinEdge.X,y,z);
4393 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4397 m_loaded_blocks.insert(p, !block_data_inexistent);
4401 void ManualMapVoxelManipulator::blitBackAll(
4402 core::map<v3s16, MapBlock*> * modified_blocks)
4404 if(m_area.getExtent() == v3s16(0,0,0))
4408 Copy data of all blocks
4410 for(core::map<v3s16, bool>::Iterator
4411 i = m_loaded_blocks.getIterator();
4412 i.atEnd() == false; i++)
4414 v3s16 p = i.getNode()->getKey();
4415 bool existed = i.getNode()->getValue();
4416 if(existed == false)
4418 // The Great Bug was found using this
4419 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4420 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4424 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4427 dstream<<"WARNING: "<<__FUNCTION_NAME
4428 <<": got NULL block "
4429 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4434 block->copyFrom(*this);
4437 modified_blocks->insert(p, block);