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)
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 setNodeMetadata(p, meta);
1021 If node is under sunlight and doesn't let sunlight through,
1022 take all sunlighted nodes under it and clear light from them
1023 and from where the light has been spread.
1024 TODO: This could be optimized by mass-unlighting instead
1027 if(node_under_sunlight && !content_features(n).sunlight_propagates)
1031 //m_dout<<DTIME<<"y="<<y<<std::endl;
1032 v3s16 n2pos(p.X, y, p.Z);
1036 n2 = getNode(n2pos);
1038 catch(InvalidPositionException &e)
1043 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1045 unLightNeighbors(LIGHTBANK_DAY,
1046 n2pos, n2.getLight(LIGHTBANK_DAY),
1047 light_sources, modified_blocks);
1048 n2.setLight(LIGHTBANK_DAY, 0);
1056 for(s32 i=0; i<2; i++)
1058 enum LightBank bank = banks[i];
1061 Spread light from all nodes that might be capable of doing so
1063 spreadLight(bank, light_sources, modified_blocks);
1067 Update information about whether day and night light differ
1069 for(core::map<v3s16, MapBlock*>::Iterator
1070 i = modified_blocks.getIterator();
1071 i.atEnd() == false; i++)
1073 MapBlock *block = i.getNode()->getValue();
1074 block->updateDayNightDiff();
1078 Add neighboring liquid nodes and the node itself if it is
1079 liquid (=water node was added) to transform queue.
1082 v3s16(0,0,0), // self
1083 v3s16(0,0,1), // back
1084 v3s16(0,1,0), // top
1085 v3s16(1,0,0), // right
1086 v3s16(0,0,-1), // front
1087 v3s16(0,-1,0), // bottom
1088 v3s16(-1,0,0), // left
1090 for(u16 i=0; i<7; i++)
1095 v3s16 p2 = p + dirs[i];
1097 MapNode n2 = getNode(p2);
1098 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1100 m_transforming_liquid.push_back(p2);
1103 }catch(InvalidPositionException &e)
1111 void Map::removeNodeAndUpdate(v3s16 p,
1112 core::map<v3s16, MapBlock*> &modified_blocks)
1114 /*PrintInfo(m_dout);
1115 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1116 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1118 bool node_under_sunlight = true;
1120 v3s16 toppos = p + v3s16(0,1,0);
1122 // Node will be replaced with this
1123 content_t replace_material = CONTENT_AIR;
1126 If there is a node at top and it doesn't have sunlight,
1127 there will be no sunlight going down.
1130 MapNode topnode = getNode(toppos);
1132 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1133 node_under_sunlight = false;
1135 catch(InvalidPositionException &e)
1139 core::map<v3s16, bool> light_sources;
1141 enum LightBank banks[] =
1146 for(s32 i=0; i<2; i++)
1148 enum LightBank bank = banks[i];
1151 Unlight neighbors (in case the node is a light source)
1153 unLightNeighbors(bank, p,
1154 getNode(p).getLight(bank),
1155 light_sources, modified_blocks);
1159 Remove node metadata
1162 removeNodeMetadata(p);
1166 This also clears the lighting.
1170 n.setContent(replace_material);
1173 for(s32 i=0; i<2; i++)
1175 enum LightBank bank = banks[i];
1178 Recalculate lighting
1180 spreadLight(bank, light_sources, modified_blocks);
1183 // Add the block of the removed node to modified_blocks
1184 v3s16 blockpos = getNodeBlockPos(p);
1185 MapBlock * block = getBlockNoCreate(blockpos);
1186 assert(block != NULL);
1187 modified_blocks.insert(blockpos, block);
1190 If the removed node was under sunlight, propagate the
1191 sunlight down from it and then light all neighbors
1192 of the propagated blocks.
1194 if(node_under_sunlight)
1196 s16 ybottom = propagateSunlight(p, modified_blocks);
1197 /*m_dout<<DTIME<<"Node was under sunlight. "
1198 "Propagating sunlight";
1199 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1201 for(; y >= ybottom; y--)
1203 v3s16 p2(p.X, y, p.Z);
1204 /*m_dout<<DTIME<<"lighting neighbors of node ("
1205 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1207 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1212 // Set the lighting of this node to 0
1213 // TODO: Is this needed? Lighting is cleared up there already.
1215 MapNode n = getNode(p);
1216 n.setLight(LIGHTBANK_DAY, 0);
1219 catch(InvalidPositionException &e)
1225 for(s32 i=0; i<2; i++)
1227 enum LightBank bank = banks[i];
1229 // Get the brightest neighbour node and propagate light from it
1230 v3s16 n2p = getBrightestNeighbour(bank, p);
1232 MapNode n2 = getNode(n2p);
1233 lightNeighbors(bank, n2p, modified_blocks);
1235 catch(InvalidPositionException &e)
1241 Update information about whether day and night light differ
1243 for(core::map<v3s16, MapBlock*>::Iterator
1244 i = modified_blocks.getIterator();
1245 i.atEnd() == false; i++)
1247 MapBlock *block = i.getNode()->getValue();
1248 block->updateDayNightDiff();
1252 Add neighboring liquid nodes and this node to transform queue.
1253 (it's vital for the node itself to get updated last.)
1256 v3s16(0,0,1), // back
1257 v3s16(0,1,0), // top
1258 v3s16(1,0,0), // right
1259 v3s16(0,0,-1), // front
1260 v3s16(0,-1,0), // bottom
1261 v3s16(-1,0,0), // left
1262 v3s16(0,0,0), // self
1264 for(u16 i=0; i<7; i++)
1269 v3s16 p2 = p + dirs[i];
1271 MapNode n2 = getNode(p2);
1272 if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR)
1274 m_transforming_liquid.push_back(p2);
1277 }catch(InvalidPositionException &e)
1283 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1286 event.type = MEET_ADDNODE;
1290 bool succeeded = true;
1292 core::map<v3s16, MapBlock*> modified_blocks;
1293 addNodeAndUpdate(p, n, modified_blocks);
1295 // Copy modified_blocks to event
1296 for(core::map<v3s16, MapBlock*>::Iterator
1297 i = modified_blocks.getIterator();
1298 i.atEnd()==false; i++)
1300 event.modified_blocks.insert(i.getNode()->getKey(), false);
1303 catch(InvalidPositionException &e){
1307 dispatchEvent(&event);
1312 bool Map::removeNodeWithEvent(v3s16 p)
1315 event.type = MEET_REMOVENODE;
1318 bool succeeded = true;
1320 core::map<v3s16, MapBlock*> modified_blocks;
1321 removeNodeAndUpdate(p, modified_blocks);
1323 // Copy modified_blocks to event
1324 for(core::map<v3s16, MapBlock*>::Iterator
1325 i = modified_blocks.getIterator();
1326 i.atEnd()==false; i++)
1328 event.modified_blocks.insert(i.getNode()->getKey(), false);
1331 catch(InvalidPositionException &e){
1335 dispatchEvent(&event);
1340 bool Map::dayNightDiffed(v3s16 blockpos)
1343 v3s16 p = blockpos + v3s16(0,0,0);
1344 MapBlock *b = getBlockNoCreate(p);
1345 if(b->dayNightDiffed())
1348 catch(InvalidPositionException &e){}
1351 v3s16 p = blockpos + v3s16(-1,0,0);
1352 MapBlock *b = getBlockNoCreate(p);
1353 if(b->dayNightDiffed())
1356 catch(InvalidPositionException &e){}
1358 v3s16 p = blockpos + v3s16(0,-1,0);
1359 MapBlock *b = getBlockNoCreate(p);
1360 if(b->dayNightDiffed())
1363 catch(InvalidPositionException &e){}
1365 v3s16 p = blockpos + v3s16(0,0,-1);
1366 MapBlock *b = getBlockNoCreate(p);
1367 if(b->dayNightDiffed())
1370 catch(InvalidPositionException &e){}
1373 v3s16 p = blockpos + v3s16(1,0,0);
1374 MapBlock *b = getBlockNoCreate(p);
1375 if(b->dayNightDiffed())
1378 catch(InvalidPositionException &e){}
1380 v3s16 p = blockpos + v3s16(0,1,0);
1381 MapBlock *b = getBlockNoCreate(p);
1382 if(b->dayNightDiffed())
1385 catch(InvalidPositionException &e){}
1387 v3s16 p = blockpos + v3s16(0,0,1);
1388 MapBlock *b = getBlockNoCreate(p);
1389 if(b->dayNightDiffed())
1392 catch(InvalidPositionException &e){}
1398 Updates usage timers
1400 void Map::timerUpdate(float dtime, float unload_timeout,
1401 core::list<v3s16> *unloaded_blocks)
1403 bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
1405 core::list<v2s16> sector_deletion_queue;
1406 u32 deleted_blocks_count = 0;
1407 u32 saved_blocks_count = 0;
1409 core::map<v2s16, MapSector*>::Iterator si;
1412 si = m_sectors.getIterator();
1413 for(; si.atEnd() == false; si++)
1415 MapSector *sector = si.getNode()->getValue();
1417 bool all_blocks_deleted = true;
1419 core::list<MapBlock*> blocks;
1420 sector->getBlocks(blocks);
1422 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1423 i != blocks.end(); i++)
1425 MapBlock *block = (*i);
1427 block->incrementUsageTimer(dtime);
1429 if(block->getUsageTimer() > unload_timeout)
1431 v3s16 p = block->getPos();
1434 if(block->getModified() != MOD_STATE_CLEAN
1435 && save_before_unloading)
1438 saved_blocks_count++;
1441 // Delete from memory
1442 sector->deleteBlock(block);
1445 unloaded_blocks->push_back(p);
1447 deleted_blocks_count++;
1451 all_blocks_deleted = false;
1455 if(all_blocks_deleted)
1457 sector_deletion_queue.push_back(si.getNode()->getKey());
1462 // Finally delete the empty sectors
1463 deleteSectors(sector_deletion_queue);
1465 if(deleted_blocks_count != 0)
1467 PrintInfo(dstream); // ServerMap/ClientMap:
1468 dstream<<"Unloaded "<<deleted_blocks_count
1469 <<" blocks from memory";
1470 if(save_before_unloading)
1471 dstream<<", of which "<<saved_blocks_count<<" were written";
1472 dstream<<"."<<std::endl;
1476 void Map::deleteSectors(core::list<v2s16> &list)
1478 core::list<v2s16>::Iterator j;
1479 for(j=list.begin(); j!=list.end(); j++)
1481 MapSector *sector = m_sectors[*j];
1482 // If sector is in sector cache, remove it from there
1483 if(m_sector_cache == sector)
1484 m_sector_cache = NULL;
1485 // Remove from map and delete
1486 m_sectors.remove(*j);
1492 void Map::unloadUnusedData(float timeout,
1493 core::list<v3s16> *deleted_blocks)
1495 core::list<v2s16> sector_deletion_queue;
1496 u32 deleted_blocks_count = 0;
1497 u32 saved_blocks_count = 0;
1499 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1500 for(; si.atEnd() == false; si++)
1502 MapSector *sector = si.getNode()->getValue();
1504 bool all_blocks_deleted = true;
1506 core::list<MapBlock*> blocks;
1507 sector->getBlocks(blocks);
1508 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1509 i != blocks.end(); i++)
1511 MapBlock *block = (*i);
1513 if(block->getUsageTimer() > timeout)
1516 if(block->getModified() != MOD_STATE_CLEAN)
1519 saved_blocks_count++;
1521 // Delete from memory
1522 sector->deleteBlock(block);
1523 deleted_blocks_count++;
1527 all_blocks_deleted = false;
1531 if(all_blocks_deleted)
1533 sector_deletion_queue.push_back(si.getNode()->getKey());
1537 deleteSectors(sector_deletion_queue);
1539 dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
1540 <<", of which "<<saved_blocks_count<<" were wr."
1543 //return sector_deletion_queue.getSize();
1544 //return deleted_blocks_count;
1548 void Map::PrintInfo(std::ostream &out)
1553 #define WATER_DROP_BOOST 4
1557 NEIGHBOR_SAME_LEVEL,
1560 struct NodeNeighbor {
1566 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1568 DSTACK(__FUNCTION_NAME);
1569 //TimeTaker timer("transformLiquids()");
1572 u32 initial_size = m_transforming_liquid.size();
1574 /*if(initial_size != 0)
1575 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1577 // list of nodes that due to viscosity have not reached their max level height
1578 UniqueQueue<v3s16> must_reflow;
1580 // List of MapBlocks that will require a lighting update (due to lava)
1581 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1583 while(m_transforming_liquid.size() != 0)
1585 // This should be done here so that it is done when continue is used
1586 if(loopcount >= initial_size * 3)
1591 Get a queued transforming liquid node
1593 v3s16 p0 = m_transforming_liquid.pop_front();
1595 MapNode n0 = getNodeNoEx(p0);
1598 Collect information about current node
1600 s8 liquid_level = -1;
1601 u8 liquid_kind = CONTENT_IGNORE;
1602 LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
1603 switch (liquid_type) {
1605 liquid_level = LIQUID_LEVEL_SOURCE;
1606 liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
1608 case LIQUID_FLOWING:
1609 liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
1610 liquid_kind = n0.getContent();
1613 // if this is an air node, it *could* be transformed into a liquid. otherwise,
1614 // continue with the next node.
1615 if (n0.getContent() != CONTENT_AIR)
1617 liquid_kind = CONTENT_AIR;
1622 Collect information about the environment
1624 const v3s16 *dirs = g_6dirs;
1625 NodeNeighbor sources[6]; // surrounding sources
1626 int num_sources = 0;
1627 NodeNeighbor flows[6]; // surrounding flowing liquid nodes
1629 NodeNeighbor airs[6]; // surrounding air
1631 NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
1632 int num_neutrals = 0;
1633 bool flowing_down = false;
1634 for (u16 i = 0; i < 6; i++) {
1635 NeighborType nt = NEIGHBOR_SAME_LEVEL;
1638 nt = NEIGHBOR_UPPER;
1641 nt = NEIGHBOR_LOWER;
1644 v3s16 npos = p0 + dirs[i];
1645 NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
1646 switch (content_features(nb.n.getContent()).liquid_type) {
1648 if (nb.n.getContent() == CONTENT_AIR) {
1649 airs[num_airs++] = nb;
1650 // if the current node is a water source the neighbor
1651 // should be enqueded for transformation regardless of whether the
1652 // current node changes or not.
1653 if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
1654 m_transforming_liquid.push_back(npos);
1655 // if the current node happens to be a flowing node, it will start to flow down here.
1656 if (nb.t == NEIGHBOR_LOWER) {
1657 flowing_down = true;
1660 neutrals[num_neutrals++] = nb;
1664 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1665 if (liquid_kind == CONTENT_AIR)
1666 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1667 if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
1668 neutrals[num_neutrals++] = nb;
1670 sources[num_sources++] = nb;
1673 case LIQUID_FLOWING:
1674 // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
1675 if (liquid_kind == CONTENT_AIR)
1676 liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
1677 if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
1678 neutrals[num_neutrals++] = nb;
1680 flows[num_flows++] = nb;
1681 if (nb.t == NEIGHBOR_LOWER)
1682 flowing_down = true;
1689 decide on the type (and possibly level) of the current node
1691 content_t new_node_content;
1692 s8 new_node_level = -1;
1693 s8 max_node_level = -1;
1694 if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
1695 // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
1696 // or the flowing alternative of the first of the surrounding sources (if it's air), so
1697 // it's perfectly safe to use liquid_kind here to determine the new node content.
1698 new_node_content = content_features(liquid_kind).liquid_alternative_source;
1699 } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
1700 // liquid_kind is set properly, see above
1701 new_node_content = liquid_kind;
1702 max_node_level = new_node_level = LIQUID_LEVEL_MAX;
1704 // no surrounding sources, so get the maximum level that can flow into this node
1705 for (u16 i = 0; i < num_flows; i++) {
1706 u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
1707 switch (flows[i].t) {
1708 case NEIGHBOR_UPPER:
1709 if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
1710 max_node_level = LIQUID_LEVEL_MAX;
1711 if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
1712 max_node_level = nb_liquid_level + WATER_DROP_BOOST;
1713 } else if (nb_liquid_level > max_node_level)
1714 max_node_level = nb_liquid_level;
1716 case NEIGHBOR_LOWER:
1718 case NEIGHBOR_SAME_LEVEL:
1719 if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
1720 nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
1721 max_node_level = nb_liquid_level - 1;
1727 u8 viscosity = content_features(liquid_kind).liquid_viscosity;
1728 if (viscosity > 1 && max_node_level != liquid_level) {
1729 // amount to gain, limited by viscosity
1730 // must be at least 1 in absolute value
1731 s8 level_inc = max_node_level - liquid_level;
1732 if (level_inc < -viscosity || level_inc > viscosity)
1733 new_node_level = liquid_level + level_inc/viscosity;
1734 else if (level_inc < 0)
1735 new_node_level = liquid_level - 1;
1736 else if (level_inc > 0)
1737 new_node_level = liquid_level + 1;
1738 if (new_node_level != max_node_level)
1739 must_reflow.push_back(p0);
1741 new_node_level = max_node_level;
1743 if (new_node_level >= 0)
1744 new_node_content = liquid_kind;
1746 new_node_content = CONTENT_AIR;
1751 check if anything has changed. if not, just continue with the next node.
1753 if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
1754 ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
1755 ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
1761 update the current node
1763 bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
1764 if (content_features(new_node_content).liquid_type == LIQUID_FLOWING) {
1765 // set level to last 3 bits, flowing down bit to 4th bit
1766 n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
1768 // set the liquid level and flow bit to 0
1769 n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
1771 n0.setContent(new_node_content);
1773 v3s16 blockpos = getNodeBlockPos(p0);
1774 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1776 modified_blocks.insert(blockpos, block);
1777 // If node emits light, MapBlock requires lighting update
1778 if(content_features(n0).light_source != 0)
1779 lighting_modified_blocks[block->getPos()] = block;
1783 enqueue neighbors for update if neccessary
1785 switch (content_features(n0.getContent()).liquid_type) {
1787 case LIQUID_FLOWING:
1788 // make sure source flows into all neighboring nodes
1789 for (u16 i = 0; i < num_flows; i++)
1790 if (flows[i].t != NEIGHBOR_UPPER)
1791 m_transforming_liquid.push_back(flows[i].p);
1792 for (u16 i = 0; i < num_airs; i++)
1793 if (airs[i].t != NEIGHBOR_UPPER)
1794 m_transforming_liquid.push_back(airs[i].p);
1797 // this flow has turned to air; neighboring flows might need to do the same
1798 for (u16 i = 0; i < num_flows; i++)
1799 m_transforming_liquid.push_back(flows[i].p);
1803 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1804 while (must_reflow.size() > 0)
1805 m_transforming_liquid.push_back(must_reflow.pop_front());
1806 updateLighting(lighting_modified_blocks, modified_blocks);
1809 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1811 v3s16 blockpos = getNodeBlockPos(p);
1812 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1813 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1816 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1820 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1824 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1826 v3s16 blockpos = getNodeBlockPos(p);
1827 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1828 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1831 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1835 block->m_node_metadata.set(p_rel, meta);
1838 void Map::removeNodeMetadata(v3s16 p)
1840 v3s16 blockpos = getNodeBlockPos(p);
1841 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1842 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1845 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1849 block->m_node_metadata.remove(p_rel);
1852 void Map::nodeMetadataStep(float dtime,
1853 core::map<v3s16, MapBlock*> &changed_blocks)
1857 Currently there is no way to ensure that all the necessary
1858 blocks are loaded when this is run. (They might get unloaded)
1859 NOTE: ^- Actually, that might not be so. In a quick test it
1860 reloaded a block with a furnace when I walked back to it from
1863 core::map<v2s16, MapSector*>::Iterator si;
1864 si = m_sectors.getIterator();
1865 for(; si.atEnd() == false; si++)
1867 MapSector *sector = si.getNode()->getValue();
1868 core::list< MapBlock * > sectorblocks;
1869 sector->getBlocks(sectorblocks);
1870 core::list< MapBlock * >::Iterator i;
1871 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1873 MapBlock *block = *i;
1874 bool changed = block->m_node_metadata.step(dtime);
1876 changed_blocks[block->getPos()] = block;
1885 ServerMap::ServerMap(std::string savedir):
1888 m_map_metadata_changed(true),
1890 m_database_read(NULL),
1891 m_database_write(NULL)
1893 dstream<<__FUNCTION_NAME<<std::endl;
1895 //m_chunksize = 8; // Takes a few seconds
1897 if (g_settings.get("fixed_map_seed").empty())
1899 m_seed = (((u64)(myrand()%0xffff)<<0)
1900 + ((u64)(myrand()%0xffff)<<16)
1901 + ((u64)(myrand()%0xffff)<<32)
1902 + ((u64)(myrand()%0xffff)<<48));
1906 m_seed = g_settings.getU64("fixed_map_seed");
1910 Experimental and debug stuff
1917 Try to load map; if not found, create a new one.
1920 m_savedir = savedir;
1921 m_map_saving_enabled = false;
1925 // If directory exists, check contents and load if possible
1926 if(fs::PathExists(m_savedir))
1928 // If directory is empty, it is safe to save into it.
1929 if(fs::GetDirListing(m_savedir).size() == 0)
1931 dstream<<DTIME<<"Server: Empty save directory is valid."
1933 m_map_saving_enabled = true;
1938 // Load map metadata (seed, chunksize)
1941 catch(FileNotGoodException &e){
1942 dstream<<DTIME<<"WARNING: Could not load map metadata"
1943 //<<" Disabling chunk-based generator."
1949 // Load chunk metadata
1952 catch(FileNotGoodException &e){
1953 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1954 <<" Disabling chunk-based generator."
1959 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1960 "metadata and sector (0,0) from "<<savedir<<
1961 ", assuming valid save directory."
1964 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1965 <<"and chunk metadata from "<<savedir
1966 <<", assuming valid save directory."
1969 m_map_saving_enabled = true;
1970 // Map loaded, not creating new one
1974 // If directory doesn't exist, it is safe to save to it
1976 m_map_saving_enabled = true;
1979 catch(std::exception &e)
1981 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1982 <<", exception: "<<e.what()<<std::endl;
1983 dstream<<"Please remove the map or fix it."<<std::endl;
1984 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1987 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1989 // Create zero sector
1990 emergeSector(v2s16(0,0));
1992 // Initially write whole map
1996 ServerMap::~ServerMap()
1998 dstream<<__FUNCTION_NAME<<std::endl;
2002 if(m_map_saving_enabled)
2004 // Save only changed parts
2006 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2010 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2013 catch(std::exception &e)
2015 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2016 <<", exception: "<<e.what()<<std::endl;
2020 Close database if it was opened
2023 sqlite3_finalize(m_database_read);
2024 if(m_database_write)
2025 sqlite3_finalize(m_database_write);
2027 sqlite3_close(m_database);
2033 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2034 for(; i.atEnd() == false; i++)
2036 MapChunk *chunk = i.getNode()->getValue();
2042 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2044 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2045 if(enable_mapgen_debug_info)
2046 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2047 <<blockpos.Z<<")"<<std::endl;
2049 // Do nothing if not inside limits (+-1 because of neighbors)
2050 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2051 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2057 data->no_op = false;
2058 data->seed = m_seed;
2059 data->blockpos = blockpos;
2062 Create the whole area of this and the neighboring blocks
2065 //TimeTaker timer("initBlockMake() create area");
2067 for(s16 x=-1; x<=1; x++)
2068 for(s16 z=-1; z<=1; z++)
2070 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2071 // Sector metadata is loaded from disk if not already loaded.
2072 ServerMapSector *sector = createSector(sectorpos);
2075 for(s16 y=-1; y<=1; y++)
2077 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2078 //MapBlock *block = createBlock(p);
2079 // 1) get from memory, 2) load from disk
2080 MapBlock *block = emergeBlock(p, false);
2081 // 3) create a blank one
2084 block = createBlock(p);
2087 Block gets sunlight if this is true.
2089 Refer to the map generator heuristics.
2091 bool ug = mapgen::block_is_underground(data->seed, p);
2092 block->setIsUnderground(ug);
2095 // Lighting will not be valid after make_chunk is called
2096 block->setLightingExpired(true);
2097 // Lighting will be calculated
2098 //block->setLightingExpired(false);
2104 Now we have a big empty area.
2106 Make a ManualMapVoxelManipulator that contains this and the
2110 // The area that contains this block and it's neighbors
2111 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2112 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2114 data->vmanip = new ManualMapVoxelManipulator(this);
2115 //data->vmanip->setMap(this);
2119 //TimeTaker timer("initBlockMake() initialEmerge");
2120 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2123 // Data is ready now.
2126 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2127 core::map<v3s16, MapBlock*> &changed_blocks)
2129 v3s16 blockpos = data->blockpos;
2130 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2131 <<blockpos.Z<<")"<<std::endl;*/
2135 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2139 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2141 /*dstream<<"Resulting vmanip:"<<std::endl;
2142 data->vmanip.print(dstream);*/
2145 Blit generated stuff to map
2146 NOTE: blitBackAll adds nearly everything to changed_blocks
2150 //TimeTaker timer("finishBlockMake() blitBackAll");
2151 data->vmanip->blitBackAll(&changed_blocks);
2154 if(enable_mapgen_debug_info)
2155 dstream<<"finishBlockMake: changed_blocks.size()="
2156 <<changed_blocks.size()<<std::endl;
2159 Copy transforming liquid information
2161 while(data->transforming_liquid.size() > 0)
2163 v3s16 p = data->transforming_liquid.pop_front();
2164 m_transforming_liquid.push_back(p);
2170 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2174 Set is_underground flag for lighting with sunlight.
2176 Refer to map generator heuristics.
2178 NOTE: This is done in initChunkMake
2180 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2184 Add sunlight to central block.
2185 This makes in-dark-spawning monsters to not flood the whole thing.
2186 Do not spread the light, though.
2188 /*core::map<v3s16, bool> light_sources;
2189 bool black_air_left = false;
2190 block->propagateSunlight(light_sources, true, &black_air_left);*/
2193 NOTE: Lighting and object adding shouldn't really be here, but
2194 lighting is a bit tricky to move properly to makeBlock.
2195 TODO: Do this the right way anyway, that is, move it to makeBlock.
2196 - There needs to be some way for makeBlock to report back if
2197 the lighting update is going further down because of the
2198 new block blocking light
2203 NOTE: This takes ~60ms, TODO: Investigate why
2206 TimeTaker t("finishBlockMake lighting update");
2208 core::map<v3s16, MapBlock*> lighting_update_blocks;
2211 lighting_update_blocks.insert(block->getPos(), block);
2216 v3s16 p = block->getPos()+v3s16(x,1,z);
2217 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2221 // All modified blocks
2222 // NOTE: Should this be done? If this is not done, then the lighting
2223 // of the others will be updated in a different place, one by one, i
2224 // think... or they might not? Well, at least they are left marked as
2225 // "lighting expired"; it seems that is not handled at all anywhere,
2226 // so enabling this will slow it down A LOT because otherwise it
2227 // would not do this at all. This causes the black trees.
2228 for(core::map<v3s16, MapBlock*>::Iterator
2229 i = changed_blocks.getIterator();
2230 i.atEnd() == false; i++)
2232 lighting_update_blocks.insert(i.getNode()->getKey(),
2233 i.getNode()->getValue());
2235 /*// Also force-add all the upmost blocks for proper sunlight
2236 for(s16 x=-1; x<=1; x++)
2237 for(s16 z=-1; z<=1; z++)
2239 v3s16 p = block->getPos()+v3s16(x,1,z);
2240 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2243 updateLighting(lighting_update_blocks, changed_blocks);
2246 Set lighting to non-expired state in all of them.
2247 This is cheating, but it is not fast enough if all of them
2248 would actually be updated.
2250 for(s16 x=-1; x<=1; x++)
2251 for(s16 y=-1; y<=1; y++)
2252 for(s16 z=-1; z<=1; z++)
2254 v3s16 p = block->getPos()+v3s16(x,y,z);
2255 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2258 if(enable_mapgen_debug_info == false)
2259 t.stop(true); // Hide output
2263 Add random objects to block
2265 mapgen::add_random_objects(block);
2268 Go through changed blocks
2270 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2271 i.atEnd() == false; i++)
2273 MapBlock *block = i.getNode()->getValue();
2276 Update day/night difference cache of the MapBlocks
2278 block->updateDayNightDiff();
2280 Set block as modified
2282 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2286 Set central block as generated
2288 block->setGenerated(true);
2291 Save changed parts of map
2292 NOTE: Will be saved later.
2296 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2297 <<blockpos.Z<<")"<<std::endl;*/
2299 if(enable_mapgen_debug_info)
2302 Analyze resulting blocks
2304 for(s16 x=-1; x<=1; x++)
2305 for(s16 y=-1; y<=1; y++)
2306 for(s16 z=-1; z<=1; z++)
2308 v3s16 p = block->getPos()+v3s16(x,y,z);
2309 MapBlock *block = getBlockNoCreateNoEx(p);
2311 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2312 dstream<<"Generated "<<spos<<": "
2313 <<analyze_block(block)<<std::endl;
2321 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2323 DSTACKF("%s: p2d=(%d,%d)",
2328 Check if it exists already in memory
2330 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2335 Try to load it from disk (with blocks)
2337 //if(loadSectorFull(p2d) == true)
2340 Try to load metadata from disk
2343 if(loadSectorMeta(p2d) == true)
2345 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2348 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2349 throw InvalidPositionException("");
2355 Do not create over-limit
2357 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2358 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2359 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2360 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2361 throw InvalidPositionException("createSector(): pos. over limit");
2364 Generate blank sector
2367 sector = new ServerMapSector(this, p2d);
2369 // Sector position on map in nodes
2370 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2375 m_sectors.insert(p2d, sector);
2381 This is a quick-hand function for calling makeBlock().
2383 MapBlock * ServerMap::generateBlock(
2385 core::map<v3s16, MapBlock*> &modified_blocks
2388 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2390 /*dstream<<"generateBlock(): "
2391 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2394 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2396 TimeTaker timer("generateBlock");
2398 //MapBlock *block = original_dummy;
2400 v2s16 p2d(p.X, p.Z);
2401 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2404 Do not generate over-limit
2406 if(blockpos_over_limit(p))
2408 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2409 throw InvalidPositionException("generateBlock(): pos. over limit");
2413 Create block make data
2415 mapgen::BlockMakeData data;
2416 initBlockMake(&data, p);
2422 TimeTaker t("mapgen::make_block()");
2423 mapgen::make_block(&data);
2425 if(enable_mapgen_debug_info == false)
2426 t.stop(true); // Hide output
2430 Blit data back on map, update lighting, add mobs and whatever this does
2432 finishBlockMake(&data, modified_blocks);
2437 MapBlock *block = getBlockNoCreateNoEx(p);
2445 bool erroneus_content = false;
2446 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2447 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2448 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2451 MapNode n = block->getNode(p);
2452 if(n.getContent() == CONTENT_IGNORE)
2454 dstream<<"CONTENT_IGNORE at "
2455 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2457 erroneus_content = true;
2461 if(erroneus_content)
2470 Generate a completely empty block
2474 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2475 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2477 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2481 n.setContent(CONTENT_AIR);
2483 n.setContent(CONTENT_STONE);
2484 block->setNode(v3s16(x0,y0,z0), n);
2490 if(enable_mapgen_debug_info == false)
2491 timer.stop(true); // Hide output
2496 MapBlock * ServerMap::createBlock(v3s16 p)
2498 DSTACKF("%s: p=(%d,%d,%d)",
2499 __FUNCTION_NAME, p.X, p.Y, p.Z);
2502 Do not create over-limit
2504 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2505 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2506 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2507 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2508 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2509 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2510 throw InvalidPositionException("createBlock(): pos. over limit");
2512 v2s16 p2d(p.X, p.Z);
2515 This will create or load a sector if not found in memory.
2516 If block exists on disk, it will be loaded.
2518 NOTE: On old save formats, this will be slow, as it generates
2519 lighting on blocks for them.
2521 ServerMapSector *sector;
2523 sector = (ServerMapSector*)createSector(p2d);
2524 assert(sector->getId() == MAPSECTOR_SERVER);
2526 catch(InvalidPositionException &e)
2528 dstream<<"createBlock: createSector() failed"<<std::endl;
2532 NOTE: This should not be done, or at least the exception
2533 should not be passed on as std::exception, because it
2534 won't be catched at all.
2536 /*catch(std::exception &e)
2538 dstream<<"createBlock: createSector() failed: "
2539 <<e.what()<<std::endl;
2544 Try to get a block from the sector
2547 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2550 if(block->isDummy())
2555 block = sector->createBlankBlock(block_y);
2559 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2561 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2563 p.X, p.Y, p.Z, allow_generate);
2566 MapBlock *block = getBlockNoCreateNoEx(p);
2567 if(block && block->isDummy() == false)
2572 MapBlock *block = loadBlock(p);
2579 core::map<v3s16, MapBlock*> modified_blocks;
2580 MapBlock *block = generateBlock(p, modified_blocks);
2584 event.type = MEET_OTHER;
2587 // Copy modified_blocks to event
2588 for(core::map<v3s16, MapBlock*>::Iterator
2589 i = modified_blocks.getIterator();
2590 i.atEnd()==false; i++)
2592 event.modified_blocks.insert(i.getNode()->getKey(), false);
2596 dispatchEvent(&event);
2607 Do not generate over-limit
2609 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2610 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2611 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2612 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2613 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2614 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2615 throw InvalidPositionException("emergeBlock(): pos. over limit");
2617 v2s16 p2d(p.X, p.Z);
2620 This will create or load a sector if not found in memory.
2621 If block exists on disk, it will be loaded.
2623 ServerMapSector *sector;
2625 sector = createSector(p2d);
2626 //sector = emergeSector(p2d, changed_blocks);
2628 catch(InvalidPositionException &e)
2630 dstream<<"emergeBlock: createSector() failed: "
2631 <<e.what()<<std::endl;
2632 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2634 <<"You could try to delete it."<<std::endl;
2637 catch(VersionMismatchException &e)
2639 dstream<<"emergeBlock: createSector() failed: "
2640 <<e.what()<<std::endl;
2641 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2643 <<"You could try to delete it."<<std::endl;
2648 Try to get a block from the sector
2651 bool does_not_exist = false;
2652 bool lighting_expired = false;
2653 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2655 // If not found, try loading from disk
2658 block = loadBlock(p);
2664 does_not_exist = true;
2666 else if(block->isDummy() == true)
2668 does_not_exist = true;
2670 else if(block->getLightingExpired())
2672 lighting_expired = true;
2677 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2682 If block was not found on disk and not going to generate a
2683 new one, make sure there is a dummy block in place.
2685 if(only_from_disk && (does_not_exist || lighting_expired))
2687 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2691 // Create dummy block
2692 block = new MapBlock(this, p, true);
2694 // Add block to sector
2695 sector->insertBlock(block);
2701 //dstream<<"Not found on disk, generating."<<std::endl;
2703 //TimeTaker("emergeBlock() generate");
2705 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2708 If the block doesn't exist, generate the block.
2712 block = generateBlock(p, block, sector, changed_blocks,
2713 lighting_invalidated_blocks);
2716 if(lighting_expired)
2718 lighting_invalidated_blocks.insert(p, block);
2723 Initially update sunlight
2726 core::map<v3s16, bool> light_sources;
2727 bool black_air_left = false;
2728 bool bottom_invalid =
2729 block->propagateSunlight(light_sources, true,
2732 // If sunlight didn't reach everywhere and part of block is
2733 // above ground, lighting has to be properly updated
2734 //if(black_air_left && some_part_underground)
2737 lighting_invalidated_blocks[block->getPos()] = block;
2742 lighting_invalidated_blocks[block->getPos()] = block;
2751 s16 ServerMap::findGroundLevel(v2s16 p2d)
2755 Uh, just do something random...
2757 // Find existing map from top to down
2760 v3s16 p(p2d.X, max, p2d.Y);
2761 for(; p.Y>min; p.Y--)
2763 MapNode n = getNodeNoEx(p);
2764 if(n.getContent() != CONTENT_IGNORE)
2769 // If this node is not air, go to plan b
2770 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2772 // Search existing walkable and return it
2773 for(; p.Y>min; p.Y--)
2775 MapNode n = getNodeNoEx(p);
2776 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2785 Determine from map generator noise functions
2788 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2791 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2792 //return (s16)level;
2795 void ServerMap::createDatabase() {
2798 e = sqlite3_exec(m_database,
2799 "CREATE TABLE IF NOT EXISTS `blocks` ("
2800 "`pos` INT NOT NULL PRIMARY KEY,"
2803 , NULL, NULL, NULL);
2804 if(e == SQLITE_ABORT)
2805 throw FileNotGoodException("Could not create database structure");
2807 dstream<<"Server: Database structure was created";
2810 void ServerMap::verifyDatabase() {
2815 std::string dbp = m_savedir + "/map.sqlite";
2816 bool needs_create = false;
2820 Open the database connection
2823 createDirs(m_savedir);
2825 if(!fs::PathExists(dbp))
2826 needs_create = true;
2828 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2829 if(d != SQLITE_OK) {
2830 dstream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2831 throw FileNotGoodException("Cannot open database file");
2837 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2838 if(d != SQLITE_OK) {
2839 dstream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2840 throw FileNotGoodException("Cannot prepare read statement");
2843 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2844 if(d != SQLITE_OK) {
2845 dstream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2846 throw FileNotGoodException("Cannot prepare write statement");
2849 dstream<<"Server: Database opened"<<std::endl;
2853 bool ServerMap::loadFromFolders() {
2854 if(!m_database && !fs::PathExists(m_savedir + "/map.sqlite"))
2859 sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
2860 return (sqlite3_int64)pos.Z*16777216 +
2861 (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
2864 void ServerMap::createDirs(std::string path)
2866 if(fs::CreateAllDirs(path) == false)
2868 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2869 <<"\""<<path<<"\""<<std::endl;
2870 throw BaseException("ServerMap failed to create directory");
2874 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2880 snprintf(cc, 9, "%.4x%.4x",
2881 (unsigned int)pos.X&0xffff,
2882 (unsigned int)pos.Y&0xffff);
2884 return m_savedir + "/sectors/" + cc;
2886 snprintf(cc, 9, "%.3x/%.3x",
2887 (unsigned int)pos.X&0xfff,
2888 (unsigned int)pos.Y&0xfff);
2890 return m_savedir + "/sectors2/" + cc;
2896 v2s16 ServerMap::getSectorPos(std::string dirname)
2900 size_t spos = dirname.rfind('/') + 1;
2901 assert(spos != std::string::npos);
2902 if(dirname.size() - spos == 8)
2905 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2907 else if(dirname.size() - spos == 3)
2910 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2911 // Sign-extend the 12 bit values up to 16 bits...
2912 if(x&0x800) x|=0xF000;
2913 if(y&0x800) y|=0xF000;
2920 v2s16 pos((s16)x, (s16)y);
2924 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2926 v2s16 p2d = getSectorPos(sectordir);
2928 if(blockfile.size() != 4){
2929 throw InvalidFilenameException("Invalid block filename");
2932 int r = sscanf(blockfile.c_str(), "%4x", &y);
2934 throw InvalidFilenameException("Invalid block filename");
2935 return v3s16(p2d.X, y, p2d.Y);
2938 std::string ServerMap::getBlockFilename(v3s16 p)
2941 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2945 void ServerMap::save(bool only_changed)
2947 DSTACK(__FUNCTION_NAME);
2948 if(m_map_saving_enabled == false)
2950 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2954 if(only_changed == false)
2955 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2958 if(only_changed == false || m_map_metadata_changed)
2963 u32 sector_meta_count = 0;
2964 u32 block_count = 0;
2965 u32 block_count_all = 0; // Number of blocks in memory
2968 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2969 for(; i.atEnd() == false; i++)
2971 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2972 assert(sector->getId() == MAPSECTOR_SERVER);
2974 if(sector->differs_from_disk || only_changed == false)
2976 saveSectorMeta(sector);
2977 sector_meta_count++;
2979 core::list<MapBlock*> blocks;
2980 sector->getBlocks(blocks);
2981 core::list<MapBlock*>::Iterator j;
2983 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2984 for(j=blocks.begin(); j!=blocks.end(); j++)
2986 MapBlock *block = *j;
2990 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2991 || only_changed == false)
2996 /*dstream<<"ServerMap: Written block ("
2997 <<block->getPos().X<<","
2998 <<block->getPos().Y<<","
2999 <<block->getPos().Z<<")"
3002 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
3008 Only print if something happened or saved whole map
3010 if(only_changed == false || sector_meta_count != 0
3011 || block_count != 0)
3013 dstream<<DTIME<<"ServerMap: Written: "
3014 <<sector_meta_count<<" sector metadata files, "
3015 <<block_count<<" block files"
3016 <<", "<<block_count_all<<" blocks in memory."
3021 void ServerMap::saveMapMeta()
3023 DSTACK(__FUNCTION_NAME);
3025 dstream<<"INFO: ServerMap::saveMapMeta(): "
3029 createDirs(m_savedir);
3031 std::string fullpath = m_savedir + "/map_meta.txt";
3032 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3033 if(os.good() == false)
3035 dstream<<"ERROR: ServerMap::saveMapMeta(): "
3036 <<"could not open"<<fullpath<<std::endl;
3037 throw FileNotGoodException("Cannot open chunk metadata");
3041 params.setU64("seed", m_seed);
3043 params.writeLines(os);
3045 os<<"[end_of_params]\n";
3047 m_map_metadata_changed = false;
3050 void ServerMap::loadMapMeta()
3052 DSTACK(__FUNCTION_NAME);
3054 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
3057 std::string fullpath = m_savedir + "/map_meta.txt";
3058 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3059 if(is.good() == false)
3061 dstream<<"ERROR: ServerMap::loadMapMeta(): "
3062 <<"could not open"<<fullpath<<std::endl;
3063 throw FileNotGoodException("Cannot open map metadata");
3071 throw SerializationError
3072 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3074 std::getline(is, line);
3075 std::string trimmedline = trim(line);
3076 if(trimmedline == "[end_of_params]")
3078 params.parseConfigLine(line);
3081 m_seed = params.getU64("seed");
3083 dstream<<"INFO: ServerMap::loadMapMeta(): "
3088 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3090 DSTACK(__FUNCTION_NAME);
3091 // Format used for writing
3092 u8 version = SER_FMT_VER_HIGHEST;
3094 v2s16 pos = sector->getPos();
3095 std::string dir = getSectorDir(pos);
3098 std::string fullpath = dir + "/meta";
3099 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3100 if(o.good() == false)
3101 throw FileNotGoodException("Cannot open sector metafile");
3103 sector->serialize(o, version);
3105 sector->differs_from_disk = false;
3108 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3110 DSTACK(__FUNCTION_NAME);
3112 v2s16 p2d = getSectorPos(sectordir);
3114 ServerMapSector *sector = NULL;
3116 std::string fullpath = sectordir + "/meta";
3117 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3118 if(is.good() == false)
3120 // If the directory exists anyway, it probably is in some old
3121 // format. Just go ahead and create the sector.
3122 if(fs::PathExists(sectordir))
3124 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
3125 <<fullpath<<" doesn't exist but directory does."
3126 <<" Continuing with a sector with no metadata."
3128 sector = new ServerMapSector(this, p2d);
3129 m_sectors.insert(p2d, sector);
3133 throw FileNotGoodException("Cannot open sector metafile");
3138 sector = ServerMapSector::deSerialize
3139 (is, this, p2d, m_sectors);
3141 saveSectorMeta(sector);
3144 sector->differs_from_disk = false;
3149 bool ServerMap::loadSectorMeta(v2s16 p2d)
3151 DSTACK(__FUNCTION_NAME);
3153 MapSector *sector = NULL;
3155 // The directory layout we're going to load from.
3156 // 1 - original sectors/xxxxzzzz/
3157 // 2 - new sectors2/xxx/zzz/
3158 // If we load from anything but the latest structure, we will
3159 // immediately save to the new one, and remove the old.
3161 std::string sectordir1 = getSectorDir(p2d, 1);
3162 std::string sectordir;
3163 if(fs::PathExists(sectordir1))
3165 sectordir = sectordir1;
3170 sectordir = getSectorDir(p2d, 2);
3174 sector = loadSectorMeta(sectordir, loadlayout != 2);
3176 catch(InvalidFilenameException &e)
3180 catch(FileNotGoodException &e)
3184 catch(std::exception &e)
3193 bool ServerMap::loadSectorFull(v2s16 p2d)
3195 DSTACK(__FUNCTION_NAME);
3197 MapSector *sector = NULL;
3199 // The directory layout we're going to load from.
3200 // 1 - original sectors/xxxxzzzz/
3201 // 2 - new sectors2/xxx/zzz/
3202 // If we load from anything but the latest structure, we will
3203 // immediately save to the new one, and remove the old.
3205 std::string sectordir1 = getSectorDir(p2d, 1);
3206 std::string sectordir;
3207 if(fs::PathExists(sectordir1))
3209 sectordir = sectordir1;
3214 sectordir = getSectorDir(p2d, 2);
3218 sector = loadSectorMeta(sectordir, loadlayout != 2);
3220 catch(InvalidFilenameException &e)
3224 catch(FileNotGoodException &e)
3228 catch(std::exception &e)
3236 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3238 std::vector<fs::DirListNode>::iterator i2;
3239 for(i2=list2.begin(); i2!=list2.end(); i2++)
3245 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3247 catch(InvalidFilenameException &e)
3249 // This catches unknown crap in directory
3255 dstream<<"Sector converted to new layout - deleting "<<
3256 sectordir1<<std::endl;
3257 fs::RecursiveDelete(sectordir1);
3264 void ServerMap::beginSave() {
3266 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3267 dstream<<"WARNING: beginSave() failed, saving might be slow.";
3270 void ServerMap::endSave() {
3272 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3273 dstream<<"WARNING: endSave() failed, map might not have saved.";
3276 void ServerMap::saveBlock(MapBlock *block)
3278 DSTACK(__FUNCTION_NAME);
3280 Dummy blocks are not written
3282 if(block->isDummy())
3284 /*v3s16 p = block->getPos();
3285 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3286 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3290 // Format used for writing
3291 u8 version = SER_FMT_VER_HIGHEST;
3293 v3s16 p3d = block->getPos();
3297 v2s16 p2d(p3d.X, p3d.Z);
3298 std::string sectordir = getSectorDir(p2d);
3300 createDirs(sectordir);
3302 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3303 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3304 if(o.good() == false)
3305 throw FileNotGoodException("Cannot open block data");
3308 [0] u8 serialization version
3314 std::ostringstream o(std::ios_base::binary);
3316 o.write((char*)&version, 1);
3319 block->serialize(o, version);
3321 // Write extra data stored on disk
3322 block->serializeDiskExtra(o, version);
3324 // Write block to database
3326 std::string tmp = o.str();
3327 const char *bytes = tmp.c_str();
3329 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3330 dstream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3331 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3332 dstream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3333 int written = sqlite3_step(m_database_write);
3334 if(written != SQLITE_DONE)
3335 dstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3336 <<sqlite3_errmsg(m_database)<<std::endl;
3337 // Make ready for later reuse
3338 sqlite3_reset(m_database_write);
3340 // We just wrote it to the disk so clear modified flag
3341 block->resetModified();
3344 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3346 DSTACK(__FUNCTION_NAME);
3348 std::string fullpath = sectordir+"/"+blockfile;
3351 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3352 if(is.good() == false)
3353 throw FileNotGoodException("Cannot open block file");
3355 v3s16 p3d = getBlockPos(sectordir, blockfile);
3356 v2s16 p2d(p3d.X, p3d.Z);
3358 assert(sector->getPos() == p2d);
3360 u8 version = SER_FMT_VER_INVALID;
3361 is.read((char*)&version, 1);
3364 throw SerializationError("ServerMap::loadBlock(): Failed"
3365 " to read MapBlock version");
3367 /*u32 block_size = MapBlock::serializedLength(version);
3368 SharedBuffer<u8> data(block_size);
3369 is.read((char*)*data, block_size);*/
3371 // This will always return a sector because we're the server
3372 //MapSector *sector = emergeSector(p2d);
3374 MapBlock *block = NULL;
3375 bool created_new = false;
3376 block = sector->getBlockNoCreateNoEx(p3d.Y);
3379 block = sector->createBlankBlockNoInsert(p3d.Y);
3384 block->deSerialize(is, version);
3386 // Read extra data stored on disk
3387 block->deSerializeDiskExtra(is, version);
3389 // If it's a new block, insert it to the map
3391 sector->insertBlock(block);
3394 Save blocks loaded in old format in new format
3397 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3402 // We just loaded it from the disk, so it's up-to-date.
3403 block->resetModified();
3406 catch(SerializationError &e)
3408 dstream<<"WARNING: Invalid block data on disk "
3409 <<"fullpath="<<fullpath
3410 <<" (SerializationError). "
3411 <<"what()="<<e.what()
3413 //" Ignoring. A new one will be generated.
3416 // TODO: Backup file; name is in fullpath.
3420 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3422 DSTACK(__FUNCTION_NAME);
3425 std::istringstream is(*blob, std::ios_base::binary);
3427 u8 version = SER_FMT_VER_INVALID;
3428 is.read((char*)&version, 1);
3431 throw SerializationError("ServerMap::loadBlock(): Failed"
3432 " to read MapBlock version");
3434 /*u32 block_size = MapBlock::serializedLength(version);
3435 SharedBuffer<u8> data(block_size);
3436 is.read((char*)*data, block_size);*/
3438 // This will always return a sector because we're the server
3439 //MapSector *sector = emergeSector(p2d);
3441 MapBlock *block = NULL;
3442 bool created_new = false;
3443 block = sector->getBlockNoCreateNoEx(p3d.Y);
3446 block = sector->createBlankBlockNoInsert(p3d.Y);
3451 block->deSerialize(is, version);
3453 // Read extra data stored on disk
3454 block->deSerializeDiskExtra(is, version);
3456 // If it's a new block, insert it to the map
3458 sector->insertBlock(block);
3461 Save blocks loaded in old format in new format
3464 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3469 // We just loaded it from, so it's up-to-date.
3470 block->resetModified();
3473 catch(SerializationError &e)
3475 dstream<<"WARNING: Invalid block data in database "
3476 <<" (SerializationError). "
3477 <<"what()="<<e.what()
3479 //" Ignoring. A new one will be generated.
3482 // TODO: Copy to a backup database.
3486 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3488 DSTACK(__FUNCTION_NAME);
3490 v2s16 p2d(blockpos.X, blockpos.Z);
3492 if(!loadFromFolders()) {
3495 if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3496 dstream<<"WARNING: Could not bind block position for load: "
3497 <<sqlite3_errmsg(m_database)<<std::endl;
3498 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3500 Make sure sector is loaded
3502 MapSector *sector = createSector(p2d);
3507 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3508 size_t len = sqlite3_column_bytes(m_database_read, 0);
3510 std::string datastr(data, len);
3512 loadBlock(&datastr, blockpos, sector, false);
3514 sqlite3_step(m_database_read);
3515 // We should never get more than 1 row, so ok to reset
3516 sqlite3_reset(m_database_read);
3518 return getBlockNoCreateNoEx(blockpos);
3520 sqlite3_reset(m_database_read);
3522 // Not found in database, try the files
3525 // The directory layout we're going to load from.
3526 // 1 - original sectors/xxxxzzzz/
3527 // 2 - new sectors2/xxx/zzz/
3528 // If we load from anything but the latest structure, we will
3529 // immediately save to the new one, and remove the old.
3531 std::string sectordir1 = getSectorDir(p2d, 1);
3532 std::string sectordir;
3533 if(fs::PathExists(sectordir1))
3535 sectordir = sectordir1;
3540 sectordir = getSectorDir(p2d, 2);
3544 Make sure sector is loaded
3546 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3550 sector = loadSectorMeta(sectordir, loadlayout != 2);
3552 catch(InvalidFilenameException &e)
3556 catch(FileNotGoodException &e)
3560 catch(std::exception &e)
3567 Make sure file exists
3570 std::string blockfilename = getBlockFilename(blockpos);
3571 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3575 Load block and save it to the database
3577 loadBlock(sectordir, blockfilename, sector, true);
3578 return getBlockNoCreateNoEx(blockpos);
3581 void ServerMap::PrintInfo(std::ostream &out)
3592 ClientMap::ClientMap(
3594 MapDrawControl &control,
3595 scene::ISceneNode* parent,
3596 scene::ISceneManager* mgr,
3600 scene::ISceneNode(parent, mgr, id),
3603 m_camera_position(0,0,0),
3604 m_camera_direction(0,0,1)
3606 m_camera_mutex.Init();
3607 assert(m_camera_mutex.IsInitialized());
3609 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3610 BS*1000000,BS*1000000,BS*1000000);
3613 ClientMap::~ClientMap()
3615 /*JMutexAutoLock lock(mesh_mutex);
3624 MapSector * ClientMap::emergeSector(v2s16 p2d)
3626 DSTACK(__FUNCTION_NAME);
3627 // Check that it doesn't exist already
3629 return getSectorNoGenerate(p2d);
3631 catch(InvalidPositionException &e)
3636 ClientMapSector *sector = new ClientMapSector(this, p2d);
3639 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3640 m_sectors.insert(p2d, sector);
3647 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3649 DSTACK(__FUNCTION_NAME);
3650 ClientMapSector *sector = NULL;
3652 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3654 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3658 sector = (ClientMapSector*)n->getValue();
3659 assert(sector->getId() == MAPSECTOR_CLIENT);
3663 sector = new ClientMapSector(this, p2d);
3665 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3666 m_sectors.insert(p2d, sector);
3670 sector->deSerialize(is);
3674 void ClientMap::OnRegisterSceneNode()
3678 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3679 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3682 ISceneNode::OnRegisterSceneNode();
3685 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3687 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3688 DSTACK(__FUNCTION_NAME);
3690 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3693 This is called two times per frame, reset on the non-transparent one
3695 if(pass == scene::ESNRP_SOLID)
3697 m_last_drawn_sectors.clear();
3701 Get time for measuring timeout.
3703 Measuring time is very useful for long delays when the
3704 machine is swapping a lot.
3706 int time1 = time(0);
3708 //u32 daynight_ratio = m_client->getDayNightRatio();
3710 m_camera_mutex.Lock();
3711 v3f camera_position = m_camera_position;
3712 v3f camera_direction = m_camera_direction;
3713 m_camera_mutex.Unlock();
3716 Get all blocks and draw all visible ones
3719 v3s16 cam_pos_nodes(
3720 camera_position.X / BS,
3721 camera_position.Y / BS,
3722 camera_position.Z / BS);
3724 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3726 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3727 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3729 // Take a fair amount as we will be dropping more out later
3731 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3732 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3733 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3735 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3736 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3737 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3739 u32 vertex_count = 0;
3741 // For limiting number of mesh updates per frame
3742 u32 mesh_update_count = 0;
3744 u32 blocks_would_have_drawn = 0;
3745 u32 blocks_drawn = 0;
3747 int timecheck_counter = 0;
3748 core::map<v2s16, MapSector*>::Iterator si;
3749 si = m_sectors.getIterator();
3750 for(; si.atEnd() == false; si++)
3753 timecheck_counter++;
3754 if(timecheck_counter > 50)
3756 timecheck_counter = 0;
3757 int time2 = time(0);
3758 if(time2 > time1 + 4)
3760 dstream<<"ClientMap::renderMap(): "
3761 "Rendering takes ages, returning."
3768 MapSector *sector = si.getNode()->getValue();
3769 v2s16 sp = sector->getPos();
3771 if(m_control.range_all == false)
3773 if(sp.X < p_blocks_min.X
3774 || sp.X > p_blocks_max.X
3775 || sp.Y < p_blocks_min.Z
3776 || sp.Y > p_blocks_max.Z)
3780 core::list< MapBlock * > sectorblocks;
3781 sector->getBlocks(sectorblocks);
3787 u32 sector_blocks_drawn = 0;
3789 core::list< MapBlock * >::Iterator i;
3790 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3792 MapBlock *block = *i;
3795 Compare block position to camera position, skip
3796 if not seen on display
3799 float range = 100000 * BS;
3800 if(m_control.range_all == false)
3801 range = m_control.wanted_range * BS;
3804 if(isBlockInSight(block->getPos(), camera_position,
3805 camera_direction, range, &d) == false)
3810 // Okay, this block will be drawn. Reset usage timer.
3811 block->resetUsageTimer();
3813 // This is ugly (spherical distance limit?)
3814 /*if(m_control.range_all == false &&
3815 d - 0.5*BS*MAP_BLOCKSIZE > range)
3820 Update expired mesh (used for day/night change)
3822 It doesn't work exactly like it should now with the
3823 tasked mesh update but whatever.
3826 bool mesh_expired = false;
3829 JMutexAutoLock lock(block->mesh_mutex);
3831 mesh_expired = block->getMeshExpired();
3833 // Mesh has not been expired and there is no mesh:
3834 // block has no content
3835 if(block->mesh == NULL && mesh_expired == false)
3839 f32 faraway = BS*50;
3840 //f32 faraway = m_control.wanted_range * BS;
3843 This has to be done with the mesh_mutex unlocked
3845 // Pretty random but this should work somewhat nicely
3846 if(mesh_expired && (
3847 (mesh_update_count < 3
3848 && (d < faraway || mesh_update_count < 2)
3851 (m_control.range_all && mesh_update_count < 20)
3854 /*if(mesh_expired && mesh_update_count < 6
3855 && (d < faraway || mesh_update_count < 3))*/
3857 mesh_update_count++;
3859 // Mesh has been expired: generate new mesh
3860 //block->updateMesh(daynight_ratio);
3861 m_client->addUpdateMeshTask(block->getPos());
3863 mesh_expired = false;
3868 Draw the faces of the block
3871 JMutexAutoLock lock(block->mesh_mutex);
3873 scene::SMesh *mesh = block->mesh;
3878 blocks_would_have_drawn++;
3879 if(blocks_drawn >= m_control.wanted_max_blocks
3880 && m_control.range_all == false
3881 && d > m_control.wanted_min_range * BS)
3885 sector_blocks_drawn++;
3887 u32 c = mesh->getMeshBufferCount();
3889 for(u32 i=0; i<c; i++)
3891 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3892 const video::SMaterial& material = buf->getMaterial();
3893 video::IMaterialRenderer* rnd =
3894 driver->getMaterialRenderer(material.MaterialType);
3895 bool transparent = (rnd && rnd->isTransparent());
3896 // Render transparent on transparent pass and likewise.
3897 if(transparent == is_transparent_pass)
3900 This *shouldn't* hurt too much because Irrlicht
3901 doesn't change opengl textures if the old
3902 material is set again.
3904 driver->setMaterial(buf->getMaterial());
3905 driver->drawMeshBuffer(buf);
3906 vertex_count += buf->getVertexCount();
3910 } // foreach sectorblocks
3912 if(sector_blocks_drawn != 0)
3914 m_last_drawn_sectors[sp] = true;
3918 m_control.blocks_drawn = blocks_drawn;
3919 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3921 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3922 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3925 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3926 core::map<v3s16, MapBlock*> *affected_blocks)
3928 bool changed = false;
3930 Add it to all blocks touching it
3933 v3s16(0,0,0), // this
3934 v3s16(0,0,1), // back
3935 v3s16(0,1,0), // top
3936 v3s16(1,0,0), // right
3937 v3s16(0,0,-1), // front
3938 v3s16(0,-1,0), // bottom
3939 v3s16(-1,0,0), // left
3941 for(u16 i=0; i<7; i++)
3943 v3s16 p2 = p + dirs[i];
3944 // Block position of neighbor (or requested) node
3945 v3s16 blockpos = getNodeBlockPos(p2);
3946 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3947 if(blockref == NULL)
3949 // Relative position of requested node
3950 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3951 if(blockref->setTempMod(relpos, mod))
3956 if(changed && affected_blocks!=NULL)
3958 for(u16 i=0; i<7; i++)
3960 v3s16 p2 = p + dirs[i];
3961 // Block position of neighbor (or requested) node
3962 v3s16 blockpos = getNodeBlockPos(p2);
3963 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3964 if(blockref == NULL)
3966 affected_blocks->insert(blockpos, blockref);
3972 bool ClientMap::clearTempMod(v3s16 p,
3973 core::map<v3s16, MapBlock*> *affected_blocks)
3975 bool changed = false;
3977 v3s16(0,0,0), // this
3978 v3s16(0,0,1), // back
3979 v3s16(0,1,0), // top
3980 v3s16(1,0,0), // right
3981 v3s16(0,0,-1), // front
3982 v3s16(0,-1,0), // bottom
3983 v3s16(-1,0,0), // left
3985 for(u16 i=0; i<7; i++)
3987 v3s16 p2 = p + dirs[i];
3988 // Block position of neighbor (or requested) node
3989 v3s16 blockpos = getNodeBlockPos(p2);
3990 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3991 if(blockref == NULL)
3993 // Relative position of requested node
3994 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3995 if(blockref->clearTempMod(relpos))
4000 if(changed && affected_blocks!=NULL)
4002 for(u16 i=0; i<7; i++)
4004 v3s16 p2 = p + dirs[i];
4005 // Block position of neighbor (or requested) node
4006 v3s16 blockpos = getNodeBlockPos(p2);
4007 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4008 if(blockref == NULL)
4010 affected_blocks->insert(blockpos, blockref);
4016 void ClientMap::expireMeshes(bool only_daynight_diffed)
4018 TimeTaker timer("expireMeshes()");
4020 core::map<v2s16, MapSector*>::Iterator si;
4021 si = m_sectors.getIterator();
4022 for(; si.atEnd() == false; si++)
4024 MapSector *sector = si.getNode()->getValue();
4026 core::list< MapBlock * > sectorblocks;
4027 sector->getBlocks(sectorblocks);
4029 core::list< MapBlock * >::Iterator i;
4030 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4032 MapBlock *block = *i;
4034 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4040 JMutexAutoLock lock(block->mesh_mutex);
4041 if(block->mesh != NULL)
4043 /*block->mesh->drop();
4044 block->mesh = NULL;*/
4045 block->setMeshExpired(true);
4052 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4054 assert(mapType() == MAPTYPE_CLIENT);
4057 v3s16 p = blockpos + v3s16(0,0,0);
4058 MapBlock *b = getBlockNoCreate(p);
4059 b->updateMesh(daynight_ratio);
4060 //b->setMeshExpired(true);
4062 catch(InvalidPositionException &e){}
4065 v3s16 p = blockpos + v3s16(-1,0,0);
4066 MapBlock *b = getBlockNoCreate(p);
4067 b->updateMesh(daynight_ratio);
4068 //b->setMeshExpired(true);
4070 catch(InvalidPositionException &e){}
4072 v3s16 p = blockpos + v3s16(0,-1,0);
4073 MapBlock *b = getBlockNoCreate(p);
4074 b->updateMesh(daynight_ratio);
4075 //b->setMeshExpired(true);
4077 catch(InvalidPositionException &e){}
4079 v3s16 p = blockpos + v3s16(0,0,-1);
4080 MapBlock *b = getBlockNoCreate(p);
4081 b->updateMesh(daynight_ratio);
4082 //b->setMeshExpired(true);
4084 catch(InvalidPositionException &e){}
4089 Update mesh of block in which the node is, and if the node is at the
4090 leading edge, update the appropriate leading blocks too.
4092 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4100 v3s16 blockposes[4];
4101 for(u32 i=0; i<4; i++)
4103 v3s16 np = nodepos + dirs[i];
4104 blockposes[i] = getNodeBlockPos(np);
4105 // Don't update mesh of block if it has been done already
4106 bool already_updated = false;
4107 for(u32 j=0; j<i; j++)
4109 if(blockposes[j] == blockposes[i])
4111 already_updated = true;
4118 MapBlock *b = getBlockNoCreate(blockposes[i]);
4119 b->updateMesh(daynight_ratio);
4124 void ClientMap::PrintInfo(std::ostream &out)
4135 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4140 MapVoxelManipulator::~MapVoxelManipulator()
4142 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4146 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4148 TimeTaker timer1("emerge", &emerge_time);
4150 // Units of these are MapBlocks
4151 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4152 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4154 VoxelArea block_area_nodes
4155 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4157 addArea(block_area_nodes);
4159 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4160 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4161 for(s32 x=p_min.X; x<=p_max.X; x++)
4164 core::map<v3s16, bool>::Node *n;
4165 n = m_loaded_blocks.find(p);
4169 bool block_data_inexistent = false;
4172 TimeTaker timer1("emerge load", &emerge_load_time);
4174 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4175 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4178 dstream<<std::endl;*/
4180 MapBlock *block = m_map->getBlockNoCreate(p);
4181 if(block->isDummy())
4182 block_data_inexistent = true;
4184 block->copyTo(*this);
4186 catch(InvalidPositionException &e)
4188 block_data_inexistent = true;
4191 if(block_data_inexistent)
4193 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4194 // Fill with VOXELFLAG_INEXISTENT
4195 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4196 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4198 s32 i = m_area.index(a.MinEdge.X,y,z);
4199 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4203 m_loaded_blocks.insert(p, !block_data_inexistent);
4206 //dstream<<"emerge done"<<std::endl;
4210 SUGG: Add an option to only update eg. water and air nodes.
4211 This will make it interfere less with important stuff if
4214 void MapVoxelManipulator::blitBack
4215 (core::map<v3s16, MapBlock*> & modified_blocks)
4217 if(m_area.getExtent() == v3s16(0,0,0))
4220 //TimeTaker timer1("blitBack");
4222 /*dstream<<"blitBack(): m_loaded_blocks.size()="
4223 <<m_loaded_blocks.size()<<std::endl;*/
4226 Initialize block cache
4228 v3s16 blockpos_last;
4229 MapBlock *block = NULL;
4230 bool block_checked_in_modified = false;
4232 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4233 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4234 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4238 u8 f = m_flags[m_area.index(p)];
4239 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4242 MapNode &n = m_data[m_area.index(p)];
4244 v3s16 blockpos = getNodeBlockPos(p);
4249 if(block == NULL || blockpos != blockpos_last){
4250 block = m_map->getBlockNoCreate(blockpos);
4251 blockpos_last = blockpos;
4252 block_checked_in_modified = false;
4255 // Calculate relative position in block
4256 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4258 // Don't continue if nothing has changed here
4259 if(block->getNode(relpos) == n)
4262 //m_map->setNode(m_area.MinEdge + p, n);
4263 block->setNode(relpos, n);
4266 Make sure block is in modified_blocks
4268 if(block_checked_in_modified == false)
4270 modified_blocks[blockpos] = block;
4271 block_checked_in_modified = true;
4274 catch(InvalidPositionException &e)
4280 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4281 MapVoxelManipulator(map),
4282 m_create_area(false)
4286 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4290 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4292 // Just create the area so that it can be pointed to
4293 VoxelManipulator::emerge(a, caller_id);
4296 void ManualMapVoxelManipulator::initialEmerge(
4297 v3s16 blockpos_min, v3s16 blockpos_max)
4299 TimeTaker timer1("initialEmerge", &emerge_time);
4301 // Units of these are MapBlocks
4302 v3s16 p_min = blockpos_min;
4303 v3s16 p_max = blockpos_max;
4305 VoxelArea block_area_nodes
4306 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4308 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4311 dstream<<"initialEmerge: area: ";
4312 block_area_nodes.print(dstream);
4313 dstream<<" ("<<size_MB<<"MB)";
4317 addArea(block_area_nodes);
4319 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4320 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4321 for(s32 x=p_min.X; x<=p_max.X; x++)
4324 core::map<v3s16, bool>::Node *n;
4325 n = m_loaded_blocks.find(p);
4329 bool block_data_inexistent = false;
4332 TimeTaker timer1("emerge load", &emerge_load_time);
4334 MapBlock *block = m_map->getBlockNoCreate(p);
4335 if(block->isDummy())
4336 block_data_inexistent = true;
4338 block->copyTo(*this);
4340 catch(InvalidPositionException &e)
4342 block_data_inexistent = true;
4345 if(block_data_inexistent)
4348 Mark area inexistent
4350 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4351 // Fill with VOXELFLAG_INEXISTENT
4352 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4353 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4355 s32 i = m_area.index(a.MinEdge.X,y,z);
4356 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4360 m_loaded_blocks.insert(p, !block_data_inexistent);
4364 void ManualMapVoxelManipulator::blitBackAll(
4365 core::map<v3s16, MapBlock*> * modified_blocks)
4367 if(m_area.getExtent() == v3s16(0,0,0))
4371 Copy data of all blocks
4373 for(core::map<v3s16, bool>::Iterator
4374 i = m_loaded_blocks.getIterator();
4375 i.atEnd() == false; i++)
4377 v3s16 p = i.getNode()->getKey();
4378 bool existed = i.getNode()->getValue();
4379 if(existed == false)
4381 // The Great Bug was found using this
4382 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4383 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4387 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4390 dstream<<"WARNING: "<<__FUNCTION_NAME
4391 <<": got NULL block "
4392 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4397 block->copyFrom(*this);
4400 modified_blocks->insert(p, block);