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;
1411 si = m_sectors.getIterator();
1412 for(; si.atEnd() == false; si++)
1414 MapSector *sector = si.getNode()->getValue();
1416 bool all_blocks_deleted = true;
1418 core::list<MapBlock*> blocks;
1419 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;
1456 if(all_blocks_deleted)
1458 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 m_seed = (((u64)(myrand()%0xffff)<<0)
1898 + ((u64)(myrand()%0xffff)<<16)
1899 + ((u64)(myrand()%0xffff)<<32)
1900 + ((u64)(myrand()%0xffff)<<48));
1903 Experimental and debug stuff
1910 Try to load map; if not found, create a new one.
1913 m_savedir = savedir;
1914 m_map_saving_enabled = false;
1918 // If directory exists, check contents and load if possible
1919 if(fs::PathExists(m_savedir))
1921 // If directory is empty, it is safe to save into it.
1922 if(fs::GetDirListing(m_savedir).size() == 0)
1924 dstream<<DTIME<<"Server: Empty save directory is valid."
1926 m_map_saving_enabled = true;
1931 // Load map metadata (seed, chunksize)
1934 catch(FileNotGoodException &e){
1935 dstream<<DTIME<<"WARNING: Could not load map metadata"
1936 //<<" Disabling chunk-based generator."
1942 // Load chunk metadata
1945 catch(FileNotGoodException &e){
1946 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1947 <<" Disabling chunk-based generator."
1952 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1953 "metadata and sector (0,0) from "<<savedir<<
1954 ", assuming valid save directory."
1957 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1958 <<"and chunk metadata from "<<savedir
1959 <<", assuming valid save directory."
1962 m_map_saving_enabled = true;
1963 // Map loaded, not creating new one
1967 // If directory doesn't exist, it is safe to save to it
1969 m_map_saving_enabled = true;
1972 catch(std::exception &e)
1974 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1975 <<", exception: "<<e.what()<<std::endl;
1976 dstream<<"Please remove the map or fix it."<<std::endl;
1977 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1980 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1982 // Create zero sector
1983 emergeSector(v2s16(0,0));
1985 // Initially write whole map
1989 ServerMap::~ServerMap()
1991 dstream<<__FUNCTION_NAME<<std::endl;
1995 if(m_map_saving_enabled)
1997 // Save only changed parts
1999 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
2003 dstream<<DTIME<<"Server: map not saved"<<std::endl;
2006 catch(std::exception &e)
2008 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
2009 <<", exception: "<<e.what()<<std::endl;
2013 Close database if it was opened
2016 sqlite3_finalize(m_database_read);
2017 if(m_database_write)
2018 sqlite3_finalize(m_database_write);
2020 sqlite3_close(m_database);
2026 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
2027 for(; i.atEnd() == false; i++)
2029 MapChunk *chunk = i.getNode()->getValue();
2035 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
2037 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2038 if(enable_mapgen_debug_info)
2039 dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2040 <<blockpos.Z<<")"<<std::endl;
2042 // Do nothing if not inside limits (+-1 because of neighbors)
2043 if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
2044 blockpos_over_limit(blockpos + v3s16(1,1,1)))
2050 data->no_op = false;
2051 data->seed = m_seed;
2052 data->blockpos = blockpos;
2055 Create the whole area of this and the neighboring blocks
2058 //TimeTaker timer("initBlockMake() create area");
2060 for(s16 x=-1; x<=1; x++)
2061 for(s16 z=-1; z<=1; z++)
2063 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
2064 // Sector metadata is loaded from disk if not already loaded.
2065 ServerMapSector *sector = createSector(sectorpos);
2068 for(s16 y=-1; y<=1; y++)
2070 v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
2071 //MapBlock *block = createBlock(p);
2072 // 1) get from memory, 2) load from disk
2073 MapBlock *block = emergeBlock(p, false);
2074 // 3) create a blank one
2077 block = createBlock(p);
2080 Block gets sunlight if this is true.
2082 Refer to the map generator heuristics.
2084 bool ug = mapgen::block_is_underground(data->seed, p);
2085 block->setIsUnderground(ug);
2088 // Lighting will not be valid after make_chunk is called
2089 block->setLightingExpired(true);
2090 // Lighting will be calculated
2091 //block->setLightingExpired(false);
2097 Now we have a big empty area.
2099 Make a ManualMapVoxelManipulator that contains this and the
2103 // The area that contains this block and it's neighbors
2104 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
2105 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
2107 data->vmanip = new ManualMapVoxelManipulator(this);
2108 //data->vmanip->setMap(this);
2112 //TimeTaker timer("initBlockMake() initialEmerge");
2113 data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
2116 // Data is ready now.
2119 MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
2120 core::map<v3s16, MapBlock*> &changed_blocks)
2122 v3s16 blockpos = data->blockpos;
2123 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
2124 <<blockpos.Z<<")"<<std::endl;*/
2128 //dstream<<"finishBlockMake(): no-op"<<std::endl;
2132 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2134 /*dstream<<"Resulting vmanip:"<<std::endl;
2135 data->vmanip.print(dstream);*/
2138 Blit generated stuff to map
2139 NOTE: blitBackAll adds nearly everything to changed_blocks
2143 //TimeTaker timer("finishBlockMake() blitBackAll");
2144 data->vmanip->blitBackAll(&changed_blocks);
2147 if(enable_mapgen_debug_info)
2148 dstream<<"finishBlockMake: changed_blocks.size()="
2149 <<changed_blocks.size()<<std::endl;
2152 Copy transforming liquid information
2154 while(data->transforming_liquid.size() > 0)
2156 v3s16 p = data->transforming_liquid.pop_front();
2157 m_transforming_liquid.push_back(p);
2163 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
2167 Set is_underground flag for lighting with sunlight.
2169 Refer to map generator heuristics.
2171 NOTE: This is done in initChunkMake
2173 //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
2177 Add sunlight to central block.
2178 This makes in-dark-spawning monsters to not flood the whole thing.
2179 Do not spread the light, though.
2181 /*core::map<v3s16, bool> light_sources;
2182 bool black_air_left = false;
2183 block->propagateSunlight(light_sources, true, &black_air_left);*/
2186 NOTE: Lighting and object adding shouldn't really be here, but
2187 lighting is a bit tricky to move properly to makeBlock.
2188 TODO: Do this the right way anyway, that is, move it to makeBlock.
2189 - There needs to be some way for makeBlock to report back if
2190 the lighting update is going further down because of the
2191 new block blocking light
2196 NOTE: This takes ~60ms, TODO: Investigate why
2199 TimeTaker t("finishBlockMake lighting update");
2201 core::map<v3s16, MapBlock*> lighting_update_blocks;
2204 lighting_update_blocks.insert(block->getPos(), block);
2209 v3s16 p = block->getPos()+v3s16(x,1,z);
2210 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2214 // All modified blocks
2215 // NOTE: Should this be done? If this is not done, then the lighting
2216 // of the others will be updated in a different place, one by one, i
2217 // think... or they might not? Well, at least they are left marked as
2218 // "lighting expired"; it seems that is not handled at all anywhere,
2219 // so enabling this will slow it down A LOT because otherwise it
2220 // would not do this at all. This causes the black trees.
2221 for(core::map<v3s16, MapBlock*>::Iterator
2222 i = changed_blocks.getIterator();
2223 i.atEnd() == false; i++)
2225 lighting_update_blocks.insert(i.getNode()->getKey(),
2226 i.getNode()->getValue());
2228 /*// Also force-add all the upmost blocks for proper sunlight
2229 for(s16 x=-1; x<=1; x++)
2230 for(s16 z=-1; z<=1; z++)
2232 v3s16 p = block->getPos()+v3s16(x,1,z);
2233 lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
2236 updateLighting(lighting_update_blocks, changed_blocks);
2239 Set lighting to non-expired state in all of them.
2240 This is cheating, but it is not fast enough if all of them
2241 would actually be updated.
2243 for(s16 x=-1; x<=1; x++)
2244 for(s16 y=-1; y<=1; y++)
2245 for(s16 z=-1; z<=1; z++)
2247 v3s16 p = block->getPos()+v3s16(x,y,z);
2248 getBlockNoCreateNoEx(p)->setLightingExpired(false);
2251 if(enable_mapgen_debug_info == false)
2252 t.stop(true); // Hide output
2256 Add random objects to block
2258 mapgen::add_random_objects(block);
2261 Go through changed blocks
2263 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
2264 i.atEnd() == false; i++)
2266 MapBlock *block = i.getNode()->getValue();
2269 Update day/night difference cache of the MapBlocks
2271 block->updateDayNightDiff();
2273 Set block as modified
2275 block->raiseModified(MOD_STATE_WRITE_NEEDED);
2279 Set central block as generated
2281 block->setGenerated(true);
2284 Save changed parts of map
2285 NOTE: Will be saved later.
2289 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
2290 <<blockpos.Z<<")"<<std::endl;*/
2292 if(enable_mapgen_debug_info)
2295 Analyze resulting blocks
2297 for(s16 x=-1; x<=1; x++)
2298 for(s16 y=-1; y<=1; y++)
2299 for(s16 z=-1; z<=1; z++)
2301 v3s16 p = block->getPos()+v3s16(x,y,z);
2302 MapBlock *block = getBlockNoCreateNoEx(p);
2304 snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
2305 dstream<<"Generated "<<spos<<": "
2306 <<analyze_block(block)<<std::endl;
2314 ServerMapSector * ServerMap::createSector(v2s16 p2d)
2316 DSTACKF("%s: p2d=(%d,%d)",
2321 Check if it exists already in memory
2323 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2328 Try to load it from disk (with blocks)
2330 //if(loadSectorFull(p2d) == true)
2333 Try to load metadata from disk
2336 if(loadSectorMeta(p2d) == true)
2338 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
2341 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
2342 throw InvalidPositionException("");
2348 Do not create over-limit
2350 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2351 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2352 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2353 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2354 throw InvalidPositionException("createSector(): pos. over limit");
2357 Generate blank sector
2360 sector = new ServerMapSector(this, p2d);
2362 // Sector position on map in nodes
2363 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
2368 m_sectors.insert(p2d, sector);
2374 This is a quick-hand function for calling makeBlock().
2376 MapBlock * ServerMap::generateBlock(
2378 core::map<v3s16, MapBlock*> &modified_blocks
2381 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
2383 /*dstream<<"generateBlock(): "
2384 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2387 bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
2389 TimeTaker timer("generateBlock");
2391 //MapBlock *block = original_dummy;
2393 v2s16 p2d(p.X, p.Z);
2394 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
2397 Do not generate over-limit
2399 if(blockpos_over_limit(p))
2401 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
2402 throw InvalidPositionException("generateBlock(): pos. over limit");
2406 Create block make data
2408 mapgen::BlockMakeData data;
2409 initBlockMake(&data, p);
2415 TimeTaker t("mapgen::make_block()");
2416 mapgen::make_block(&data);
2418 if(enable_mapgen_debug_info == false)
2419 t.stop(true); // Hide output
2423 Blit data back on map, update lighting, add mobs and whatever this does
2425 finishBlockMake(&data, modified_blocks);
2430 MapBlock *block = getBlockNoCreateNoEx(p);
2438 bool erroneus_content = false;
2439 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2440 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2441 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2444 MapNode n = block->getNode(p);
2445 if(n.getContent() == CONTENT_IGNORE)
2447 dstream<<"CONTENT_IGNORE at "
2448 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
2450 erroneus_content = true;
2454 if(erroneus_content)
2463 Generate a completely empty block
2467 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2468 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2470 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2474 n.setContent(CONTENT_AIR);
2476 n.setContent(CONTENT_STONE);
2477 block->setNode(v3s16(x0,y0,z0), n);
2483 if(enable_mapgen_debug_info == false)
2484 timer.stop(true); // Hide output
2489 MapBlock * ServerMap::createBlock(v3s16 p)
2491 DSTACKF("%s: p=(%d,%d,%d)",
2492 __FUNCTION_NAME, p.X, p.Y, p.Z);
2495 Do not create over-limit
2497 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2498 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2499 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2500 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2501 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2502 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2503 throw InvalidPositionException("createBlock(): pos. over limit");
2505 v2s16 p2d(p.X, p.Z);
2508 This will create or load a sector if not found in memory.
2509 If block exists on disk, it will be loaded.
2511 NOTE: On old save formats, this will be slow, as it generates
2512 lighting on blocks for them.
2514 ServerMapSector *sector;
2516 sector = (ServerMapSector*)createSector(p2d);
2517 assert(sector->getId() == MAPSECTOR_SERVER);
2519 catch(InvalidPositionException &e)
2521 dstream<<"createBlock: createSector() failed"<<std::endl;
2525 NOTE: This should not be done, or at least the exception
2526 should not be passed on as std::exception, because it
2527 won't be catched at all.
2529 /*catch(std::exception &e)
2531 dstream<<"createBlock: createSector() failed: "
2532 <<e.what()<<std::endl;
2537 Try to get a block from the sector
2540 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2543 if(block->isDummy())
2548 block = sector->createBlankBlock(block_y);
2552 MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
2554 DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
2556 p.X, p.Y, p.Z, allow_generate);
2559 MapBlock *block = getBlockNoCreateNoEx(p);
2560 if(block && block->isDummy() == false)
2565 MapBlock *block = loadBlock(p);
2572 core::map<v3s16, MapBlock*> modified_blocks;
2573 MapBlock *block = generateBlock(p, modified_blocks);
2577 event.type = MEET_OTHER;
2580 // Copy modified_blocks to event
2581 for(core::map<v3s16, MapBlock*>::Iterator
2582 i = modified_blocks.getIterator();
2583 i.atEnd()==false; i++)
2585 event.modified_blocks.insert(i.getNode()->getKey(), false);
2589 dispatchEvent(&event);
2600 Do not generate over-limit
2602 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2603 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2604 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2605 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2606 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
2607 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
2608 throw InvalidPositionException("emergeBlock(): pos. over limit");
2610 v2s16 p2d(p.X, p.Z);
2613 This will create or load a sector if not found in memory.
2614 If block exists on disk, it will be loaded.
2616 ServerMapSector *sector;
2618 sector = createSector(p2d);
2619 //sector = emergeSector(p2d, changed_blocks);
2621 catch(InvalidPositionException &e)
2623 dstream<<"emergeBlock: createSector() failed: "
2624 <<e.what()<<std::endl;
2625 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2627 <<"You could try to delete it."<<std::endl;
2630 catch(VersionMismatchException &e)
2632 dstream<<"emergeBlock: createSector() failed: "
2633 <<e.what()<<std::endl;
2634 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
2636 <<"You could try to delete it."<<std::endl;
2641 Try to get a block from the sector
2644 bool does_not_exist = false;
2645 bool lighting_expired = false;
2646 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
2648 // If not found, try loading from disk
2651 block = loadBlock(p);
2657 does_not_exist = true;
2659 else if(block->isDummy() == true)
2661 does_not_exist = true;
2663 else if(block->getLightingExpired())
2665 lighting_expired = true;
2670 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
2675 If block was not found on disk and not going to generate a
2676 new one, make sure there is a dummy block in place.
2678 if(only_from_disk && (does_not_exist || lighting_expired))
2680 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
2684 // Create dummy block
2685 block = new MapBlock(this, p, true);
2687 // Add block to sector
2688 sector->insertBlock(block);
2694 //dstream<<"Not found on disk, generating."<<std::endl;
2696 //TimeTaker("emergeBlock() generate");
2698 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
2701 If the block doesn't exist, generate the block.
2705 block = generateBlock(p, block, sector, changed_blocks,
2706 lighting_invalidated_blocks);
2709 if(lighting_expired)
2711 lighting_invalidated_blocks.insert(p, block);
2716 Initially update sunlight
2719 core::map<v3s16, bool> light_sources;
2720 bool black_air_left = false;
2721 bool bottom_invalid =
2722 block->propagateSunlight(light_sources, true,
2725 // If sunlight didn't reach everywhere and part of block is
2726 // above ground, lighting has to be properly updated
2727 //if(black_air_left && some_part_underground)
2730 lighting_invalidated_blocks[block->getPos()] = block;
2735 lighting_invalidated_blocks[block->getPos()] = block;
2744 s16 ServerMap::findGroundLevel(v2s16 p2d)
2748 Uh, just do something random...
2750 // Find existing map from top to down
2753 v3s16 p(p2d.X, max, p2d.Y);
2754 for(; p.Y>min; p.Y--)
2756 MapNode n = getNodeNoEx(p);
2757 if(n.getContent() != CONTENT_IGNORE)
2762 // If this node is not air, go to plan b
2763 if(getNodeNoEx(p).getContent() != CONTENT_AIR)
2765 // Search existing walkable and return it
2766 for(; p.Y>min; p.Y--)
2768 MapNode n = getNodeNoEx(p);
2769 if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
2778 Determine from map generator noise functions
2781 s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
2784 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
2785 //return (s16)level;
2788 void ServerMap::createDatabase() {
2791 e = sqlite3_exec(m_database,
2792 "CREATE TABLE IF NOT EXISTS `blocks` ("
2793 "`pos` INT NOT NULL PRIMARY KEY,"
2796 , NULL, NULL, NULL);
2797 if(e == SQLITE_ABORT)
2798 throw FileNotGoodException("Could not create database structure");
2800 dstream<<"Server: Database structure was created";
2803 void ServerMap::verifyDatabase() {
2808 std::string dbp = m_savedir + "/map.sqlite";
2809 bool needs_create = false;
2813 Open the database connection
2816 createDirs(m_savedir);
2818 if(!fs::PathExists(dbp))
2819 needs_create = true;
2821 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
2822 if(d != SQLITE_OK) {
2823 dstream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
2824 throw FileNotGoodException("Cannot open database file");
2830 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
2831 if(d != SQLITE_OK) {
2832 dstream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2833 throw FileNotGoodException("Cannot prepare read statement");
2836 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
2837 if(d != SQLITE_OK) {
2838 dstream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
2839 throw FileNotGoodException("Cannot prepare write statement");
2842 dstream<<"Server: Database opened"<<std::endl;
2846 bool ServerMap::loadFromFolders() {
2847 if(!m_database && !fs::PathExists(m_savedir + "/map.sqlite"))
2852 int ServerMap::getBlockAsInteger(const v3s16 pos) {
2853 return (pos.Z+2048)*16777216 + (pos.Y+2048)*4096 + pos.X;
2856 void ServerMap::createDirs(std::string path)
2858 if(fs::CreateAllDirs(path) == false)
2860 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2861 <<"\""<<path<<"\""<<std::endl;
2862 throw BaseException("ServerMap failed to create directory");
2866 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
2872 snprintf(cc, 9, "%.4x%.4x",
2873 (unsigned int)pos.X&0xffff,
2874 (unsigned int)pos.Y&0xffff);
2876 return m_savedir + "/sectors/" + cc;
2878 snprintf(cc, 9, "%.3x/%.3x",
2879 (unsigned int)pos.X&0xfff,
2880 (unsigned int)pos.Y&0xfff);
2882 return m_savedir + "/sectors2/" + cc;
2888 v2s16 ServerMap::getSectorPos(std::string dirname)
2892 size_t spos = dirname.rfind('/') + 1;
2893 assert(spos != std::string::npos);
2894 if(dirname.size() - spos == 8)
2897 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
2899 else if(dirname.size() - spos == 3)
2902 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
2903 // Sign-extend the 12 bit values up to 16 bits...
2904 if(x&0x800) x|=0xF000;
2905 if(y&0x800) y|=0xF000;
2912 v2s16 pos((s16)x, (s16)y);
2916 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2918 v2s16 p2d = getSectorPos(sectordir);
2920 if(blockfile.size() != 4){
2921 throw InvalidFilenameException("Invalid block filename");
2924 int r = sscanf(blockfile.c_str(), "%4x", &y);
2926 throw InvalidFilenameException("Invalid block filename");
2927 return v3s16(p2d.X, y, p2d.Y);
2930 std::string ServerMap::getBlockFilename(v3s16 p)
2933 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
2937 void ServerMap::save(bool only_changed)
2939 DSTACK(__FUNCTION_NAME);
2940 if(m_map_saving_enabled == false)
2942 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2946 if(only_changed == false)
2947 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2950 if(only_changed == false || m_map_metadata_changed)
2955 u32 sector_meta_count = 0;
2956 u32 block_count = 0;
2957 u32 block_count_all = 0; // Number of blocks in memory
2960 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2961 for(; i.atEnd() == false; i++)
2963 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2964 assert(sector->getId() == MAPSECTOR_SERVER);
2966 if(sector->differs_from_disk || only_changed == false)
2968 saveSectorMeta(sector);
2969 sector_meta_count++;
2971 core::list<MapBlock*> blocks;
2972 sector->getBlocks(blocks);
2973 core::list<MapBlock*>::Iterator j;
2975 //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
2976 for(j=blocks.begin(); j!=blocks.end(); j++)
2978 MapBlock *block = *j;
2982 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
2983 || only_changed == false)
2988 /*dstream<<"ServerMap: Written block ("
2989 <<block->getPos().X<<","
2990 <<block->getPos().Y<<","
2991 <<block->getPos().Z<<")"
2994 //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
3000 Only print if something happened or saved whole map
3002 if(only_changed == false || sector_meta_count != 0
3003 || block_count != 0)
3005 dstream<<DTIME<<"ServerMap: Written: "
3006 <<sector_meta_count<<" sector metadata files, "
3007 <<block_count<<" block files"
3008 <<", "<<block_count_all<<" blocks in memory."
3013 void ServerMap::saveMapMeta()
3015 DSTACK(__FUNCTION_NAME);
3017 dstream<<"INFO: ServerMap::saveMapMeta(): "
3021 createDirs(m_savedir);
3023 std::string fullpath = m_savedir + "/map_meta.txt";
3024 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
3025 if(os.good() == false)
3027 dstream<<"ERROR: ServerMap::saveMapMeta(): "
3028 <<"could not open"<<fullpath<<std::endl;
3029 throw FileNotGoodException("Cannot open chunk metadata");
3033 params.setU64("seed", m_seed);
3035 params.writeLines(os);
3037 os<<"[end_of_params]\n";
3039 m_map_metadata_changed = false;
3042 void ServerMap::loadMapMeta()
3044 DSTACK(__FUNCTION_NAME);
3046 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
3049 std::string fullpath = m_savedir + "/map_meta.txt";
3050 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3051 if(is.good() == false)
3053 dstream<<"ERROR: ServerMap::loadMapMeta(): "
3054 <<"could not open"<<fullpath<<std::endl;
3055 throw FileNotGoodException("Cannot open map metadata");
3063 throw SerializationError
3064 ("ServerMap::loadMapMeta(): [end_of_params] not found");
3066 std::getline(is, line);
3067 std::string trimmedline = trim(line);
3068 if(trimmedline == "[end_of_params]")
3070 params.parseConfigLine(line);
3073 m_seed = params.getU64("seed");
3075 dstream<<"INFO: ServerMap::loadMapMeta(): "
3080 void ServerMap::saveSectorMeta(ServerMapSector *sector)
3082 DSTACK(__FUNCTION_NAME);
3083 // Format used for writing
3084 u8 version = SER_FMT_VER_HIGHEST;
3086 v2s16 pos = sector->getPos();
3087 std::string dir = getSectorDir(pos);
3090 std::string fullpath = dir + "/meta";
3091 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3092 if(o.good() == false)
3093 throw FileNotGoodException("Cannot open sector metafile");
3095 sector->serialize(o, version);
3097 sector->differs_from_disk = false;
3100 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
3102 DSTACK(__FUNCTION_NAME);
3104 v2s16 p2d = getSectorPos(sectordir);
3106 ServerMapSector *sector = NULL;
3108 std::string fullpath = sectordir + "/meta";
3109 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3110 if(is.good() == false)
3112 // If the directory exists anyway, it probably is in some old
3113 // format. Just go ahead and create the sector.
3114 if(fs::PathExists(sectordir))
3116 /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
3117 <<fullpath<<" doesn't exist but directory does."
3118 <<" Continuing with a sector with no metadata."
3120 sector = new ServerMapSector(this, p2d);
3121 m_sectors.insert(p2d, sector);
3125 throw FileNotGoodException("Cannot open sector metafile");
3130 sector = ServerMapSector::deSerialize
3131 (is, this, p2d, m_sectors);
3133 saveSectorMeta(sector);
3136 sector->differs_from_disk = false;
3141 bool ServerMap::loadSectorMeta(v2s16 p2d)
3143 DSTACK(__FUNCTION_NAME);
3145 MapSector *sector = NULL;
3147 // The directory layout we're going to load from.
3148 // 1 - original sectors/xxxxzzzz/
3149 // 2 - new sectors2/xxx/zzz/
3150 // If we load from anything but the latest structure, we will
3151 // immediately save to the new one, and remove the old.
3153 std::string sectordir1 = getSectorDir(p2d, 1);
3154 std::string sectordir;
3155 if(fs::PathExists(sectordir1))
3157 sectordir = sectordir1;
3162 sectordir = getSectorDir(p2d, 2);
3166 sector = loadSectorMeta(sectordir, loadlayout != 2);
3168 catch(InvalidFilenameException &e)
3172 catch(FileNotGoodException &e)
3176 catch(std::exception &e)
3185 bool ServerMap::loadSectorFull(v2s16 p2d)
3187 DSTACK(__FUNCTION_NAME);
3189 MapSector *sector = NULL;
3191 // The directory layout we're going to load from.
3192 // 1 - original sectors/xxxxzzzz/
3193 // 2 - new sectors2/xxx/zzz/
3194 // If we load from anything but the latest structure, we will
3195 // immediately save to the new one, and remove the old.
3197 std::string sectordir1 = getSectorDir(p2d, 1);
3198 std::string sectordir;
3199 if(fs::PathExists(sectordir1))
3201 sectordir = sectordir1;
3206 sectordir = getSectorDir(p2d, 2);
3210 sector = loadSectorMeta(sectordir, loadlayout != 2);
3212 catch(InvalidFilenameException &e)
3216 catch(FileNotGoodException &e)
3220 catch(std::exception &e)
3228 std::vector<fs::DirListNode> list2 = fs::GetDirListing
3230 std::vector<fs::DirListNode>::iterator i2;
3231 for(i2=list2.begin(); i2!=list2.end(); i2++)
3237 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
3239 catch(InvalidFilenameException &e)
3241 // This catches unknown crap in directory
3247 dstream<<"Sector converted to new layout - deleting "<<
3248 sectordir1<<std::endl;
3249 fs::RecursiveDelete(sectordir1);
3256 void ServerMap::beginSave() {
3258 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
3259 dstream<<"WARNING: beginSave() failed, saving might be slow.";
3262 void ServerMap::endSave() {
3264 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
3265 dstream<<"WARNING: endSave() failed, map might not have saved.";
3268 void ServerMap::saveBlock(MapBlock *block)
3270 DSTACK(__FUNCTION_NAME);
3272 Dummy blocks are not written
3274 if(block->isDummy())
3276 /*v3s16 p = block->getPos();
3277 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
3278 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
3282 // Format used for writing
3283 u8 version = SER_FMT_VER_HIGHEST;
3285 v3s16 p3d = block->getPos();
3289 v2s16 p2d(p3d.X, p3d.Z);
3290 std::string sectordir = getSectorDir(p2d);
3292 createDirs(sectordir);
3294 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
3295 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
3296 if(o.good() == false)
3297 throw FileNotGoodException("Cannot open block data");
3300 [0] u8 serialization version
3306 std::ostringstream o(std::ios_base::binary);
3308 o.write((char*)&version, 1);
3311 block->serialize(o, version);
3313 // Write extra data stored on disk
3314 block->serializeDiskExtra(o, version);
3316 // Write block to database
3318 std::string tmp = o.str();
3319 const char *bytes = tmp.c_str();
3321 if(sqlite3_bind_int(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
3322 dstream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3323 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
3324 dstream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
3325 int written = sqlite3_step(m_database_write);
3326 if(written != SQLITE_DONE)
3327 dstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
3328 <<sqlite3_errmsg(m_database)<<std::endl;
3329 // Make ready for later reuse
3330 sqlite3_reset(m_database_write);
3332 // We just wrote it to the disk so clear modified flag
3333 block->resetModified();
3336 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
3338 DSTACK(__FUNCTION_NAME);
3340 std::string fullpath = sectordir+"/"+blockfile;
3343 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
3344 if(is.good() == false)
3345 throw FileNotGoodException("Cannot open block file");
3347 v3s16 p3d = getBlockPos(sectordir, blockfile);
3348 v2s16 p2d(p3d.X, p3d.Z);
3350 assert(sector->getPos() == p2d);
3352 u8 version = SER_FMT_VER_INVALID;
3353 is.read((char*)&version, 1);
3356 throw SerializationError("ServerMap::loadBlock(): Failed"
3357 " to read MapBlock version");
3359 /*u32 block_size = MapBlock::serializedLength(version);
3360 SharedBuffer<u8> data(block_size);
3361 is.read((char*)*data, block_size);*/
3363 // This will always return a sector because we're the server
3364 //MapSector *sector = emergeSector(p2d);
3366 MapBlock *block = NULL;
3367 bool created_new = false;
3368 block = sector->getBlockNoCreateNoEx(p3d.Y);
3371 block = sector->createBlankBlockNoInsert(p3d.Y);
3376 block->deSerialize(is, version);
3378 // Read extra data stored on disk
3379 block->deSerializeDiskExtra(is, version);
3381 // If it's a new block, insert it to the map
3383 sector->insertBlock(block);
3386 Save blocks loaded in old format in new format
3389 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3394 // We just loaded it from the disk, so it's up-to-date.
3395 block->resetModified();
3398 catch(SerializationError &e)
3400 dstream<<"WARNING: Invalid block data on disk "
3401 <<"fullpath="<<fullpath
3402 <<" (SerializationError). "
3403 <<"what()="<<e.what()
3405 //" Ignoring. A new one will be generated.
3408 // TODO: Backup file; name is in fullpath.
3412 void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
3414 DSTACK(__FUNCTION_NAME);
3417 std::istringstream is(*blob, std::ios_base::binary);
3419 u8 version = SER_FMT_VER_INVALID;
3420 is.read((char*)&version, 1);
3423 throw SerializationError("ServerMap::loadBlock(): Failed"
3424 " to read MapBlock version");
3426 /*u32 block_size = MapBlock::serializedLength(version);
3427 SharedBuffer<u8> data(block_size);
3428 is.read((char*)*data, block_size);*/
3430 // This will always return a sector because we're the server
3431 //MapSector *sector = emergeSector(p2d);
3433 MapBlock *block = NULL;
3434 bool created_new = false;
3435 block = sector->getBlockNoCreateNoEx(p3d.Y);
3438 block = sector->createBlankBlockNoInsert(p3d.Y);
3443 block->deSerialize(is, version);
3445 // Read extra data stored on disk
3446 block->deSerializeDiskExtra(is, version);
3448 // If it's a new block, insert it to the map
3450 sector->insertBlock(block);
3453 Save blocks loaded in old format in new format
3456 if(version < SER_FMT_VER_HIGHEST || save_after_load)
3461 // We just loaded it from, so it's up-to-date.
3462 block->resetModified();
3465 catch(SerializationError &e)
3467 dstream<<"WARNING: Invalid block data in database "
3468 <<" (SerializationError). "
3469 <<"what()="<<e.what()
3471 //" Ignoring. A new one will be generated.
3474 // TODO: Copy to a backup database.
3478 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
3480 DSTACK(__FUNCTION_NAME);
3482 v2s16 p2d(blockpos.X, blockpos.Z);
3484 if(!loadFromFolders()) {
3487 if(sqlite3_bind_int(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
3488 dstream<<"WARNING: Could not bind block position for load: "
3489 <<sqlite3_errmsg(m_database)<<std::endl;
3490 if(sqlite3_step(m_database_read) == SQLITE_ROW) {
3492 Make sure sector is loaded
3494 MapSector *sector = createSector(p2d);
3499 const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
3500 size_t len = sqlite3_column_bytes(m_database_read, 0);
3502 std::string datastr(data, len);
3504 loadBlock(&datastr, blockpos, sector, false);
3506 sqlite3_step(m_database_read);
3507 // We should never get more than 1 row, so ok to reset
3508 sqlite3_reset(m_database_read);
3510 return getBlockNoCreateNoEx(blockpos);
3512 sqlite3_reset(m_database_read);
3514 // Not found in database, try the files
3517 // The directory layout we're going to load from.
3518 // 1 - original sectors/xxxxzzzz/
3519 // 2 - new sectors2/xxx/zzz/
3520 // If we load from anything but the latest structure, we will
3521 // immediately save to the new one, and remove the old.
3523 std::string sectordir1 = getSectorDir(p2d, 1);
3524 std::string sectordir;
3525 if(fs::PathExists(sectordir1))
3527 sectordir = sectordir1;
3532 sectordir = getSectorDir(p2d, 2);
3536 Make sure sector is loaded
3538 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3542 sector = loadSectorMeta(sectordir, loadlayout != 2);
3544 catch(InvalidFilenameException &e)
3548 catch(FileNotGoodException &e)
3552 catch(std::exception &e)
3559 Make sure file exists
3562 std::string blockfilename = getBlockFilename(blockpos);
3563 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
3567 Load block and save it to the database
3569 loadBlock(sectordir, blockfilename, sector, true);
3570 return getBlockNoCreateNoEx(blockpos);
3573 void ServerMap::PrintInfo(std::ostream &out)
3584 ClientMap::ClientMap(
3586 MapDrawControl &control,
3587 scene::ISceneNode* parent,
3588 scene::ISceneManager* mgr,
3592 scene::ISceneNode(parent, mgr, id),
3595 m_camera_position(0,0,0),
3596 m_camera_direction(0,0,1)
3598 m_camera_mutex.Init();
3599 assert(m_camera_mutex.IsInitialized());
3601 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
3602 BS*1000000,BS*1000000,BS*1000000);
3605 ClientMap::~ClientMap()
3607 /*JMutexAutoLock lock(mesh_mutex);
3616 MapSector * ClientMap::emergeSector(v2s16 p2d)
3618 DSTACK(__FUNCTION_NAME);
3619 // Check that it doesn't exist already
3621 return getSectorNoGenerate(p2d);
3623 catch(InvalidPositionException &e)
3628 ClientMapSector *sector = new ClientMapSector(this, p2d);
3631 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3632 m_sectors.insert(p2d, sector);
3639 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
3641 DSTACK(__FUNCTION_NAME);
3642 ClientMapSector *sector = NULL;
3644 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3646 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
3650 sector = (ClientMapSector*)n->getValue();
3651 assert(sector->getId() == MAPSECTOR_CLIENT);
3655 sector = new ClientMapSector(this, p2d);
3657 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
3658 m_sectors.insert(p2d, sector);
3662 sector->deSerialize(is);
3666 void ClientMap::OnRegisterSceneNode()
3670 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
3671 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
3674 ISceneNode::OnRegisterSceneNode();
3677 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
3679 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
3680 DSTACK(__FUNCTION_NAME);
3682 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
3685 This is called two times per frame, reset on the non-transparent one
3687 if(pass == scene::ESNRP_SOLID)
3689 m_last_drawn_sectors.clear();
3693 Get time for measuring timeout.
3695 Measuring time is very useful for long delays when the
3696 machine is swapping a lot.
3698 int time1 = time(0);
3700 //u32 daynight_ratio = m_client->getDayNightRatio();
3702 m_camera_mutex.Lock();
3703 v3f camera_position = m_camera_position;
3704 v3f camera_direction = m_camera_direction;
3705 m_camera_mutex.Unlock();
3708 Get all blocks and draw all visible ones
3711 v3s16 cam_pos_nodes(
3712 camera_position.X / BS,
3713 camera_position.Y / BS,
3714 camera_position.Z / BS);
3716 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
3718 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
3719 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
3721 // Take a fair amount as we will be dropping more out later
3723 p_nodes_min.X / MAP_BLOCKSIZE - 2,
3724 p_nodes_min.Y / MAP_BLOCKSIZE - 2,
3725 p_nodes_min.Z / MAP_BLOCKSIZE - 2);
3727 p_nodes_max.X / MAP_BLOCKSIZE + 1,
3728 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
3729 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
3731 u32 vertex_count = 0;
3733 // For limiting number of mesh updates per frame
3734 u32 mesh_update_count = 0;
3736 u32 blocks_would_have_drawn = 0;
3737 u32 blocks_drawn = 0;
3739 int timecheck_counter = 0;
3740 core::map<v2s16, MapSector*>::Iterator si;
3741 si = m_sectors.getIterator();
3742 for(; si.atEnd() == false; si++)
3745 timecheck_counter++;
3746 if(timecheck_counter > 50)
3748 timecheck_counter = 0;
3749 int time2 = time(0);
3750 if(time2 > time1 + 4)
3752 dstream<<"ClientMap::renderMap(): "
3753 "Rendering takes ages, returning."
3760 MapSector *sector = si.getNode()->getValue();
3761 v2s16 sp = sector->getPos();
3763 if(m_control.range_all == false)
3765 if(sp.X < p_blocks_min.X
3766 || sp.X > p_blocks_max.X
3767 || sp.Y < p_blocks_min.Z
3768 || sp.Y > p_blocks_max.Z)
3772 core::list< MapBlock * > sectorblocks;
3773 sector->getBlocks(sectorblocks);
3779 u32 sector_blocks_drawn = 0;
3781 core::list< MapBlock * >::Iterator i;
3782 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
3784 MapBlock *block = *i;
3787 Compare block position to camera position, skip
3788 if not seen on display
3791 float range = 100000 * BS;
3792 if(m_control.range_all == false)
3793 range = m_control.wanted_range * BS;
3796 if(isBlockInSight(block->getPos(), camera_position,
3797 camera_direction, range, &d) == false)
3802 // Okay, this block will be drawn. Reset usage timer.
3803 block->resetUsageTimer();
3805 // This is ugly (spherical distance limit?)
3806 /*if(m_control.range_all == false &&
3807 d - 0.5*BS*MAP_BLOCKSIZE > range)
3812 Update expired mesh (used for day/night change)
3814 It doesn't work exactly like it should now with the
3815 tasked mesh update but whatever.
3818 bool mesh_expired = false;
3821 JMutexAutoLock lock(block->mesh_mutex);
3823 mesh_expired = block->getMeshExpired();
3825 // Mesh has not been expired and there is no mesh:
3826 // block has no content
3827 if(block->mesh == NULL && mesh_expired == false)
3831 f32 faraway = BS*50;
3832 //f32 faraway = m_control.wanted_range * BS;
3835 This has to be done with the mesh_mutex unlocked
3837 // Pretty random but this should work somewhat nicely
3838 if(mesh_expired && (
3839 (mesh_update_count < 3
3840 && (d < faraway || mesh_update_count < 2)
3843 (m_control.range_all && mesh_update_count < 20)
3846 /*if(mesh_expired && mesh_update_count < 6
3847 && (d < faraway || mesh_update_count < 3))*/
3849 mesh_update_count++;
3851 // Mesh has been expired: generate new mesh
3852 //block->updateMesh(daynight_ratio);
3853 m_client->addUpdateMeshTask(block->getPos());
3855 mesh_expired = false;
3860 Draw the faces of the block
3863 JMutexAutoLock lock(block->mesh_mutex);
3865 scene::SMesh *mesh = block->mesh;
3870 blocks_would_have_drawn++;
3871 if(blocks_drawn >= m_control.wanted_max_blocks
3872 && m_control.range_all == false
3873 && d > m_control.wanted_min_range * BS)
3877 sector_blocks_drawn++;
3879 u32 c = mesh->getMeshBufferCount();
3881 for(u32 i=0; i<c; i++)
3883 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
3884 const video::SMaterial& material = buf->getMaterial();
3885 video::IMaterialRenderer* rnd =
3886 driver->getMaterialRenderer(material.MaterialType);
3887 bool transparent = (rnd && rnd->isTransparent());
3888 // Render transparent on transparent pass and likewise.
3889 if(transparent == is_transparent_pass)
3892 This *shouldn't* hurt too much because Irrlicht
3893 doesn't change opengl textures if the old
3894 material is set again.
3896 driver->setMaterial(buf->getMaterial());
3897 driver->drawMeshBuffer(buf);
3898 vertex_count += buf->getVertexCount();
3902 } // foreach sectorblocks
3904 if(sector_blocks_drawn != 0)
3906 m_last_drawn_sectors[sp] = true;
3910 m_control.blocks_drawn = blocks_drawn;
3911 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
3913 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
3914 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
3917 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
3918 core::map<v3s16, MapBlock*> *affected_blocks)
3920 bool changed = false;
3922 Add it to all blocks touching it
3925 v3s16(0,0,0), // this
3926 v3s16(0,0,1), // back
3927 v3s16(0,1,0), // top
3928 v3s16(1,0,0), // right
3929 v3s16(0,0,-1), // front
3930 v3s16(0,-1,0), // bottom
3931 v3s16(-1,0,0), // left
3933 for(u16 i=0; i<7; i++)
3935 v3s16 p2 = p + dirs[i];
3936 // Block position of neighbor (or requested) node
3937 v3s16 blockpos = getNodeBlockPos(p2);
3938 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3939 if(blockref == NULL)
3941 // Relative position of requested node
3942 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3943 if(blockref->setTempMod(relpos, mod))
3948 if(changed && affected_blocks!=NULL)
3950 for(u16 i=0; i<7; i++)
3952 v3s16 p2 = p + dirs[i];
3953 // Block position of neighbor (or requested) node
3954 v3s16 blockpos = getNodeBlockPos(p2);
3955 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3956 if(blockref == NULL)
3958 affected_blocks->insert(blockpos, blockref);
3964 bool ClientMap::clearTempMod(v3s16 p,
3965 core::map<v3s16, MapBlock*> *affected_blocks)
3967 bool changed = false;
3969 v3s16(0,0,0), // this
3970 v3s16(0,0,1), // back
3971 v3s16(0,1,0), // top
3972 v3s16(1,0,0), // right
3973 v3s16(0,0,-1), // front
3974 v3s16(0,-1,0), // bottom
3975 v3s16(-1,0,0), // left
3977 for(u16 i=0; i<7; i++)
3979 v3s16 p2 = p + dirs[i];
3980 // Block position of neighbor (or requested) node
3981 v3s16 blockpos = getNodeBlockPos(p2);
3982 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
3983 if(blockref == NULL)
3985 // Relative position of requested node
3986 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
3987 if(blockref->clearTempMod(relpos))
3992 if(changed && affected_blocks!=NULL)
3994 for(u16 i=0; i<7; i++)
3996 v3s16 p2 = p + dirs[i];
3997 // Block position of neighbor (or requested) node
3998 v3s16 blockpos = getNodeBlockPos(p2);
3999 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
4000 if(blockref == NULL)
4002 affected_blocks->insert(blockpos, blockref);
4008 void ClientMap::expireMeshes(bool only_daynight_diffed)
4010 TimeTaker timer("expireMeshes()");
4012 core::map<v2s16, MapSector*>::Iterator si;
4013 si = m_sectors.getIterator();
4014 for(; si.atEnd() == false; si++)
4016 MapSector *sector = si.getNode()->getValue();
4018 core::list< MapBlock * > sectorblocks;
4019 sector->getBlocks(sectorblocks);
4021 core::list< MapBlock * >::Iterator i;
4022 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
4024 MapBlock *block = *i;
4026 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
4032 JMutexAutoLock lock(block->mesh_mutex);
4033 if(block->mesh != NULL)
4035 /*block->mesh->drop();
4036 block->mesh = NULL;*/
4037 block->setMeshExpired(true);
4044 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
4046 assert(mapType() == MAPTYPE_CLIENT);
4049 v3s16 p = blockpos + v3s16(0,0,0);
4050 MapBlock *b = getBlockNoCreate(p);
4051 b->updateMesh(daynight_ratio);
4052 //b->setMeshExpired(true);
4054 catch(InvalidPositionException &e){}
4057 v3s16 p = blockpos + v3s16(-1,0,0);
4058 MapBlock *b = getBlockNoCreate(p);
4059 b->updateMesh(daynight_ratio);
4060 //b->setMeshExpired(true);
4062 catch(InvalidPositionException &e){}
4064 v3s16 p = blockpos + v3s16(0,-1,0);
4065 MapBlock *b = getBlockNoCreate(p);
4066 b->updateMesh(daynight_ratio);
4067 //b->setMeshExpired(true);
4069 catch(InvalidPositionException &e){}
4071 v3s16 p = blockpos + v3s16(0,0,-1);
4072 MapBlock *b = getBlockNoCreate(p);
4073 b->updateMesh(daynight_ratio);
4074 //b->setMeshExpired(true);
4076 catch(InvalidPositionException &e){}
4081 Update mesh of block in which the node is, and if the node is at the
4082 leading edge, update the appropriate leading blocks too.
4084 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
4092 v3s16 blockposes[4];
4093 for(u32 i=0; i<4; i++)
4095 v3s16 np = nodepos + dirs[i];
4096 blockposes[i] = getNodeBlockPos(np);
4097 // Don't update mesh of block if it has been done already
4098 bool already_updated = false;
4099 for(u32 j=0; j<i; j++)
4101 if(blockposes[j] == blockposes[i])
4103 already_updated = true;
4110 MapBlock *b = getBlockNoCreate(blockposes[i]);
4111 b->updateMesh(daynight_ratio);
4116 void ClientMap::PrintInfo(std::ostream &out)
4127 MapVoxelManipulator::MapVoxelManipulator(Map *map)
4132 MapVoxelManipulator::~MapVoxelManipulator()
4134 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
4138 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4140 TimeTaker timer1("emerge", &emerge_time);
4142 // Units of these are MapBlocks
4143 v3s16 p_min = getNodeBlockPos(a.MinEdge);
4144 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
4146 VoxelArea block_area_nodes
4147 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4149 addArea(block_area_nodes);
4151 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4152 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4153 for(s32 x=p_min.X; x<=p_max.X; x++)
4156 core::map<v3s16, bool>::Node *n;
4157 n = m_loaded_blocks.find(p);
4161 bool block_data_inexistent = false;
4164 TimeTaker timer1("emerge load", &emerge_load_time);
4166 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
4167 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4170 dstream<<std::endl;*/
4172 MapBlock *block = m_map->getBlockNoCreate(p);
4173 if(block->isDummy())
4174 block_data_inexistent = true;
4176 block->copyTo(*this);
4178 catch(InvalidPositionException &e)
4180 block_data_inexistent = true;
4183 if(block_data_inexistent)
4185 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4186 // Fill with VOXELFLAG_INEXISTENT
4187 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4188 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4190 s32 i = m_area.index(a.MinEdge.X,y,z);
4191 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4195 m_loaded_blocks.insert(p, !block_data_inexistent);
4198 //dstream<<"emerge done"<<std::endl;
4202 SUGG: Add an option to only update eg. water and air nodes.
4203 This will make it interfere less with important stuff if
4206 void MapVoxelManipulator::blitBack
4207 (core::map<v3s16, MapBlock*> & modified_blocks)
4209 if(m_area.getExtent() == v3s16(0,0,0))
4212 //TimeTaker timer1("blitBack");
4214 /*dstream<<"blitBack(): m_loaded_blocks.size()="
4215 <<m_loaded_blocks.size()<<std::endl;*/
4218 Initialize block cache
4220 v3s16 blockpos_last;
4221 MapBlock *block = NULL;
4222 bool block_checked_in_modified = false;
4224 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
4225 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
4226 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
4230 u8 f = m_flags[m_area.index(p)];
4231 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
4234 MapNode &n = m_data[m_area.index(p)];
4236 v3s16 blockpos = getNodeBlockPos(p);
4241 if(block == NULL || blockpos != blockpos_last){
4242 block = m_map->getBlockNoCreate(blockpos);
4243 blockpos_last = blockpos;
4244 block_checked_in_modified = false;
4247 // Calculate relative position in block
4248 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
4250 // Don't continue if nothing has changed here
4251 if(block->getNode(relpos) == n)
4254 //m_map->setNode(m_area.MinEdge + p, n);
4255 block->setNode(relpos, n);
4258 Make sure block is in modified_blocks
4260 if(block_checked_in_modified == false)
4262 modified_blocks[blockpos] = block;
4263 block_checked_in_modified = true;
4266 catch(InvalidPositionException &e)
4272 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
4273 MapVoxelManipulator(map),
4274 m_create_area(false)
4278 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
4282 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
4284 // Just create the area so that it can be pointed to
4285 VoxelManipulator::emerge(a, caller_id);
4288 void ManualMapVoxelManipulator::initialEmerge(
4289 v3s16 blockpos_min, v3s16 blockpos_max)
4291 TimeTaker timer1("initialEmerge", &emerge_time);
4293 // Units of these are MapBlocks
4294 v3s16 p_min = blockpos_min;
4295 v3s16 p_max = blockpos_max;
4297 VoxelArea block_area_nodes
4298 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4300 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
4303 dstream<<"initialEmerge: area: ";
4304 block_area_nodes.print(dstream);
4305 dstream<<" ("<<size_MB<<"MB)";
4309 addArea(block_area_nodes);
4311 for(s32 z=p_min.Z; z<=p_max.Z; z++)
4312 for(s32 y=p_min.Y; y<=p_max.Y; y++)
4313 for(s32 x=p_min.X; x<=p_max.X; x++)
4316 core::map<v3s16, bool>::Node *n;
4317 n = m_loaded_blocks.find(p);
4321 bool block_data_inexistent = false;
4324 TimeTaker timer1("emerge load", &emerge_load_time);
4326 MapBlock *block = m_map->getBlockNoCreate(p);
4327 if(block->isDummy())
4328 block_data_inexistent = true;
4330 block->copyTo(*this);
4332 catch(InvalidPositionException &e)
4334 block_data_inexistent = true;
4337 if(block_data_inexistent)
4340 Mark area inexistent
4342 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
4343 // Fill with VOXELFLAG_INEXISTENT
4344 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
4345 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
4347 s32 i = m_area.index(a.MinEdge.X,y,z);
4348 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
4352 m_loaded_blocks.insert(p, !block_data_inexistent);
4356 void ManualMapVoxelManipulator::blitBackAll(
4357 core::map<v3s16, MapBlock*> * modified_blocks)
4359 if(m_area.getExtent() == v3s16(0,0,0))
4363 Copy data of all blocks
4365 for(core::map<v3s16, bool>::Iterator
4366 i = m_loaded_blocks.getIterator();
4367 i.atEnd() == false; i++)
4369 v3s16 p = i.getNode()->getKey();
4370 bool existed = i.getNode()->getValue();
4371 if(existed == false)
4373 // The Great Bug was found using this
4374 /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
4375 <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4379 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
4382 dstream<<"WARNING: "<<__FUNCTION_NAME
4383 <<": got NULL block "
4384 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4389 block->copyFrom(*this);
4392 modified_blocks->insert(p, block);