3 Copyright (C) 2010 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.
22 #include "jmutexautolock.h"
30 #include "serverobject.h"
36 Map::Map(std::ostream &dout):
40 m_sector_mutex.Init();
41 assert(m_sector_mutex.IsInitialized());
49 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
50 for(; i.atEnd() == false; i++)
52 MapSector *sector = i.getNode()->getValue();
57 void Map::addEventReceiver(MapEventReceiver *event_receiver)
59 m_event_receivers.insert(event_receiver, false);
62 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
64 if(m_event_receivers.find(event_receiver) == NULL)
66 m_event_receivers.remove(event_receiver);
69 void Map::dispatchEvent(MapEditEvent *event)
71 for(core::map<MapEventReceiver*, bool>::Iterator
72 i = m_event_receivers.getIterator();
73 i.atEnd()==false; i++)
75 MapEventReceiver* event_receiver = i.getNode()->getKey();
76 event_receiver->onMapEditEvent(event);
80 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
82 if(m_sector_cache != NULL && p == m_sector_cache_p){
83 MapSector * sector = m_sector_cache;
84 // Reset inactivity timer
85 sector->usage_timer = 0.0;
89 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
94 MapSector *sector = n->getValue();
96 // Cache the last result
98 m_sector_cache = sector;
100 // Reset inactivity timer
101 sector->usage_timer = 0.0;
105 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
107 JMutexAutoLock lock(m_sector_mutex);
109 return getSectorNoGenerateNoExNoLock(p);
112 MapSector * Map::getSectorNoGenerate(v2s16 p)
114 MapSector *sector = getSectorNoGenerateNoEx(p);
116 throw InvalidPositionException();
121 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
123 v2s16 p2d(p3d.X, p3d.Z);
124 MapSector * sector = getSectorNoGenerate(p2d);
126 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
131 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
135 v2s16 p2d(p3d.X, p3d.Z);
136 MapSector * sector = getSectorNoGenerate(p2d);
137 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
140 catch(InvalidPositionException &e)
146 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
148 v2s16 p2d(p3d.X, p3d.Z);
149 MapSector * sector = getSectorCreate(p2d);
151 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
154 block = sector->createBlankBlock(p3d.Y);
158 bool Map::isNodeUnderground(v3s16 p)
160 v3s16 blockpos = getNodeBlockPos(p);
162 MapBlock * block = getBlockNoCreate(blockpos);
163 return block->getIsUnderground();
165 catch(InvalidPositionException &e)
172 Goes recursively through the neighbours of the node.
174 Alters only transparent nodes.
176 If the lighting of the neighbour is lower than the lighting of
177 the node was (before changing it to 0 at the step before), the
178 lighting of the neighbour is set to 0 and then the same stuff
179 repeats for the neighbour.
181 The ending nodes of the routine are stored in light_sources.
182 This is useful when a light is removed. In such case, this
183 routine can be called for the light node and then again for
184 light_sources to re-light the area without the removed light.
186 values of from_nodes are lighting values.
188 void Map::unspreadLight(enum LightBank bank,
189 core::map<v3s16, u8> & from_nodes,
190 core::map<v3s16, bool> & light_sources,
191 core::map<v3s16, MapBlock*> & modified_blocks)
194 v3s16(0,0,1), // back
196 v3s16(1,0,0), // right
197 v3s16(0,0,-1), // front
198 v3s16(0,-1,0), // bottom
199 v3s16(-1,0,0), // left
202 if(from_nodes.size() == 0)
205 u32 blockchangecount = 0;
207 core::map<v3s16, u8> unlighted_nodes;
208 core::map<v3s16, u8>::Iterator j;
209 j = from_nodes.getIterator();
212 Initialize block cache
215 MapBlock *block = NULL;
216 // Cache this a bit, too
217 bool block_checked_in_modified = false;
219 for(; j.atEnd() == false; j++)
221 v3s16 pos = j.getNode()->getKey();
222 v3s16 blockpos = getNodeBlockPos(pos);
224 // Only fetch a new block if the block position has changed
226 if(block == NULL || blockpos != blockpos_last){
227 block = getBlockNoCreate(blockpos);
228 blockpos_last = blockpos;
230 block_checked_in_modified = false;
234 catch(InvalidPositionException &e)
242 // Calculate relative position in block
243 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
245 // Get node straight from the block
246 MapNode n = block->getNode(relpos);
248 u8 oldlight = j.getNode()->getValue();
250 // Loop through 6 neighbors
251 for(u16 i=0; i<6; i++)
253 // Get the position of the neighbor node
254 v3s16 n2pos = pos + dirs[i];
256 // Get the block where the node is located
257 v3s16 blockpos = getNodeBlockPos(n2pos);
261 // Only fetch a new block if the block position has changed
263 if(block == NULL || blockpos != blockpos_last){
264 block = getBlockNoCreate(blockpos);
265 blockpos_last = blockpos;
267 block_checked_in_modified = false;
271 catch(InvalidPositionException &e)
276 // Calculate relative position in block
277 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
278 // Get node straight from the block
279 MapNode n2 = block->getNode(relpos);
281 bool changed = false;
283 //TODO: Optimize output by optimizing light_sources?
286 If the neighbor is dimmer than what was specified
287 as oldlight (the light of the previous node)
289 if(n2.getLight(bank) < oldlight)
292 And the neighbor is transparent and it has some light
294 if(n2.light_propagates() && n2.getLight(bank) != 0)
297 Set light to 0 and add to queue
300 u8 current_light = n2.getLight(bank);
301 n2.setLight(bank, 0);
302 block->setNode(relpos, n2);
304 unlighted_nodes.insert(n2pos, current_light);
308 Remove from light_sources if it is there
309 NOTE: This doesn't happen nearly at all
311 /*if(light_sources.find(n2pos))
313 std::cout<<"Removed from light_sources"<<std::endl;
314 light_sources.remove(n2pos);
319 if(light_sources.find(n2pos) != NULL)
320 light_sources.remove(n2pos);*/
323 light_sources.insert(n2pos, true);
326 // Add to modified_blocks
327 if(changed == true && block_checked_in_modified == false)
329 // If the block is not found in modified_blocks, add.
330 if(modified_blocks.find(blockpos) == NULL)
332 modified_blocks.insert(blockpos, block);
334 block_checked_in_modified = true;
337 catch(InvalidPositionException &e)
344 /*dstream<<"unspreadLight(): Changed block "
345 <<blockchangecount<<" times"
346 <<" for "<<from_nodes.size()<<" nodes"
349 if(unlighted_nodes.size() > 0)
350 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
354 A single-node wrapper of the above
356 void Map::unLightNeighbors(enum LightBank bank,
357 v3s16 pos, u8 lightwas,
358 core::map<v3s16, bool> & light_sources,
359 core::map<v3s16, MapBlock*> & modified_blocks)
361 core::map<v3s16, u8> from_nodes;
362 from_nodes.insert(pos, lightwas);
364 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
368 Lights neighbors of from_nodes, collects all them and then
371 void Map::spreadLight(enum LightBank bank,
372 core::map<v3s16, bool> & from_nodes,
373 core::map<v3s16, MapBlock*> & modified_blocks)
375 const v3s16 dirs[6] = {
376 v3s16(0,0,1), // back
378 v3s16(1,0,0), // right
379 v3s16(0,0,-1), // front
380 v3s16(0,-1,0), // bottom
381 v3s16(-1,0,0), // left
384 if(from_nodes.size() == 0)
387 u32 blockchangecount = 0;
389 core::map<v3s16, bool> lighted_nodes;
390 core::map<v3s16, bool>::Iterator j;
391 j = from_nodes.getIterator();
394 Initialize block cache
397 MapBlock *block = NULL;
398 // Cache this a bit, too
399 bool block_checked_in_modified = false;
401 for(; j.atEnd() == false; j++)
402 //for(; j != from_nodes.end(); j++)
404 v3s16 pos = j.getNode()->getKey();
406 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
407 v3s16 blockpos = getNodeBlockPos(pos);
409 // Only fetch a new block if the block position has changed
411 if(block == NULL || blockpos != blockpos_last){
412 block = getBlockNoCreate(blockpos);
413 blockpos_last = blockpos;
415 block_checked_in_modified = false;
419 catch(InvalidPositionException &e)
427 // Calculate relative position in block
428 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
430 // Get node straight from the block
431 MapNode n = block->getNode(relpos);
433 u8 oldlight = n.getLight(bank);
434 u8 newlight = diminish_light(oldlight);
436 // Loop through 6 neighbors
437 for(u16 i=0; i<6; i++){
438 // Get the position of the neighbor node
439 v3s16 n2pos = pos + dirs[i];
441 // Get the block where the node is located
442 v3s16 blockpos = getNodeBlockPos(n2pos);
446 // Only fetch a new block if the block position has changed
448 if(block == NULL || blockpos != blockpos_last){
449 block = getBlockNoCreate(blockpos);
450 blockpos_last = blockpos;
452 block_checked_in_modified = false;
456 catch(InvalidPositionException &e)
461 // Calculate relative position in block
462 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
463 // Get node straight from the block
464 MapNode n2 = block->getNode(relpos);
466 bool changed = false;
468 If the neighbor is brighter than the current node,
469 add to list (it will light up this node on its turn)
471 if(n2.getLight(bank) > undiminish_light(oldlight))
473 lighted_nodes.insert(n2pos, true);
474 //lighted_nodes.push_back(n2pos);
478 If the neighbor is dimmer than how much light this node
479 would spread on it, add to list
481 if(n2.getLight(bank) < newlight)
483 if(n2.light_propagates())
485 n2.setLight(bank, newlight);
486 block->setNode(relpos, n2);
487 lighted_nodes.insert(n2pos, true);
488 //lighted_nodes.push_back(n2pos);
493 // Add to modified_blocks
494 if(changed == true && block_checked_in_modified == false)
496 // If the block is not found in modified_blocks, add.
497 if(modified_blocks.find(blockpos) == NULL)
499 modified_blocks.insert(blockpos, block);
501 block_checked_in_modified = true;
504 catch(InvalidPositionException &e)
511 /*dstream<<"spreadLight(): Changed block "
512 <<blockchangecount<<" times"
513 <<" for "<<from_nodes.size()<<" nodes"
516 if(lighted_nodes.size() > 0)
517 spreadLight(bank, lighted_nodes, modified_blocks);
521 A single-node source variation of the above.
523 void Map::lightNeighbors(enum LightBank bank,
525 core::map<v3s16, MapBlock*> & modified_blocks)
527 core::map<v3s16, bool> from_nodes;
528 from_nodes.insert(pos, true);
529 spreadLight(bank, from_nodes, modified_blocks);
532 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
535 v3s16(0,0,1), // back
537 v3s16(1,0,0), // right
538 v3s16(0,0,-1), // front
539 v3s16(0,-1,0), // bottom
540 v3s16(-1,0,0), // left
543 u8 brightest_light = 0;
544 v3s16 brightest_pos(0,0,0);
545 bool found_something = false;
547 // Loop through 6 neighbors
548 for(u16 i=0; i<6; i++){
549 // Get the position of the neighbor node
550 v3s16 n2pos = p + dirs[i];
555 catch(InvalidPositionException &e)
559 if(n2.getLight(bank) > brightest_light || found_something == false){
560 brightest_light = n2.getLight(bank);
561 brightest_pos = n2pos;
562 found_something = true;
566 if(found_something == false)
567 throw InvalidPositionException();
569 return brightest_pos;
573 Propagates sunlight down from a node.
574 Starting point gets sunlight.
576 Returns the lowest y value of where the sunlight went.
578 Mud is turned into grass in where the sunlight stops.
580 s16 Map::propagateSunlight(v3s16 start,
581 core::map<v3s16, MapBlock*> & modified_blocks)
586 v3s16 pos(start.X, y, start.Z);
588 v3s16 blockpos = getNodeBlockPos(pos);
591 block = getBlockNoCreate(blockpos);
593 catch(InvalidPositionException &e)
598 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
599 MapNode n = block->getNode(relpos);
601 if(n.sunlight_propagates())
603 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
604 block->setNode(relpos, n);
606 modified_blocks.insert(blockpos, block);
610 // Turn mud into grass
611 if(n.d == CONTENT_MUD)
614 block->setNode(relpos, n);
615 modified_blocks.insert(blockpos, block);
618 // Sunlight goes no further
625 void Map::updateLighting(enum LightBank bank,
626 core::map<v3s16, MapBlock*> & a_blocks,
627 core::map<v3s16, MapBlock*> & modified_blocks)
629 /*m_dout<<DTIME<<"Map::updateLighting(): "
630 <<a_blocks.size()<<" blocks."<<std::endl;*/
632 //TimeTaker timer("updateLighting");
636 //u32 count_was = modified_blocks.size();
638 core::map<v3s16, MapBlock*> blocks_to_update;
640 core::map<v3s16, bool> light_sources;
642 core::map<v3s16, u8> unlight_from;
644 core::map<v3s16, MapBlock*>::Iterator i;
645 i = a_blocks.getIterator();
646 for(; i.atEnd() == false; i++)
648 MapBlock *block = i.getNode()->getValue();
652 // Don't bother with dummy blocks.
656 v3s16 pos = block->getPos();
657 modified_blocks.insert(pos, block);
659 blocks_to_update.insert(pos, block);
662 Clear all light from block
664 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
665 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
666 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
671 MapNode n = block->getNode(v3s16(x,y,z));
672 u8 oldlight = n.getLight(bank);
674 block->setNode(v3s16(x,y,z), n);
676 // Collect borders for unlighting
677 if(x==0 || x == MAP_BLOCKSIZE-1
678 || y==0 || y == MAP_BLOCKSIZE-1
679 || z==0 || z == MAP_BLOCKSIZE-1)
681 v3s16 p_map = p + v3s16(
684 MAP_BLOCKSIZE*pos.Z);
685 unlight_from.insert(p_map, oldlight);
688 catch(InvalidPositionException &e)
691 This would happen when dealing with a
695 dstream<<"updateLighting(): InvalidPositionException"
700 if(bank == LIGHTBANK_DAY)
702 bool bottom_valid = block->propagateSunlight(light_sources);
704 // If bottom is valid, we're done.
708 else if(bank == LIGHTBANK_NIGHT)
710 // For night lighting, sunlight is not propagated
715 // Invalid lighting bank
719 /*dstream<<"Bottom for sunlight-propagated block ("
720 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
723 // Bottom sunlight is not valid; get the block and loop to it
727 block = getBlockNoCreate(pos);
729 catch(InvalidPositionException &e)
739 TimeTaker timer("unspreadLight");
740 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
745 u32 diff = modified_blocks.size() - count_was;
746 count_was = modified_blocks.size();
747 dstream<<"unspreadLight modified "<<diff<<std::endl;
751 TimeTaker timer("spreadLight");
752 spreadLight(bank, light_sources, modified_blocks);
757 u32 diff = modified_blocks.size() - count_was;
758 count_was = modified_blocks.size();
759 dstream<<"spreadLight modified "<<diff<<std::endl;
764 //MapVoxelManipulator vmanip(this);
766 // Make a manual voxel manipulator and load all the blocks
767 // that touch the requested blocks
768 ManualMapVoxelManipulator vmanip(this);
769 core::map<v3s16, MapBlock*>::Iterator i;
770 i = blocks_to_update.getIterator();
771 for(; i.atEnd() == false; i++)
773 MapBlock *block = i.getNode()->getValue();
774 v3s16 p = block->getPos();
776 // Add all surrounding blocks
777 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
780 Add all surrounding blocks that have up-to-date lighting
781 NOTE: This doesn't quite do the job (not everything
782 appropriate is lighted)
784 /*for(s16 z=-1; z<=1; z++)
785 for(s16 y=-1; y<=1; y++)
786 for(s16 x=-1; x<=1; x++)
789 MapBlock *block = getBlockNoCreateNoEx(p);
794 if(block->getLightingExpired())
796 vmanip.initialEmerge(p, p);
799 // Lighting of block will be updated completely
800 block->setLightingExpired(false);
804 //TimeTaker timer("unSpreadLight");
805 vmanip.unspreadLight(bank, unlight_from, light_sources);
808 //TimeTaker timer("spreadLight");
809 vmanip.spreadLight(bank, light_sources);
812 //TimeTaker timer("blitBack");
813 vmanip.blitBack(modified_blocks);
815 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
819 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
822 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
823 core::map<v3s16, MapBlock*> & modified_blocks)
825 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
826 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
829 Update information about whether day and night light differ
831 for(core::map<v3s16, MapBlock*>::Iterator
832 i = modified_blocks.getIterator();
833 i.atEnd() == false; i++)
835 MapBlock *block = i.getNode()->getValue();
836 block->updateDayNightDiff();
841 This is called after changing a node from transparent to opaque.
842 The lighting value of the node should be left as-is after changing
843 other values. This sets the lighting value to 0.
845 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
846 core::map<v3s16, MapBlock*> &modified_blocks)
849 m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
850 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
853 From this node to nodes underneath:
854 If lighting is sunlight (1.0), unlight neighbours and
859 v3s16 toppos = p + v3s16(0,1,0);
860 v3s16 bottompos = p + v3s16(0,-1,0);
862 bool node_under_sunlight = true;
863 core::map<v3s16, bool> light_sources;
866 If there is a node at top and it doesn't have sunlight,
867 there has not been any sunlight going down.
869 Otherwise there probably is.
872 MapNode topnode = getNode(toppos);
874 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
875 node_under_sunlight = false;
877 catch(InvalidPositionException &e)
882 If the new node doesn't propagate sunlight and there is
883 grass below, change it to mud
885 if(content_features(n.d).sunlight_propagates == false)
888 MapNode bottomnode = getNode(bottompos);
890 if(bottomnode.d == CONTENT_GRASS
891 || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
893 bottomnode.d = CONTENT_MUD;
894 setNode(bottompos, bottomnode);
897 catch(InvalidPositionException &e)
903 If the new node is mud and it is under sunlight, change it
906 if(n.d == CONTENT_MUD && node_under_sunlight)
912 Remove all light that has come out of this node
915 enum LightBank banks[] =
920 for(s32 i=0; i<2; i++)
922 enum LightBank bank = banks[i];
924 u8 lightwas = getNode(p).getLight(bank);
926 // Add the block of the added node to modified_blocks
927 v3s16 blockpos = getNodeBlockPos(p);
928 MapBlock * block = getBlockNoCreate(blockpos);
929 assert(block != NULL);
930 modified_blocks.insert(blockpos, block);
932 assert(isValidPosition(p));
934 // Unlight neighbours of node.
935 // This means setting light of all consequent dimmer nodes
937 // This also collects the nodes at the border which will spread
938 // light again into this.
939 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
945 If node lets sunlight through and is under sunlight, it has
948 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
950 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
954 Set the node on the map
963 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
966 NodeMetadata *meta = meta_proto->clone();
967 setNodeMetadata(p, meta);
971 If node is under sunlight and doesn't let sunlight through,
972 take all sunlighted nodes under it and clear light from them
973 and from where the light has been spread.
974 TODO: This could be optimized by mass-unlighting instead
977 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
981 //m_dout<<DTIME<<"y="<<y<<std::endl;
982 v3s16 n2pos(p.X, y, p.Z);
988 catch(InvalidPositionException &e)
993 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
995 unLightNeighbors(LIGHTBANK_DAY,
996 n2pos, n2.getLight(LIGHTBANK_DAY),
997 light_sources, modified_blocks);
998 n2.setLight(LIGHTBANK_DAY, 0);
1006 for(s32 i=0; i<2; i++)
1008 enum LightBank bank = banks[i];
1011 Spread light from all nodes that might be capable of doing so
1013 spreadLight(bank, light_sources, modified_blocks);
1017 Update information about whether day and night light differ
1019 for(core::map<v3s16, MapBlock*>::Iterator
1020 i = modified_blocks.getIterator();
1021 i.atEnd() == false; i++)
1023 MapBlock *block = i.getNode()->getValue();
1024 block->updateDayNightDiff();
1028 Add neighboring liquid nodes and the node itself if it is
1029 liquid (=water node was added) to transform queue.
1032 v3s16(0,0,0), // self
1033 v3s16(0,0,1), // back
1034 v3s16(0,1,0), // top
1035 v3s16(1,0,0), // right
1036 v3s16(0,0,-1), // front
1037 v3s16(0,-1,0), // bottom
1038 v3s16(-1,0,0), // left
1040 for(u16 i=0; i<7; i++)
1045 v3s16 p2 = p + dirs[i];
1047 MapNode n2 = getNode(p2);
1048 if(content_liquid(n2.d))
1050 m_transforming_liquid.push_back(p2);
1053 }catch(InvalidPositionException &e)
1061 void Map::removeNodeAndUpdate(v3s16 p,
1062 core::map<v3s16, MapBlock*> &modified_blocks)
1064 /*PrintInfo(m_dout);
1065 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1066 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1068 bool node_under_sunlight = true;
1070 v3s16 toppos = p + v3s16(0,1,0);
1072 // Node will be replaced with this
1073 u8 replace_material = CONTENT_AIR;
1076 If there is a node at top and it doesn't have sunlight,
1077 there will be no sunlight going down.
1080 MapNode topnode = getNode(toppos);
1082 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1083 node_under_sunlight = false;
1085 catch(InvalidPositionException &e)
1089 core::map<v3s16, bool> light_sources;
1091 enum LightBank banks[] =
1096 for(s32 i=0; i<2; i++)
1098 enum LightBank bank = banks[i];
1101 Unlight neighbors (in case the node is a light source)
1103 unLightNeighbors(bank, p,
1104 getNode(p).getLight(bank),
1105 light_sources, modified_blocks);
1109 Remove node metadata
1112 removeNodeMetadata(p);
1116 This also clears the lighting.
1120 n.d = replace_material;
1123 for(s32 i=0; i<2; i++)
1125 enum LightBank bank = banks[i];
1128 Recalculate lighting
1130 spreadLight(bank, light_sources, modified_blocks);
1133 // Add the block of the removed node to modified_blocks
1134 v3s16 blockpos = getNodeBlockPos(p);
1135 MapBlock * block = getBlockNoCreate(blockpos);
1136 assert(block != NULL);
1137 modified_blocks.insert(blockpos, block);
1140 If the removed node was under sunlight, propagate the
1141 sunlight down from it and then light all neighbors
1142 of the propagated blocks.
1144 if(node_under_sunlight)
1146 s16 ybottom = propagateSunlight(p, modified_blocks);
1147 /*m_dout<<DTIME<<"Node was under sunlight. "
1148 "Propagating sunlight";
1149 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1151 for(; y >= ybottom; y--)
1153 v3s16 p2(p.X, y, p.Z);
1154 /*m_dout<<DTIME<<"lighting neighbors of node ("
1155 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1157 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1162 // Set the lighting of this node to 0
1163 // TODO: Is this needed? Lighting is cleared up there already.
1165 MapNode n = getNode(p);
1166 n.setLight(LIGHTBANK_DAY, 0);
1169 catch(InvalidPositionException &e)
1175 for(s32 i=0; i<2; i++)
1177 enum LightBank bank = banks[i];
1179 // Get the brightest neighbour node and propagate light from it
1180 v3s16 n2p = getBrightestNeighbour(bank, p);
1182 MapNode n2 = getNode(n2p);
1183 lightNeighbors(bank, n2p, modified_blocks);
1185 catch(InvalidPositionException &e)
1191 Update information about whether day and night light differ
1193 for(core::map<v3s16, MapBlock*>::Iterator
1194 i = modified_blocks.getIterator();
1195 i.atEnd() == false; i++)
1197 MapBlock *block = i.getNode()->getValue();
1198 block->updateDayNightDiff();
1202 Add neighboring liquid nodes to transform queue.
1205 v3s16(0,0,1), // back
1206 v3s16(0,1,0), // top
1207 v3s16(1,0,0), // right
1208 v3s16(0,0,-1), // front
1209 v3s16(0,-1,0), // bottom
1210 v3s16(-1,0,0), // left
1212 for(u16 i=0; i<6; i++)
1217 v3s16 p2 = p + dirs[i];
1219 MapNode n2 = getNode(p2);
1220 if(content_liquid(n2.d))
1222 m_transforming_liquid.push_back(p2);
1225 }catch(InvalidPositionException &e)
1231 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1234 event.type = MEET_ADDNODE;
1238 bool succeeded = true;
1240 core::map<v3s16, MapBlock*> modified_blocks;
1241 addNodeAndUpdate(p, n, modified_blocks);
1243 // Copy modified_blocks to event
1244 for(core::map<v3s16, MapBlock*>::Iterator
1245 i = modified_blocks.getIterator();
1246 i.atEnd()==false; i++)
1248 event.modified_blocks.insert(i.getNode()->getKey(), false);
1251 catch(InvalidPositionException &e){
1255 dispatchEvent(&event);
1260 bool Map::removeNodeWithEvent(v3s16 p)
1263 event.type = MEET_REMOVENODE;
1266 bool succeeded = true;
1268 core::map<v3s16, MapBlock*> modified_blocks;
1269 removeNodeAndUpdate(p, modified_blocks);
1271 // Copy modified_blocks to event
1272 for(core::map<v3s16, MapBlock*>::Iterator
1273 i = modified_blocks.getIterator();
1274 i.atEnd()==false; i++)
1276 event.modified_blocks.insert(i.getNode()->getKey(), false);
1279 catch(InvalidPositionException &e){
1283 dispatchEvent(&event);
1288 bool Map::dayNightDiffed(v3s16 blockpos)
1291 v3s16 p = blockpos + v3s16(0,0,0);
1292 MapBlock *b = getBlockNoCreate(p);
1293 if(b->dayNightDiffed())
1296 catch(InvalidPositionException &e){}
1299 v3s16 p = blockpos + v3s16(-1,0,0);
1300 MapBlock *b = getBlockNoCreate(p);
1301 if(b->dayNightDiffed())
1304 catch(InvalidPositionException &e){}
1306 v3s16 p = blockpos + v3s16(0,-1,0);
1307 MapBlock *b = getBlockNoCreate(p);
1308 if(b->dayNightDiffed())
1311 catch(InvalidPositionException &e){}
1313 v3s16 p = blockpos + v3s16(0,0,-1);
1314 MapBlock *b = getBlockNoCreate(p);
1315 if(b->dayNightDiffed())
1318 catch(InvalidPositionException &e){}
1321 v3s16 p = blockpos + v3s16(1,0,0);
1322 MapBlock *b = getBlockNoCreate(p);
1323 if(b->dayNightDiffed())
1326 catch(InvalidPositionException &e){}
1328 v3s16 p = blockpos + v3s16(0,1,0);
1329 MapBlock *b = getBlockNoCreate(p);
1330 if(b->dayNightDiffed())
1333 catch(InvalidPositionException &e){}
1335 v3s16 p = blockpos + v3s16(0,0,1);
1336 MapBlock *b = getBlockNoCreate(p);
1337 if(b->dayNightDiffed())
1340 catch(InvalidPositionException &e){}
1346 Updates usage timers
1348 void Map::timerUpdate(float dtime)
1350 JMutexAutoLock lock(m_sector_mutex);
1352 core::map<v2s16, MapSector*>::Iterator si;
1354 si = m_sectors.getIterator();
1355 for(; si.atEnd() == false; si++)
1357 MapSector *sector = si.getNode()->getValue();
1358 sector->usage_timer += dtime;
1362 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1365 Wait for caches to be removed before continuing.
1367 This disables the existence of caches while locked
1369 //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1371 core::list<v2s16>::Iterator j;
1372 for(j=list.begin(); j!=list.end(); j++)
1374 MapSector *sector = m_sectors[*j];
1377 sector->deleteBlocks();
1382 If sector is in sector cache, remove it from there
1384 if(m_sector_cache == sector)
1386 m_sector_cache = NULL;
1389 Remove from map and delete
1391 m_sectors.remove(*j);
1397 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1398 core::list<v3s16> *deleted_blocks)
1400 JMutexAutoLock lock(m_sector_mutex);
1402 core::list<v2s16> sector_deletion_queue;
1403 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1404 for(; i.atEnd() == false; i++)
1406 MapSector *sector = i.getNode()->getValue();
1408 Delete sector from memory if it hasn't been used in a long time
1410 if(sector->usage_timer > timeout)
1412 sector_deletion_queue.push_back(i.getNode()->getKey());
1414 if(deleted_blocks != NULL)
1416 // Collect positions of blocks of sector
1417 MapSector *sector = i.getNode()->getValue();
1418 core::list<MapBlock*> blocks;
1419 sector->getBlocks(blocks);
1420 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1421 i != blocks.end(); i++)
1423 deleted_blocks->push_back((*i)->getPos());
1428 deleteSectors(sector_deletion_queue, only_blocks);
1429 return sector_deletion_queue.getSize();
1432 void Map::PrintInfo(std::ostream &out)
1437 #define WATER_DROP_BOOST 4
1439 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1441 DSTACK(__FUNCTION_NAME);
1442 //TimeTaker timer("transformLiquids()");
1445 u32 initial_size = m_transforming_liquid.size();
1447 /*if(initial_size != 0)
1448 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1450 while(m_transforming_liquid.size() != 0)
1453 Get a queued transforming liquid node
1455 v3s16 p0 = m_transforming_liquid.pop_front();
1457 MapNode n0 = getNode(p0);
1459 // Don't deal with non-liquids
1460 if(content_liquid(n0.d) == false)
1463 bool is_source = !content_flowing_liquid(n0.d);
1465 u8 liquid_level = 8;
1466 if(is_source == false)
1467 liquid_level = n0.param2 & 0x0f;
1469 // Turn possible source into non-source
1470 u8 nonsource_c = make_liquid_flowing(n0.d);
1473 If not source, check that some node flows into this one
1474 and what is the level of liquid in this one
1476 if(is_source == false)
1478 s8 new_liquid_level_max = -1;
1480 v3s16 dirs_from[5] = {
1481 v3s16(0,1,0), // top
1482 v3s16(0,0,1), // back
1483 v3s16(1,0,0), // right
1484 v3s16(0,0,-1), // front
1485 v3s16(-1,0,0), // left
1487 for(u16 i=0; i<5; i++)
1492 bool from_top = (i==0);
1494 v3s16 p2 = p0 + dirs_from[i];
1495 MapNode n2 = getNode(p2);
1497 if(content_liquid(n2.d))
1499 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1500 // Check that the liquids are the same type
1501 if(n2_nonsource_c != nonsource_c)
1503 dstream<<"WARNING: Not handling: different liquids"
1504 " collide"<<std::endl;
1507 bool n2_is_source = !content_flowing_liquid(n2.d);
1508 s8 n2_liquid_level = 8;
1509 if(n2_is_source == false)
1510 n2_liquid_level = n2.param2 & 0x07;
1512 s8 new_liquid_level = -1;
1515 //new_liquid_level = 7;
1516 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1517 new_liquid_level = 7;
1519 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1521 else if(n2_liquid_level > 0)
1523 new_liquid_level = n2_liquid_level - 1;
1526 if(new_liquid_level > new_liquid_level_max)
1527 new_liquid_level_max = new_liquid_level;
1530 }catch(InvalidPositionException &e)
1536 If liquid level should be something else, update it and
1537 add all the neighboring water nodes to the transform queue.
1539 if(new_liquid_level_max != liquid_level)
1541 if(new_liquid_level_max == -1)
1543 // Remove water alltoghether
1550 n0.param2 = new_liquid_level_max;
1554 // Block has been modified
1556 v3s16 blockpos = getNodeBlockPos(p0);
1557 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1559 modified_blocks.insert(blockpos, block);
1563 Add neighboring non-source liquid nodes to transform queue.
1566 v3s16(0,0,1), // back
1567 v3s16(0,1,0), // top
1568 v3s16(1,0,0), // right
1569 v3s16(0,0,-1), // front
1570 v3s16(0,-1,0), // bottom
1571 v3s16(-1,0,0), // left
1573 for(u16 i=0; i<6; i++)
1578 v3s16 p2 = p0 + dirs[i];
1580 MapNode n2 = getNode(p2);
1581 if(content_flowing_liquid(n2.d))
1583 m_transforming_liquid.push_back(p2);
1586 }catch(InvalidPositionException &e)
1593 // Get a new one from queue if the node has turned into non-water
1594 if(content_liquid(n0.d) == false)
1598 Flow water from this node
1600 v3s16 dirs_to[5] = {
1601 v3s16(0,-1,0), // bottom
1602 v3s16(0,0,1), // back
1603 v3s16(1,0,0), // right
1604 v3s16(0,0,-1), // front
1605 v3s16(-1,0,0), // left
1607 for(u16 i=0; i<5; i++)
1612 bool to_bottom = (i == 0);
1614 // If liquid is at lowest possible height, it's not going
1615 // anywhere except down
1616 if(liquid_level == 0 && to_bottom == false)
1619 u8 liquid_next_level = 0;
1620 // If going to bottom
1623 //liquid_next_level = 7;
1624 if(liquid_level >= 7 - WATER_DROP_BOOST)
1625 liquid_next_level = 7;
1627 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1630 liquid_next_level = liquid_level - 1;
1632 bool n2_changed = false;
1633 bool flowed = false;
1635 v3s16 p2 = p0 + dirs_to[i];
1637 MapNode n2 = getNode(p2);
1638 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1640 if(content_liquid(n2.d))
1642 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1643 // Check that the liquids are the same type
1644 if(n2_nonsource_c != nonsource_c)
1646 dstream<<"WARNING: Not handling: different liquids"
1647 " collide"<<std::endl;
1650 bool n2_is_source = !content_flowing_liquid(n2.d);
1651 u8 n2_liquid_level = 8;
1652 if(n2_is_source == false)
1653 n2_liquid_level = n2.param2 & 0x07;
1662 // Just flow into the source, nothing changes.
1663 // n2_changed is not set because destination didn't change
1668 if(liquid_next_level > liquid_level)
1670 n2.param2 = liquid_next_level;
1678 else if(n2.d == CONTENT_AIR)
1681 n2.param2 = liquid_next_level;
1688 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1692 m_transforming_liquid.push_back(p2);
1694 v3s16 blockpos = getNodeBlockPos(p2);
1695 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1697 modified_blocks.insert(blockpos, block);
1700 // If n2_changed to bottom, don't flow anywhere else
1701 if(to_bottom && flowed && !is_source)
1704 }catch(InvalidPositionException &e)
1710 //if(loopcount >= 100000)
1711 if(loopcount >= initial_size * 1)
1714 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1717 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1719 v3s16 blockpos = getNodeBlockPos(p);
1720 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1721 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1724 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1728 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1732 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1734 v3s16 blockpos = getNodeBlockPos(p);
1735 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1736 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1739 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1743 block->m_node_metadata.set(p_rel, meta);
1746 void Map::removeNodeMetadata(v3s16 p)
1748 v3s16 blockpos = getNodeBlockPos(p);
1749 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1750 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1753 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1757 block->m_node_metadata.remove(p_rel);
1760 void Map::nodeMetadataStep(float dtime,
1761 core::map<v3s16, MapBlock*> &changed_blocks)
1765 Currently there is no way to ensure that all the necessary
1766 blocks are loaded when this is run. (They might get unloaded)
1767 NOTE: ^- Actually, that might not be so. In a quick test it
1768 reloaded a block with a furnace when I walked back to it from
1771 core::map<v2s16, MapSector*>::Iterator si;
1772 si = m_sectors.getIterator();
1773 for(; si.atEnd() == false; si++)
1775 MapSector *sector = si.getNode()->getValue();
1776 core::list< MapBlock * > sectorblocks;
1777 sector->getBlocks(sectorblocks);
1778 core::list< MapBlock * >::Iterator i;
1779 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1781 MapBlock *block = *i;
1782 bool changed = block->m_node_metadata.step(dtime);
1784 changed_blocks[block->getPos()] = block;
1793 ServerMap::ServerMap(std::string savedir):
1799 //m_chunksize = 16; // Too slow
1800 m_chunksize = 8; // Takes a few seconds
1804 m_seed = (((u64)(myrand()%0xffff)<<0)
1805 + ((u64)(myrand()%0xffff)<<16)
1806 + ((u64)(myrand()%0xffff)<<32)
1807 + ((u64)(myrand()%0xffff)<<48));
1810 Experimental and debug stuff
1817 Try to load map; if not found, create a new one.
1820 m_savedir = savedir;
1821 m_map_saving_enabled = false;
1825 // If directory exists, check contents and load if possible
1826 if(fs::PathExists(m_savedir))
1828 // If directory is empty, it is safe to save into it.
1829 if(fs::GetDirListing(m_savedir).size() == 0)
1831 dstream<<DTIME<<"Server: Empty save directory is valid."
1833 m_map_saving_enabled = true;
1838 // Load map metadata (seed, chunksize)
1841 // Load chunk metadata
1844 catch(FileNotGoodException &e){
1845 dstream<<DTIME<<"WARNING: Server: Could not load "
1846 <<"metafile(s). Disabling chunk-based "
1847 <<"generation."<<std::endl;
1851 /*// Load sector (0,0) and throw and exception on fail
1852 if(loadSectorFull(v2s16(0,0)) == false)
1853 throw LoadError("Failed to load sector (0,0)");*/
1855 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1856 "metadata and sector (0,0) from "<<savedir<<
1857 ", assuming valid save directory."
1860 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1861 <<"and chunk metadata from "<<savedir
1862 <<", assuming valid save directory."
1865 m_map_saving_enabled = true;
1866 // Map loaded, not creating new one
1870 // If directory doesn't exist, it is safe to save to it
1872 m_map_saving_enabled = true;
1875 catch(std::exception &e)
1877 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1878 <<", exception: "<<e.what()<<std::endl;
1879 dstream<<"Please remove the map or fix it."<<std::endl;
1880 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1883 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1885 // Create zero sector
1886 emergeSector(v2s16(0,0));
1888 // Initially write whole map
1892 ServerMap::~ServerMap()
1896 if(m_map_saving_enabled)
1899 // Save only changed parts
1901 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1905 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1908 catch(std::exception &e)
1910 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1911 <<", exception: "<<e.what()<<std::endl;
1917 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1918 for(; i.atEnd() == false; i++)
1920 MapChunk *chunk = i.getNode()->getValue();
1926 Some helper functions for the map generator
1929 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1931 v3s16 em = vmanip.m_area.getExtent();
1932 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1933 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1934 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1936 for(y=y_nodes_max; y>=y_nodes_min; y--)
1938 MapNode &n = vmanip.m_data[i];
1939 if(content_walkable(n.d))
1942 vmanip.m_area.add_y(em, i, -1);
1944 if(y >= y_nodes_min)
1950 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1952 v3s16 em = vmanip.m_area.getExtent();
1953 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1954 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1955 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1957 for(y=y_nodes_max; y>=y_nodes_min; y--)
1959 MapNode &n = vmanip.m_data[i];
1960 if(content_walkable(n.d)
1961 && n.d != CONTENT_TREE
1962 && n.d != CONTENT_LEAVES)
1965 vmanip.m_area.add_y(em, i, -1);
1967 if(y >= y_nodes_min)
1973 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1975 MapNode treenode(CONTENT_TREE);
1976 MapNode leavesnode(CONTENT_LEAVES);
1978 s16 trunk_h = myrand_range(3, 6);
1980 for(s16 ii=0; ii<trunk_h; ii++)
1982 if(vmanip.m_area.contains(p1))
1983 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1987 // p1 is now the last piece of the trunk
1990 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1991 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1992 Buffer<u8> leaves_d(leaves_a.getVolume());
1993 for(s32 i=0; i<leaves_a.getVolume(); i++)
1996 // Force leaves at near the end of the trunk
1999 for(s16 z=-d; z<=d; z++)
2000 for(s16 y=-d; y<=d; y++)
2001 for(s16 x=-d; x<=d; x++)
2003 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2007 // Add leaves randomly
2008 for(u32 iii=0; iii<7; iii++)
2013 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2014 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2015 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2018 for(s16 z=0; z<=d; z++)
2019 for(s16 y=0; y<=d; y++)
2020 for(s16 x=0; x<=d; x++)
2022 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2026 // Blit leaves to vmanip
2027 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2028 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2029 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2033 if(vmanip.m_area.contains(p) == false)
2035 u32 vi = vmanip.m_area.index(p);
2036 if(vmanip.m_data[vi].d != CONTENT_AIR)
2038 u32 i = leaves_a.index(x,y,z);
2039 if(leaves_d[i] == 1)
2040 vmanip.m_data[vi] = leavesnode;
2045 Noise functions. Make sure seed is mangled differently in each one.
2048 // Amount of trees per area in nodes
2049 double tree_amount_2d(u64 seed, v2s16 p)
2051 double noise = noise2d_perlin(
2052 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2054 double zeroval = -0.3;
2058 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2061 #define AVERAGE_MUD_AMOUNT 4
2063 double base_rock_level_2d(u64 seed, v2s16 p)
2065 // The base ground level
2066 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2067 + 25. * noise2d_perlin(
2068 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2069 (seed>>32)+654879876, 6, 0.6);
2071 /*// A bit hillier one
2072 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2073 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2074 (seed>>27)+90340, 6, 0.69);
2078 // Higher ground level
2079 double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
2080 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2081 seed+85039, 5, 0.69);
2082 //higher = 30; // For debugging
2084 // Limit higher to at least base
2088 // Steepness factor of cliffs
2089 double b = 1.0 + 1.0 * noise2d_perlin(
2090 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2092 b = rangelim(b, 0.0, 1000.0);
2095 b = rangelim(b, 3.0, 1000.0);
2096 //dstream<<"b="<<b<<std::endl;
2099 // Offset to more low
2100 double a_off = -0.2;
2101 // High/low selector
2102 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2103 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2104 seed-359, 6, 0.7));*/
2105 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2106 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2107 seed-359, 5, 0.60));
2109 a = rangelim(a, 0.0, 1.0);
2111 //dstream<<"a="<<a<<std::endl;
2113 double h = base*(1.0-a) + higher*a;
2121 Adds random objects to block, depending on the content of the block
2123 void addRandomObjects(MapBlock *block)
2125 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2126 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2128 bool last_node_walkable = false;
2129 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2132 MapNode n = block->getNodeNoEx(p);
2133 if(n.d == CONTENT_IGNORE)
2135 if(content_features(n.d).liquid_type != LIQUID_NONE)
2137 if(content_features(n.d).walkable)
2139 last_node_walkable = true;
2142 if(last_node_walkable)
2144 // If block contains light information
2145 if(content_features(n.d).param_type == CPT_LIGHT)
2147 if(n.getLight(LIGHTBANK_DAY) <= 3)
2149 if(myrand() % 300 == 0)
2151 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2153 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
2154 std::string data = obj->getStaticData();
2155 StaticObject s_obj(obj->getType(),
2156 obj->getBasePosition(), data);
2158 block->m_static_objects.insert(0, s_obj);
2159 block->m_static_objects.insert(0, s_obj);
2160 block->m_static_objects.insert(0, s_obj);
2161 block->m_static_objects.insert(0, s_obj);
2162 block->m_static_objects.insert(0, s_obj);
2163 block->m_static_objects.insert(0, s_obj);
2169 last_node_walkable = false;
2172 block->setChangedFlag();
2175 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2178 This is the main map generation method
2181 void makeChunk(ChunkMakeData *data)
2186 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2187 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2188 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2189 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2190 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2191 *(u32)h_blocks*MAP_BLOCKSIZE;
2192 v3s16 bigarea_blocks_min(
2193 data->sectorpos_bigbase.X,
2195 data->sectorpos_bigbase.Y
2197 v3s16 bigarea_blocks_max(
2198 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2200 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2202 s16 lighting_min_d = 0-data->max_spread_amount;
2203 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2204 + data->max_spread_amount-1;
2207 data->vmanip.clearFlag(0xff);
2209 TimeTaker timer_generate("makeChunk() generate");
2211 // Maximum height of the stone surface and obstacles.
2212 // This is used to disable cave generation from going too high.
2213 s16 stone_surface_max_y = 0;
2216 Generate general ground level to full area
2221 //TimeTaker timer1("ground level");
2223 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2224 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2227 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2230 Skip of already generated
2233 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2234 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2238 // Ground height at this point
2239 float surface_y_f = 0.0;
2241 // Use perlin noise for ground height
2242 surface_y_f = base_rock_level_2d(data->seed, p2d);
2244 /*// Experimental stuff
2246 float a = highlands_level_2d(data->seed, p2d);
2251 // Convert to integer
2252 s16 surface_y = (s16)surface_y_f;
2255 if(surface_y > stone_surface_max_y)
2256 stone_surface_max_y = surface_y;
2259 Fill ground with stone
2262 // Use fast index incrementing
2263 v3s16 em = data->vmanip.m_area.getExtent();
2264 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2265 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2267 // Skip if already generated.
2268 // This is done here because there might be a cave at
2269 // any point in ground, which could look like it
2270 // wasn't generated.
2271 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2274 data->vmanip.m_data[i].d = CONTENT_STONE;
2276 data->vmanip.m_area.add_y(em, i, 1);
2284 Randomize some parameters
2287 //s32 stone_obstacle_count = 0;
2288 /*s32 stone_obstacle_count =
2289 rangelim((1.0+noise2d(data->seed+897,
2290 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2292 //s16 stone_obstacle_max_height = 0;
2293 /*s16 stone_obstacle_max_height =
2294 rangelim((1.0+noise2d(data->seed+5902,
2295 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2298 Loop this part, it will make stuff look older and newer nicely
2300 //for(u32 i_age=0; i_age<1; i_age++)
2301 for(u32 i_age=0; i_age<2; i_age++)
2303 /******************************
2304 BEGINNING OF AGING LOOP
2305 ******************************/
2309 //TimeTaker timer1("caves");
2314 u32 caves_count = relative_volume / 400000;
2315 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2316 if(stone_surface_max_y < WATER_LEVEL)
2318 /*u32 caves_count = 0;
2319 u32 bruises_count = 0;*/
2320 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2322 s16 min_tunnel_diameter = 3;
2323 s16 max_tunnel_diameter = 5;
2324 u16 tunnel_routepoints = 20;
2326 v3f main_direction(0,0,0);
2328 bool bruise_surface = (jj > caves_count);
2332 min_tunnel_diameter = 5;
2333 max_tunnel_diameter = myrand_range(10, 20);
2334 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2335 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2337 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2338 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2340 tunnel_routepoints = 5;
2346 // Allowed route area size in nodes
2348 data->sectorpos_base_size*MAP_BLOCKSIZE,
2349 h_blocks*MAP_BLOCKSIZE,
2350 data->sectorpos_base_size*MAP_BLOCKSIZE
2353 // Area starting point in nodes
2355 data->sectorpos_base.X*MAP_BLOCKSIZE,
2356 data->y_blocks_min*MAP_BLOCKSIZE,
2357 data->sectorpos_base.Y*MAP_BLOCKSIZE
2361 //(this should be more than the maximum radius of the tunnel)
2362 //s16 insure = 5; // Didn't work with max_d = 20
2364 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2365 ar += v3s16(1,0,1) * more * 2;
2366 of -= v3s16(1,0,1) * more;
2368 s16 route_y_min = 0;
2369 // Allow half a diameter + 7 over stone surface
2370 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2372 /*// If caves, don't go through surface too often
2373 if(bruise_surface == false)
2374 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2376 // Limit maximum to area
2377 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2381 /*// Minimum is at y=0
2382 route_y_min = -of.Y - 0;*/
2383 // Minimum is at y=max_tunnel_diameter/4
2384 //route_y_min = -of.Y + max_tunnel_diameter/4;
2385 //s16 min = -of.Y + max_tunnel_diameter/4;
2386 s16 min = -of.Y + 0;
2387 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2388 route_y_min = rangelim(route_y_min, 0, route_y_max);
2391 /*dstream<<"route_y_min = "<<route_y_min
2392 <<", route_y_max = "<<route_y_max<<std::endl;*/
2394 s16 route_start_y_min = route_y_min;
2395 s16 route_start_y_max = route_y_max;
2397 // Start every 2nd cave from surface
2398 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2400 if(coming_from_surface)
2402 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2405 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2406 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2408 // Randomize starting position
2410 (float)(myrand()%ar.X)+0.5,
2411 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2412 (float)(myrand()%ar.Z)+0.5
2415 MapNode airnode(CONTENT_AIR);
2418 Generate some tunnel starting from orp
2421 for(u16 j=0; j<tunnel_routepoints; j++)
2423 if(j%7==0 && bruise_surface == false)
2425 main_direction = v3f(
2426 ((float)(myrand()%20)-(float)10)/10,
2427 ((float)(myrand()%20)-(float)10)/30,
2428 ((float)(myrand()%20)-(float)10)/10
2430 main_direction *= (float)myrand_range(1, 3);
2434 s16 min_d = min_tunnel_diameter;
2435 s16 max_d = max_tunnel_diameter;
2436 s16 rs = myrand_range(min_d, max_d);
2441 maxlen = v3s16(rs*7,rs*7,rs*7);
2445 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2450 if(coming_from_surface && j < 3)
2453 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2454 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2455 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2461 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2462 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2463 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2467 vec += main_direction;
2472 else if(rp.X >= ar.X)
2474 if(rp.Y < route_y_min)
2476 else if(rp.Y >= route_y_max)
2477 rp.Y = route_y_max-1;
2480 else if(rp.Z >= ar.Z)
2484 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2486 v3f fp = orp + vec * f;
2487 v3s16 cp(fp.X, fp.Y, fp.Z);
2490 s16 d1 = d0 + rs - 1;
2491 for(s16 z0=d0; z0<=d1; z0++)
2493 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2494 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2495 for(s16 x0=-si; x0<=si-1; x0++)
2497 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2498 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2499 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2500 //s16 si2 = rs - abs(x0);
2501 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2507 /*if(isInArea(p, ar) == false)
2509 // Check only height
2510 if(y < 0 || y >= ar.Y)
2514 //assert(data->vmanip.m_area.contains(p));
2515 if(data->vmanip.m_area.contains(p) == false)
2517 dstream<<"WARNING: "<<__FUNCTION_NAME
2518 <<":"<<__LINE__<<": "
2519 <<"point not in area"
2524 // Just set it to air, it will be changed to
2526 u32 i = data->vmanip.m_area.index(p);
2527 data->vmanip.m_data[i] = airnode;
2529 if(bruise_surface == false)
2532 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2547 //TimeTaker timer1("ore veins");
2552 for(u32 jj=0; jj<relative_volume/1000; jj++)
2554 s16 max_vein_diameter = 3;
2556 // Allowed route area size in nodes
2558 data->sectorpos_base_size*MAP_BLOCKSIZE,
2559 h_blocks*MAP_BLOCKSIZE,
2560 data->sectorpos_base_size*MAP_BLOCKSIZE
2563 // Area starting point in nodes
2565 data->sectorpos_base.X*MAP_BLOCKSIZE,
2566 data->y_blocks_min*MAP_BLOCKSIZE,
2567 data->sectorpos_base.Y*MAP_BLOCKSIZE
2571 //(this should be more than the maximum radius of the tunnel)
2573 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2574 ar += v3s16(1,0,1) * more * 2;
2575 of -= v3s16(1,0,1) * more;
2577 // Randomize starting position
2579 (float)(myrand()%ar.X)+0.5,
2580 (float)(myrand()%ar.Y)+0.5,
2581 (float)(myrand()%ar.Z)+0.5
2584 // Randomize mineral
2587 mineral = MINERAL_COAL;
2589 mineral = MINERAL_IRON;
2592 Generate some vein starting from orp
2595 for(u16 j=0; j<2; j++)
2598 (float)(myrand()%ar.X)+0.5,
2599 (float)(myrand()%ar.Y)+0.5,
2600 (float)(myrand()%ar.Z)+0.5
2602 v3f vec = rp - orp;*/
2604 v3s16 maxlen(5, 5, 5);
2606 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2607 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2608 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2613 else if(rp.X >= ar.X)
2617 else if(rp.Y >= ar.Y)
2621 else if(rp.Z >= ar.Z)
2627 s16 max_d = max_vein_diameter;
2628 s16 rs = myrand_range(min_d, max_d);
2630 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2632 v3f fp = orp + vec * f;
2633 v3s16 cp(fp.X, fp.Y, fp.Z);
2635 s16 d1 = d0 + rs - 1;
2636 for(s16 z0=d0; z0<=d1; z0++)
2638 s16 si = rs - abs(z0);
2639 for(s16 x0=-si; x0<=si-1; x0++)
2641 s16 si2 = rs - abs(x0);
2642 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2644 // Don't put mineral to every place
2652 /*if(isInArea(p, ar) == false)
2654 // Check only height
2655 if(y < 0 || y >= ar.Y)
2659 assert(data->vmanip.m_area.contains(p));
2661 // Just set it to air, it will be changed to
2663 u32 i = data->vmanip.m_area.index(p);
2664 MapNode *n = &data->vmanip.m_data[i];
2665 if(n->d == CONTENT_STONE)
2680 //TimeTaker timer1("add mud");
2683 Add mud to the central chunk
2686 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2687 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2689 // Node position in 2d
2690 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2692 // Randomize mud amount
2693 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2694 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2695 data->seed+1, 3, 0.55));
2697 // Find ground level
2698 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2701 If topmost node is grass, change it to mud.
2702 It might be if it was flown to there from a neighboring
2703 chunk and then converted.
2706 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2707 MapNode *n = &data->vmanip.m_data[i];
2708 if(n->d == CONTENT_GRASS)
2717 v3s16 em = data->vmanip.m_area.getExtent();
2718 s16 y_start = surface_y+1;
2719 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2720 for(s16 y=y_start; y<=y_nodes_max; y++)
2722 if(mudcount >= mud_add_amount)
2725 MapNode &n = data->vmanip.m_data[i];
2729 data->vmanip.m_area.add_y(em, i, 1);
2738 TimeTaker timer1("flow mud");
2741 Flow mud away from steep edges
2744 // Limit area by 1 because mud is flown into neighbors.
2745 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2746 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2748 // Iterate a few times
2749 for(s16 k=0; k<3; k++)
2752 for(s16 x=mudflow_minpos;
2755 for(s16 z=mudflow_minpos;
2759 // Invert coordinates every 2nd iteration
2762 x = mudflow_maxpos - (x-mudflow_minpos);
2763 z = mudflow_maxpos - (z-mudflow_minpos);
2766 // Node position in 2d
2767 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2769 v3s16 em = data->vmanip.m_area.getExtent();
2770 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2777 for(; y>=y_nodes_min; y--)
2779 n = &data->vmanip.m_data[i];
2780 //if(content_walkable(n->d))
2782 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2785 data->vmanip.m_area.add_y(em, i, -1);
2788 // Stop if out of area
2789 //if(data->vmanip.m_area.contains(i) == false)
2793 /*// If not mud, do nothing to it
2794 MapNode *n = &data->vmanip.m_data[i];
2795 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2799 Don't flow it if the stuff under it is not mud
2803 data->vmanip.m_area.add_y(em, i2, -1);
2804 // Cancel if out of area
2805 if(data->vmanip.m_area.contains(i2) == false)
2807 MapNode *n2 = &data->vmanip.m_data[i2];
2808 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2812 // Make it exactly mud
2815 /*s16 recurse_count = 0;
2819 v3s16(0,0,1), // back
2820 v3s16(1,0,0), // right
2821 v3s16(0,0,-1), // front
2822 v3s16(-1,0,0), // left
2825 // Theck that upper is air or doesn't exist.
2826 // Cancel dropping if upper keeps it in place
2828 data->vmanip.m_area.add_y(em, i3, 1);
2829 if(data->vmanip.m_area.contains(i3) == true
2830 && content_walkable(data->vmanip.m_data[i3].d) == true)
2837 for(u32 di=0; di<4; di++)
2839 v3s16 dirp = dirs4[di];
2842 data->vmanip.m_area.add_p(em, i2, dirp);
2843 // Fail if out of area
2844 if(data->vmanip.m_area.contains(i2) == false)
2846 // Check that side is air
2847 MapNode *n2 = &data->vmanip.m_data[i2];
2848 if(content_walkable(n2->d))
2850 // Check that under side is air
2851 data->vmanip.m_area.add_y(em, i2, -1);
2852 if(data->vmanip.m_area.contains(i2) == false)
2854 n2 = &data->vmanip.m_data[i2];
2855 if(content_walkable(n2->d))
2857 /*// Check that under that is air (need a drop of 2)
2858 data->vmanip.m_area.add_y(em, i2, -1);
2859 if(data->vmanip.m_area.contains(i2) == false)
2861 n2 = &data->vmanip.m_data[i2];
2862 if(content_walkable(n2->d))
2864 // Loop further down until not air
2866 data->vmanip.m_area.add_y(em, i2, -1);
2867 // Fail if out of area
2868 if(data->vmanip.m_area.contains(i2) == false)
2870 n2 = &data->vmanip.m_data[i2];
2871 }while(content_walkable(n2->d) == false);
2872 // Loop one up so that we're in air
2873 data->vmanip.m_area.add_y(em, i2, 1);
2874 n2 = &data->vmanip.m_data[i2];
2876 // Move mud to new place
2878 // Set old place to be air
2879 *n = MapNode(CONTENT_AIR);
2892 //TimeTaker timer1("add water");
2895 Add water to the central chunk (and a bit more)
2898 for(s16 x=0-data->max_spread_amount;
2899 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
2901 for(s16 z=0-data->max_spread_amount;
2902 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
2905 // Node position in 2d
2906 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2908 // Find ground level
2909 //s16 surface_y = find_ground_level(data->vmanip, p2d);
2912 If ground level is over water level, skip.
2913 NOTE: This leaves caves near water without water,
2914 which looks especially crappy when the nearby water
2915 won't start flowing either for some reason
2917 /*if(surface_y > WATER_LEVEL)
2924 v3s16 em = data->vmanip.m_area.getExtent();
2925 u8 light = LIGHT_MAX;
2926 // Start at global water surface level
2927 s16 y_start = WATER_LEVEL;
2928 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2929 MapNode *n = &data->vmanip.m_data[i];
2931 for(s16 y=y_start; y>=y_nodes_min; y--)
2933 n = &data->vmanip.m_data[i];
2935 // Stop when there is no water and no air
2936 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2937 && n->d != CONTENT_WATER)
2943 // Make water only not in caves
2944 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
2946 n->d = CONTENT_WATERSOURCE;
2947 //n->setLight(LIGHTBANK_DAY, light);
2949 // Add to transforming liquid queue (in case it'd
2951 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2952 data->transforming_liquid.push_back(p);
2956 data->vmanip.m_area.add_y(em, i, -1);
2967 /***********************
2969 ************************/
2972 //TimeTaker timer1("convert mud to sand");
2978 //s16 mud_add_amount = myrand_range(2, 4);
2979 //s16 mud_add_amount = 0;
2981 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2982 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
2983 for(s16 x=0-data->max_spread_amount+1;
2984 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
2986 for(s16 z=0-data->max_spread_amount+1;
2987 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
2990 // Node position in 2d
2991 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2993 // Determine whether to have sand here
2994 double sandnoise = noise2d_perlin(
2995 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
2996 data->seed+59420, 3, 0.50);
2998 bool have_sand = (sandnoise > -0.15);
3000 if(have_sand == false)
3003 // Find ground level
3004 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3006 if(surface_y > WATER_LEVEL + 2)
3010 v3s16 em = data->vmanip.m_area.getExtent();
3011 s16 y_start = surface_y;
3012 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3013 u32 not_sand_counter = 0;
3014 for(s16 y=y_start; y>=y_nodes_min; y--)
3016 MapNode *n = &data->vmanip.m_data[i];
3017 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3019 n->d = CONTENT_SAND;
3024 if(not_sand_counter > 3)
3028 data->vmanip.m_area.add_y(em, i, -1);
3037 //TimeTaker timer1("generate trees");
3043 // Divide area into parts
3045 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3046 double area = sidelen * sidelen;
3047 for(s16 x0=0; x0<div; x0++)
3048 for(s16 z0=0; z0<div; z0++)
3050 // Center position of part of division
3052 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3053 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3055 // Minimum edge of part of division
3057 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3058 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3060 // Maximum edge of part of division
3062 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3063 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3066 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3067 // Put trees in random places on part of division
3068 for(u32 i=0; i<tree_count; i++)
3070 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3071 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3072 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3073 // Don't make a tree under water level
3076 // Don't make a tree so high that it doesn't fit
3077 if(y > y_nodes_max - 6)
3081 Trees grow only on mud and grass
3084 u32 i = data->vmanip.m_area.index(v3s16(p));
3085 MapNode *n = &data->vmanip.m_data[i];
3086 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3091 make_tree(data->vmanip, p);
3094 /*u32 tree_max = relative_area / 60;
3095 //u32 count = myrand_range(0, tree_max);
3096 for(u32 i=0; i<count; i++)
3098 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3099 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3100 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3101 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3102 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3103 // Don't make a tree under water level
3108 make_tree(data->vmanip, p);
3116 //TimeTaker timer1("grow grass");
3122 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3123 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3124 for(s16 x=0-data->max_spread_amount;
3125 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3127 for(s16 z=0-data->max_spread_amount;
3128 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3131 // Node position in 2d
3132 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3135 Find the lowest surface to which enough light ends up
3138 Basically just wait until not air and not leaves.
3142 v3s16 em = data->vmanip.m_area.getExtent();
3143 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3145 // Go to ground level
3146 for(y=y_nodes_max; y>=y_nodes_min; y--)
3148 MapNode &n = data->vmanip.m_data[i];
3149 if(n.d != CONTENT_AIR
3150 && n.d != CONTENT_LEAVES)
3152 data->vmanip.m_area.add_y(em, i, -1);
3154 if(y >= y_nodes_min)
3157 surface_y = y_nodes_min;
3160 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3161 MapNode *n = &data->vmanip.m_data[i];
3162 if(n->d == CONTENT_MUD)
3163 n->d = CONTENT_GRASS;
3169 Initial lighting (sunlight)
3172 core::map<v3s16, bool> light_sources;
3175 // 750ms @cs=8, can't optimize more
3176 TimeTaker timer1("initial lighting");
3180 Go through the edges and add all nodes that have light to light_sources
3184 for(s16 i=0; i<4; i++)
3186 for(s16 j=lighting_min_d;
3193 if(i == 0 || i == 1)
3195 x = (i==0) ? lighting_min_d : lighting_max_d;
3204 z = (i==0) ? lighting_min_d : lighting_max_d;
3211 // Node position in 2d
3212 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3215 v3s16 em = data->vmanip.m_area.getExtent();
3216 s16 y_start = y_nodes_max;
3217 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3218 for(s16 y=y_start; y>=y_nodes_min; y--)
3220 MapNode *n = &data->vmanip.m_data[i];
3221 if(n->getLight(LIGHTBANK_DAY) != 0)
3223 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3225 //NOTE: This is broken, at least the index has to
3234 Go through the edges and apply sunlight to them, not caring
3239 for(s16 i=0; i<4; i++)
3241 for(s16 j=lighting_min_d;
3248 if(i == 0 || i == 1)
3250 x = (i==0) ? lighting_min_d : lighting_max_d;
3259 z = (i==0) ? lighting_min_d : lighting_max_d;
3266 // Node position in 2d
3267 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3269 // Loop from top to down
3271 u8 light = LIGHT_SUN;
3272 v3s16 em = data->vmanip.m_area.getExtent();
3273 s16 y_start = y_nodes_max;
3274 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3275 for(s16 y=y_start; y>=y_nodes_min; y--)
3277 MapNode *n = &data->vmanip.m_data[i];
3278 if(light_propagates_content(n->d) == false)
3282 else if(light != LIGHT_SUN
3283 || sunlight_propagates_content(n->d) == false)
3289 n->setLight(LIGHTBANK_DAY, light);
3290 n->setLight(LIGHTBANK_NIGHT, 0);
3294 // Insert light source
3295 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3298 // Increment index by y
3299 data->vmanip.m_area.add_y(em, i, -1);
3305 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3306 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3307 /*for(s16 x=0-data->max_spread_amount+1;
3308 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3310 for(s16 z=0-data->max_spread_amount+1;
3311 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3315 This has to be 1 smaller than the actual area, because
3316 neighboring nodes are checked.
3318 for(s16 x=lighting_min_d+1;
3319 x<=lighting_max_d-1;
3321 for(s16 z=lighting_min_d+1;
3322 z<=lighting_max_d-1;
3325 // Node position in 2d
3326 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3329 Apply initial sunlight
3332 u8 light = LIGHT_SUN;
3333 bool add_to_sources = false;
3334 v3s16 em = data->vmanip.m_area.getExtent();
3335 s16 y_start = y_nodes_max;
3336 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3337 for(s16 y=y_start; y>=y_nodes_min; y--)
3339 MapNode *n = &data->vmanip.m_data[i];
3341 if(light_propagates_content(n->d) == false)
3345 else if(light != LIGHT_SUN
3346 || sunlight_propagates_content(n->d) == false)
3352 // This doesn't take much time
3353 if(add_to_sources == false)
3356 Check sides. If side is not air or water, start
3357 adding to light_sources.
3360 v3s16(0,0,1), // back
3361 v3s16(1,0,0), // right
3362 v3s16(0,0,-1), // front
3363 v3s16(-1,0,0), // left
3365 for(u32 di=0; di<4; di++)
3367 v3s16 dirp = dirs4[di];
3369 data->vmanip.m_area.add_p(em, i2, dirp);
3370 MapNode *n2 = &data->vmanip.m_data[i2];
3372 n2->d != CONTENT_AIR
3373 && n2->d != CONTENT_WATERSOURCE
3374 && n2->d != CONTENT_WATER
3376 add_to_sources = true;
3382 n->setLight(LIGHTBANK_DAY, light);
3383 n->setLight(LIGHTBANK_NIGHT, 0);
3385 // This doesn't take much time
3386 if(light != 0 && add_to_sources)
3388 // Insert light source
3389 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3392 // Increment index by y
3393 data->vmanip.m_area.add_y(em, i, -1);
3401 // Spread light around
3403 TimeTaker timer("makeChunk() spreadLight");
3404 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3411 timer_generate.stop();
3414 //###################################################################
3415 //###################################################################
3416 //###################################################################
3417 //###################################################################
3418 //###################################################################
3419 //###################################################################
3420 //###################################################################
3421 //###################################################################
3422 //###################################################################
3423 //###################################################################
3424 //###################################################################
3425 //###################################################################
3426 //###################################################################
3427 //###################################################################
3428 //###################################################################
3430 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3432 if(m_chunksize == 0)
3440 // The distance how far into the neighbors the generator is allowed to go.
3441 s16 max_spread_amount_sectors = 2;
3442 assert(max_spread_amount_sectors <= m_chunksize);
3443 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3445 s16 y_blocks_min = -4;
3446 s16 y_blocks_max = 3;
3448 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3449 s16 sectorpos_base_size = m_chunksize;
3451 v2s16 sectorpos_bigbase =
3452 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3453 s16 sectorpos_bigbase_size =
3454 sectorpos_base_size + 2 * max_spread_amount_sectors;
3457 data.chunkpos = chunkpos;
3458 data.y_blocks_min = y_blocks_min;
3459 data.y_blocks_max = y_blocks_max;
3460 data.sectorpos_base = sectorpos_base;
3461 data.sectorpos_base_size = sectorpos_base_size;
3462 data.sectorpos_bigbase = sectorpos_bigbase;
3463 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3464 data.max_spread_amount = max_spread_amount;
3467 Create the whole area of this and the neighboring chunks
3470 TimeTaker timer("initChunkMake() create area");
3472 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3473 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3475 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3476 ServerMapSector *sector = createSector(sectorpos);
3479 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3481 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3482 MapBlock *block = createBlock(blockpos);
3484 // Lighting won't be calculated
3485 //block->setLightingExpired(true);
3486 // Lighting will be calculated
3487 block->setLightingExpired(false);
3490 Block gets sunlight if this is true.
3492 This should be set to true when the top side of a block
3493 is completely exposed to the sky.
3495 Actually this doesn't matter now because the
3496 initial lighting is done here.
3498 block->setIsUnderground(y != y_blocks_max);
3504 Now we have a big empty area.
3506 Make a ManualMapVoxelManipulator that contains this and the
3510 v3s16 bigarea_blocks_min(
3511 sectorpos_bigbase.X,
3515 v3s16 bigarea_blocks_max(
3516 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3518 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3521 data.vmanip.setMap(this);
3524 TimeTaker timer("initChunkMake() initialEmerge");
3525 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3530 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3531 core::map<v3s16, MapBlock*> &changed_blocks)
3537 Blit generated stuff to map
3541 //TimeTaker timer("generateChunkRaw() blitBackAll");
3542 data.vmanip.blitBackAll(&changed_blocks);
3546 Update day/night difference cache of the MapBlocks
3549 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3550 i.atEnd() == false; i++)
3552 MapBlock *block = i.getNode()->getValue();
3553 block->updateDayNightDiff();
3558 Copy transforming liquid information
3560 while(data.transforming_liquid.size() > 0)
3562 v3s16 p = data.transforming_liquid.pop_front();
3563 m_transforming_liquid.push_back(p);
3567 Add random objects to blocks
3570 for(s16 x=0; x<data.sectorpos_base_size; x++)
3571 for(s16 z=0; z<data.sectorpos_base_size; z++)
3573 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3574 ServerMapSector *sector = createSector(sectorpos);
3577 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3579 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3580 MapBlock *block = createBlock(blockpos);
3581 addRandomObjects(block);
3587 Create chunk metadata
3590 for(s16 x=-1; x<=1; x++)
3591 for(s16 y=-1; y<=1; y++)
3593 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3594 // Add chunk meta information
3595 MapChunk *chunk = getChunk(chunkpos0);
3598 chunk = new MapChunk();
3599 m_chunks.insert(chunkpos0, chunk);
3601 //chunk->setIsVolatile(true);
3602 if(chunk->getGenLevel() > GENERATED_PARTLY)
3603 chunk->setGenLevel(GENERATED_PARTLY);
3607 Set central chunk non-volatile
3609 MapChunk *chunk = getChunk(data.chunkpos);
3612 //chunk->setIsVolatile(false);
3613 chunk->setGenLevel(GENERATED_FULLY);
3616 Save changed parts of map
3625 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3626 core::map<v3s16, MapBlock*> &changed_blocks,
3629 DSTACK(__FUNCTION_NAME);
3632 Don't generate if already fully generated
3636 MapChunk *chunk = getChunk(chunkpos);
3637 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3639 dstream<<"generateChunkRaw(): Chunk "
3640 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3641 <<" already generated"<<std::endl;
3646 dstream<<"generateChunkRaw(): Generating chunk "
3647 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3650 TimeTaker timer("generateChunkRaw()");
3654 // Initialize generation
3655 initChunkMake(data, chunkpos);
3660 // Finalize generation
3661 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3664 Return central chunk (which was requested)
3670 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3671 core::map<v3s16, MapBlock*> &changed_blocks)
3673 dstream<<"generateChunk(): Generating chunk "
3674 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3677 /*for(s16 x=-1; x<=1; x++)
3678 for(s16 y=-1; y<=1; y++)*/
3679 for(s16 x=-0; x<=0; x++)
3680 for(s16 y=-0; y<=0; y++)
3682 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3683 MapChunk *chunk = getChunk(chunkpos0);
3684 // Skip if already generated
3685 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3687 generateChunkRaw(chunkpos0, changed_blocks);
3690 assert(chunkNonVolatile(chunkpos1));
3692 MapChunk *chunk = getChunk(chunkpos1);
3697 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3699 DSTACK("%s: p2d=(%d,%d)",
3704 Check if it exists already in memory
3706 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3711 Try to load it from disk (with blocks)
3713 if(loadSectorFull(p2d) == true)
3715 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3718 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3719 throw InvalidPositionException("");
3725 Do not create over-limit
3727 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3728 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3729 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3730 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3731 throw InvalidPositionException("createSector(): pos. over limit");
3734 Generate blank sector
3737 sector = new ServerMapSector(this, p2d);
3739 // Sector position on map in nodes
3740 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3745 m_sectors.insert(p2d, sector);
3751 MapSector * ServerMap::emergeSector(v2s16 p2d,
3752 core::map<v3s16, MapBlock*> &changed_blocks)
3754 DSTACK("%s: p2d=(%d,%d)",
3761 v2s16 chunkpos = sector_to_chunk(p2d);
3762 /*bool chunk_nonvolatile = false;
3763 MapChunk *chunk = getChunk(chunkpos);
3764 if(chunk && chunk->getIsVolatile() == false)
3765 chunk_nonvolatile = true;*/
3766 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3769 If chunk is not fully generated, generate chunk
3771 if(chunk_nonvolatile == false)
3773 // Generate chunk and neighbors
3774 generateChunk(chunkpos, changed_blocks);
3778 Return sector if it exists now
3780 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3785 Try to load it from disk
3787 if(loadSectorFull(p2d) == true)
3789 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3792 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3793 throw InvalidPositionException("");
3799 generateChunk should have generated the sector
3803 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3804 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3808 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3810 return createSector(p2d);
3815 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3818 generateChunkRaw(chunkpos, changed_blocks, true);
3821 Return sector if it exists now
3823 sector = getSectorNoGenerateNoEx(p2d);
3827 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3835 //return generateSector();
3840 NOTE: This is not used for main map generation, only for blocks
3841 that are very high or low
3843 MapBlock * ServerMap::generateBlock(
3845 MapBlock *original_dummy,
3846 ServerMapSector *sector,
3847 core::map<v3s16, MapBlock*> &changed_blocks,
3848 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3851 DSTACK("%s: p=(%d,%d,%d)",
3855 // If chunks are disabled
3856 /*if(m_chunksize == 0)
3858 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
3859 <<"not generating."<<std::endl;
3863 /*dstream<<"generateBlock(): "
3864 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3867 MapBlock *block = original_dummy;
3869 v2s16 p2d(p.X, p.Z);
3871 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3874 Do not generate over-limit
3876 if(blockpos_over_limit(p))
3878 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3879 throw InvalidPositionException("generateBlock(): pos. over limit");
3883 If block doesn't exist, create one.
3884 If it exists, it is a dummy. In that case unDummify() it.
3886 NOTE: This already sets the map as the parent of the block
3890 block = sector->createBlankBlockNoInsert(block_y);
3894 // Remove the block so that nobody can get a half-generated one.
3895 sector->removeBlock(block);
3896 // Allocate the block to contain the generated data
3900 u8 water_material = CONTENT_WATERSOURCE;
3902 s32 lowest_ground_y = 32767;
3903 s32 highest_ground_y = -32768;
3905 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3906 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3908 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3910 //s16 surface_y = 0;
3912 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
3913 + AVERAGE_MUD_AMOUNT;
3914 // If chunks are disabled
3915 if(m_chunksize == 0)
3916 surface_y = WATER_LEVEL + 1;
3918 if(surface_y < lowest_ground_y)
3919 lowest_ground_y = surface_y;
3920 if(surface_y > highest_ground_y)
3921 highest_ground_y = surface_y;
3923 s32 surface_depth = AVERAGE_MUD_AMOUNT;
3925 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3927 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3932 NOTE: If there are some man-made structures above the
3933 newly created block, they won't be taken into account.
3935 if(real_y > surface_y)
3936 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3942 // If node is over heightmap y, it's air or water
3943 if(real_y > surface_y)
3945 // If under water level, it's water
3946 if(real_y < WATER_LEVEL)
3948 n.d = water_material;
3949 n.setLight(LIGHTBANK_DAY,
3950 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3952 Add to transforming liquid queue (in case it'd
3955 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3956 m_transforming_liquid.push_back(real_pos);
3962 // Else it's ground or caves (air)
3965 // If it's surface_depth under ground, it's stone
3966 if(real_y <= surface_y - surface_depth)
3968 n.d = CONTENT_STONE;
3972 // It is mud if it is under the first ground
3973 // level or under water
3974 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3980 n.d = CONTENT_GRASS;
3983 //n.d = CONTENT_MUD;
3985 /*// If under water level, it's mud
3986 if(real_y < WATER_LEVEL)
3988 // Only the topmost node is grass
3989 else if(real_y <= surface_y - 1)
3992 n.d = CONTENT_GRASS;*/
3996 block->setNode(v3s16(x0,y0,z0), n);
4001 Calculate some helper variables
4004 // Completely underground if the highest part of block is under lowest
4006 // This has to be very sure; it's probably one too strict now but
4007 // that's just better.
4008 bool completely_underground =
4009 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4011 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4013 bool mostly_underwater_surface = false;
4014 if(highest_ground_y < WATER_LEVEL
4015 && some_part_underground && !completely_underground)
4016 mostly_underwater_surface = true;
4019 Get local attributes
4022 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4024 float caves_amount = 0.5;
4029 NOTE: BEWARE: Too big amount of attribute points slows verything
4031 1 interpolation from 5000 points takes 2-3ms.
4033 //TimeTaker timer("generateBlock() local attribute retrieval");
4034 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4035 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4036 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4040 //dstream<<"generateBlock(): Done"<<std::endl;
4046 // Initialize temporary table
4047 const s32 ued = MAP_BLOCKSIZE;
4048 bool underground_emptiness[ued*ued*ued];
4049 for(s32 i=0; i<ued*ued*ued; i++)
4051 underground_emptiness[i] = 0;
4058 Initialize orp and ors. Try to find if some neighboring
4059 MapBlock has a tunnel ended in its side
4063 (float)(myrand()%ued)+0.5,
4064 (float)(myrand()%ued)+0.5,
4065 (float)(myrand()%ued)+0.5
4068 bool found_existing = false;
4074 for(s16 y=0; y<ued; y++)
4075 for(s16 x=0; x<ued; x++)
4077 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4078 if(getNode(ap).d == CONTENT_AIR)
4080 orp = v3f(x+1,y+1,0);
4081 found_existing = true;
4082 goto continue_generating;
4086 catch(InvalidPositionException &e){}
4092 for(s16 y=0; y<ued; y++)
4093 for(s16 x=0; x<ued; x++)
4095 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4096 if(getNode(ap).d == CONTENT_AIR)
4098 orp = v3f(x+1,y+1,ued-1);
4099 found_existing = true;
4100 goto continue_generating;
4104 catch(InvalidPositionException &e){}
4110 for(s16 y=0; y<ued; y++)
4111 for(s16 z=0; z<ued; z++)
4113 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4114 if(getNode(ap).d == CONTENT_AIR)
4116 orp = v3f(0,y+1,z+1);
4117 found_existing = true;
4118 goto continue_generating;
4122 catch(InvalidPositionException &e){}
4128 for(s16 y=0; y<ued; y++)
4129 for(s16 z=0; z<ued; z++)
4131 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4132 if(getNode(ap).d == CONTENT_AIR)
4134 orp = v3f(ued-1,y+1,z+1);
4135 found_existing = true;
4136 goto continue_generating;
4140 catch(InvalidPositionException &e){}
4146 for(s16 x=0; x<ued; x++)
4147 for(s16 z=0; z<ued; z++)
4149 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4150 if(getNode(ap).d == CONTENT_AIR)
4152 orp = v3f(x+1,0,z+1);
4153 found_existing = true;
4154 goto continue_generating;
4158 catch(InvalidPositionException &e){}
4164 for(s16 x=0; x<ued; x++)
4165 for(s16 z=0; z<ued; z++)
4167 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4168 if(getNode(ap).d == CONTENT_AIR)
4170 orp = v3f(x+1,ued-1,z+1);
4171 found_existing = true;
4172 goto continue_generating;
4176 catch(InvalidPositionException &e){}
4178 continue_generating:
4181 Choose whether to actually generate cave
4183 bool do_generate_caves = true;
4184 // Don't generate if no part is underground
4185 if(!some_part_underground)
4187 do_generate_caves = false;
4189 // Don't generate if mostly underwater surface
4190 /*else if(mostly_underwater_surface)
4192 do_generate_caves = false;
4194 // Partly underground = cave
4195 else if(!completely_underground)
4197 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4199 // Found existing cave underground
4200 else if(found_existing && completely_underground)
4202 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4204 // Underground and no caves found
4207 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4210 if(do_generate_caves)
4213 Generate some tunnel starting from orp and ors
4215 for(u16 i=0; i<3; i++)
4218 (float)(myrand()%ued)+0.5,
4219 (float)(myrand()%ued)+0.5,
4220 (float)(myrand()%ued)+0.5
4224 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4228 for(float f=0; f<1.0; f+=0.04)
4230 v3f fp = orp + vec * f;
4231 v3s16 cp(fp.X, fp.Y, fp.Z);
4233 s16 d1 = d0 + rs - 1;
4234 for(s16 z0=d0; z0<=d1; z0++)
4236 s16 si = rs - abs(z0);
4237 for(s16 x0=-si; x0<=si-1; x0++)
4239 s16 si2 = rs - abs(x0);
4240 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4246 if(isInArea(p, ued) == false)
4248 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4260 // Set to true if has caves.
4261 // Set when some non-air is changed to air when making caves.
4262 bool has_caves = false;
4265 Apply temporary cave data to block
4268 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4269 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4271 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4273 MapNode n = block->getNode(v3s16(x0,y0,z0));
4276 if(underground_emptiness[
4277 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4278 +ued*(y0*ued/MAP_BLOCKSIZE)
4279 +(x0*ued/MAP_BLOCKSIZE)])
4281 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4290 block->setNode(v3s16(x0,y0,z0), n);
4295 This is used for guessing whether or not the block should
4296 receive sunlight from the top if the block above doesn't exist
4298 block->setIsUnderground(completely_underground);
4301 Force lighting update if some part of block is partly
4302 underground and has caves.
4304 /*if(some_part_underground && !completely_underground && has_caves)
4306 //dstream<<"Half-ground caves"<<std::endl;
4307 lighting_invalidated_blocks[block->getPos()] = block;
4310 // DEBUG: Always update lighting
4311 //lighting_invalidated_blocks[block->getPos()] = block;
4317 if(some_part_underground)
4319 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4324 for(s16 i=0; i<underground_level/4 + 1; i++)
4326 if(myrand()%50 == 0)
4329 (myrand()%(MAP_BLOCKSIZE-2))+1,
4330 (myrand()%(MAP_BLOCKSIZE-2))+1,
4331 (myrand()%(MAP_BLOCKSIZE-2))+1
4337 for(u16 i=0; i<27; i++)
4339 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4341 block->setNode(cp+g_27dirs[i], n);
4349 u16 coal_amount = 30;
4350 u16 coal_rareness = 60 / coal_amount;
4351 if(coal_rareness == 0)
4353 if(myrand()%coal_rareness == 0)
4355 u16 a = myrand() % 16;
4356 u16 amount = coal_amount * a*a*a / 1000;
4357 for(s16 i=0; i<amount; i++)
4360 (myrand()%(MAP_BLOCKSIZE-2))+1,
4361 (myrand()%(MAP_BLOCKSIZE-2))+1,
4362 (myrand()%(MAP_BLOCKSIZE-2))+1
4366 n.d = CONTENT_STONE;
4367 n.param = MINERAL_COAL;
4369 for(u16 i=0; i<27; i++)
4371 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4373 block->setNode(cp+g_27dirs[i], n);
4381 //TODO: change to iron_amount or whatever
4382 u16 iron_amount = 15;
4383 u16 iron_rareness = 60 / iron_amount;
4384 if(iron_rareness == 0)
4386 if(myrand()%iron_rareness == 0)
4388 u16 a = myrand() % 16;
4389 u16 amount = iron_amount * a*a*a / 1000;
4390 for(s16 i=0; i<amount; i++)
4393 (myrand()%(MAP_BLOCKSIZE-2))+1,
4394 (myrand()%(MAP_BLOCKSIZE-2))+1,
4395 (myrand()%(MAP_BLOCKSIZE-2))+1
4399 n.d = CONTENT_STONE;
4400 n.param = MINERAL_IRON;
4402 for(u16 i=0; i<27; i++)
4404 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4406 block->setNode(cp+g_27dirs[i], n);
4413 Create a few rats in empty blocks underground
4415 if(completely_underground)
4417 //for(u16 i=0; i<2; i++)
4420 (myrand()%(MAP_BLOCKSIZE-2))+1,
4421 (myrand()%(MAP_BLOCKSIZE-2))+1,
4422 (myrand()%(MAP_BLOCKSIZE-2))+1
4425 // Check that the place is empty
4426 //if(!is_ground_content(block->getNode(cp).d))
4429 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4430 block->addObject(obj);
4436 Add block to sector.
4438 sector->insertBlock(block);
4440 // Lighting is invalid after generation.
4441 block->setLightingExpired(true);
4448 <<"lighting_invalidated_blocks.size()"
4452 <<" "<<lighting_invalidated_blocks.size()
4454 <<", "<<completely_underground
4455 <<", "<<some_part_underground
4462 MapBlock * ServerMap::createBlock(v3s16 p)
4464 DSTACK("%s: p=(%d,%d,%d)",
4465 __FUNCTION_NAME, p.X, p.Y, p.Z);
4468 Do not create over-limit
4470 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4471 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4472 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4473 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4474 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4475 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4476 throw InvalidPositionException("createBlock(): pos. over limit");
4478 v2s16 p2d(p.X, p.Z);
4481 This will create or load a sector if not found in memory.
4482 If block exists on disk, it will be loaded.
4484 NOTE: On old save formats, this will be slow, as it generates
4485 lighting on blocks for them.
4487 ServerMapSector *sector;
4489 sector = (ServerMapSector*)createSector(p2d);
4490 assert(sector->getId() == MAPSECTOR_SERVER);
4492 catch(InvalidPositionException &e)
4494 dstream<<"createBlock: createSector() failed"<<std::endl;
4498 NOTE: This should not be done, or at least the exception
4499 should not be passed on as std::exception, because it
4500 won't be catched at all.
4502 /*catch(std::exception &e)
4504 dstream<<"createBlock: createSector() failed: "
4505 <<e.what()<<std::endl;
4510 Try to get a block from the sector
4513 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4517 block = sector->createBlankBlock(block_y);
4521 MapBlock * ServerMap::emergeBlock(
4523 bool only_from_disk,
4524 core::map<v3s16, MapBlock*> &changed_blocks,
4525 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4528 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4530 p.X, p.Y, p.Z, only_from_disk);
4533 Do not generate over-limit
4535 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4536 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4537 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4538 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4539 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4540 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4541 throw InvalidPositionException("emergeBlock(): pos. over limit");
4543 v2s16 p2d(p.X, p.Z);
4546 This will create or load a sector if not found in memory.
4547 If block exists on disk, it will be loaded.
4549 ServerMapSector *sector;
4551 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4552 assert(sector->getId() == MAPSECTOR_SERVER);
4554 catch(InvalidPositionException &e)
4556 dstream<<"emergeBlock: emergeSector() failed: "
4557 <<e.what()<<std::endl;
4558 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4560 <<"You could try to delete it."<<std::endl;
4563 catch(VersionMismatchException &e)
4565 dstream<<"emergeBlock: emergeSector() failed: "
4566 <<e.what()<<std::endl;
4567 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4569 <<"You could try to delete it."<<std::endl;
4573 NOTE: This should not be done, or at least the exception
4574 should not be passed on as std::exception, because it
4575 won't be catched at all.
4577 /*catch(std::exception &e)
4579 dstream<<"emergeBlock: emergeSector() failed: "
4580 <<e.what()<<std::endl;
4581 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4583 <<"You could try to delete it."<<std::endl;
4588 Try to get a block from the sector
4591 bool does_not_exist = false;
4592 bool lighting_expired = false;
4593 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4597 does_not_exist = true;
4599 else if(block->isDummy() == true)
4601 does_not_exist = true;
4603 else if(block->getLightingExpired())
4605 lighting_expired = true;
4610 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4615 If block was not found on disk and not going to generate a
4616 new one, make sure there is a dummy block in place.
4618 if(only_from_disk && (does_not_exist || lighting_expired))
4620 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4624 // Create dummy block
4625 block = new MapBlock(this, p, true);
4627 // Add block to sector
4628 sector->insertBlock(block);
4634 //dstream<<"Not found on disk, generating."<<std::endl;
4636 //TimeTaker("emergeBlock() generate");
4638 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4641 If the block doesn't exist, generate the block.
4645 block = generateBlock(p, block, sector, changed_blocks,
4646 lighting_invalidated_blocks);
4649 if(lighting_expired)
4651 lighting_invalidated_blocks.insert(p, block);
4655 Initially update sunlight
4659 core::map<v3s16, bool> light_sources;
4660 bool black_air_left = false;
4661 bool bottom_invalid =
4662 block->propagateSunlight(light_sources, true,
4663 &black_air_left, true);
4665 // If sunlight didn't reach everywhere and part of block is
4666 // above ground, lighting has to be properly updated
4667 //if(black_air_left && some_part_underground)
4670 lighting_invalidated_blocks[block->getPos()] = block;
4675 lighting_invalidated_blocks[block->getPos()] = block;
4682 s16 ServerMap::findGroundLevel(v2s16 p2d)
4685 Uh, just do something random...
4687 // Find existing map from top to down
4690 v3s16 p(p2d.X, max, p2d.Y);
4691 for(; p.Y>min; p.Y--)
4693 MapNode n = getNodeNoEx(p);
4694 if(n.d != CONTENT_IGNORE)
4699 // If this node is not air, go to plan b
4700 if(getNodeNoEx(p).d != CONTENT_AIR)
4702 // Search existing walkable and return it
4703 for(; p.Y>min; p.Y--)
4705 MapNode n = getNodeNoEx(p);
4706 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4712 Plan B: Get from map generator perlin noise function
4714 double level = base_rock_level_2d(m_seed, p2d);
4718 void ServerMap::createDir(std::string path)
4720 if(fs::CreateDir(path) == false)
4722 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4723 <<"\""<<path<<"\""<<std::endl;
4724 throw BaseException("ServerMap failed to create directory");
4728 std::string ServerMap::getSectorSubDir(v2s16 pos)
4731 snprintf(cc, 9, "%.4x%.4x",
4732 (unsigned int)pos.X&0xffff,
4733 (unsigned int)pos.Y&0xffff);
4735 return std::string(cc);
4738 std::string ServerMap::getSectorDir(v2s16 pos)
4740 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4743 v2s16 ServerMap::getSectorPos(std::string dirname)
4745 if(dirname.size() != 8)
4746 throw InvalidFilenameException("Invalid sector directory name");
4748 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4750 throw InvalidFilenameException("Invalid sector directory name");
4751 v2s16 pos((s16)x, (s16)y);
4755 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4757 v2s16 p2d = getSectorPos(sectordir);
4759 if(blockfile.size() != 4){
4760 throw InvalidFilenameException("Invalid block filename");
4763 int r = sscanf(blockfile.c_str(), "%4x", &y);
4765 throw InvalidFilenameException("Invalid block filename");
4766 return v3s16(p2d.X, y, p2d.Y);
4769 void ServerMap::save(bool only_changed)
4771 DSTACK(__FUNCTION_NAME);
4772 if(m_map_saving_enabled == false)
4774 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4778 if(only_changed == false)
4779 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4784 // Disable saving chunk metadata file if chunks are disabled
4785 if(m_chunksize != 0)
4790 u32 sector_meta_count = 0;
4791 u32 block_count = 0;
4794 JMutexAutoLock lock(m_sector_mutex);
4796 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4797 for(; i.atEnd() == false; i++)
4799 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4800 assert(sector->getId() == MAPSECTOR_SERVER);
4802 if(sector->differs_from_disk || only_changed == false)
4804 saveSectorMeta(sector);
4805 sector_meta_count++;
4807 core::list<MapBlock*> blocks;
4808 sector->getBlocks(blocks);
4809 core::list<MapBlock*>::Iterator j;
4810 for(j=blocks.begin(); j!=blocks.end(); j++)
4812 MapBlock *block = *j;
4813 if(block->getChangedFlag() || only_changed == false)
4818 /*dstream<<"ServerMap: Written block ("
4819 <<block->getPos().X<<","
4820 <<block->getPos().Y<<","
4821 <<block->getPos().Z<<")"
4830 Only print if something happened or saved whole map
4832 if(only_changed == false || sector_meta_count != 0
4833 || block_count != 0)
4835 dstream<<DTIME<<"ServerMap: Written: "
4836 <<sector_meta_count<<" sector metadata files, "
4837 <<block_count<<" block files"
4843 // NOTE: Doing this is insane. Deprecated and probably broken.
4844 void ServerMap::loadAll()
4846 DSTACK(__FUNCTION_NAME);
4847 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4852 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4854 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4856 JMutexAutoLock lock(m_sector_mutex);
4859 s32 printed_counter = -100000;
4860 s32 count = list.size();
4862 std::vector<fs::DirListNode>::iterator i;
4863 for(i=list.begin(); i!=list.end(); i++)
4865 if(counter > printed_counter + 10)
4867 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4868 printed_counter = counter;
4872 MapSector *sector = NULL;
4874 // We want directories
4878 sector = loadSectorMeta(i->name);
4880 catch(InvalidFilenameException &e)
4882 // This catches unknown crap in directory
4885 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4886 (m_savedir+"/sectors/"+i->name);
4887 std::vector<fs::DirListNode>::iterator i2;
4888 for(i2=list2.begin(); i2!=list2.end(); i2++)
4894 loadBlock(i->name, i2->name, sector);
4896 catch(InvalidFilenameException &e)
4898 // This catches unknown crap in directory
4902 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4907 void ServerMap::saveMasterHeightmap()
4909 DSTACK(__FUNCTION_NAME);
4911 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4913 createDir(m_savedir);
4915 /*std::string fullpath = m_savedir + "/master_heightmap";
4916 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4917 if(o.good() == false)
4918 throw FileNotGoodException("Cannot open master heightmap");*/
4920 // Format used for writing
4921 //u8 version = SER_FMT_VER_HIGHEST;
4924 void ServerMap::loadMasterHeightmap()
4926 DSTACK(__FUNCTION_NAME);
4928 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4930 /*std::string fullpath = m_savedir + "/master_heightmap";
4931 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4932 if(is.good() == false)
4933 throw FileNotGoodException("Cannot open master heightmap");*/
4937 void ServerMap::saveMapMeta()
4939 DSTACK(__FUNCTION_NAME);
4941 dstream<<"INFO: ServerMap::saveMapMeta(): "
4942 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4945 createDir(m_savedir);
4947 std::string fullpath = m_savedir + "/map_meta.txt";
4948 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4949 if(os.good() == false)
4951 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4952 <<"could not open"<<fullpath<<std::endl;
4953 throw FileNotGoodException("Cannot open chunk metadata");
4957 params.setU64("seed", m_seed);
4958 params.setS32("chunksize", m_chunksize);
4960 params.writeLines(os);
4962 os<<"[end_of_params]\n";
4966 void ServerMap::loadMapMeta()
4968 DSTACK(__FUNCTION_NAME);
4970 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
4973 std::string fullpath = m_savedir + "/map_meta.txt";
4974 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4975 if(is.good() == false)
4977 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4978 <<"could not open"<<fullpath<<std::endl;
4979 throw FileNotGoodException("Cannot open map metadata");
4987 throw SerializationError
4988 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4990 std::getline(is, line);
4991 std::string trimmedline = trim(line);
4992 if(trimmedline == "[end_of_params]")
4994 params.parseConfigLine(line);
4997 m_seed = params.getU64("seed");
4998 m_chunksize = params.getS32("chunksize");
5000 dstream<<"INFO: ServerMap::loadMapMeta(): "
5001 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5005 void ServerMap::saveChunkMeta()
5007 DSTACK(__FUNCTION_NAME);
5009 // This should not be called if chunks are disabled.
5010 assert(m_chunksize != 0);
5012 u32 count = m_chunks.size();
5014 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5015 <<count<<" chunks"<<std::endl;
5017 createDir(m_savedir);
5019 std::string fullpath = m_savedir + "/chunk_meta";
5020 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5021 if(os.good() == false)
5023 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5024 <<"could not open"<<fullpath<<std::endl;
5025 throw FileNotGoodException("Cannot open chunk metadata");
5031 os.write((char*)&version, 1);
5036 writeU32(buf, count);
5037 os.write((char*)buf, 4);
5039 for(core::map<v2s16, MapChunk*>::Iterator
5040 i = m_chunks.getIterator();
5041 i.atEnd()==false; i++)
5043 v2s16 p = i.getNode()->getKey();
5044 MapChunk *chunk = i.getNode()->getValue();
5047 os.write((char*)buf, 4);
5049 chunk->serialize(os, version);
5053 void ServerMap::loadChunkMeta()
5055 DSTACK(__FUNCTION_NAME);
5057 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5060 std::string fullpath = m_savedir + "/chunk_meta";
5061 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5062 if(is.good() == false)
5064 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5065 <<"could not open"<<fullpath<<std::endl;
5066 throw FileNotGoodException("Cannot open chunk metadata");
5072 is.read((char*)&version, 1);
5077 is.read((char*)buf, 4);
5078 u32 count = readU32(buf);
5080 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5081 <<count<<" chunks"<<std::endl;
5083 for(u32 i=0; i<count; i++)
5086 MapChunk *chunk = new MapChunk();
5088 is.read((char*)buf, 4);
5091 chunk->deSerialize(is, version);
5092 m_chunks.insert(p, chunk);
5096 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5098 DSTACK(__FUNCTION_NAME);
5099 // Format used for writing
5100 u8 version = SER_FMT_VER_HIGHEST;
5102 v2s16 pos = sector->getPos();
5103 createDir(m_savedir);
5104 createDir(m_savedir+"/sectors");
5105 std::string dir = getSectorDir(pos);
5108 std::string fullpath = dir + "/meta";
5109 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5110 if(o.good() == false)
5111 throw FileNotGoodException("Cannot open sector metafile");
5113 sector->serialize(o, version);
5115 sector->differs_from_disk = false;
5118 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5120 DSTACK(__FUNCTION_NAME);
5122 v2s16 p2d = getSectorPos(dirname);
5123 std::string dir = m_savedir + "/sectors/" + dirname;
5125 ServerMapSector *sector = NULL;
5127 std::string fullpath = dir + "/meta";
5128 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5129 if(is.good() == false)
5131 // If the directory exists anyway, it probably is in some old
5132 // format. Just go ahead and create the sector.
5133 if(fs::PathExists(dir))
5135 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5136 <<fullpath<<" doesn't exist but directory does."
5137 <<" Continuing with a sector with no metadata."
5139 sector = new ServerMapSector(this, p2d);
5140 m_sectors.insert(p2d, sector);
5143 throw FileNotGoodException("Cannot open sector metafile");
5147 sector = ServerMapSector::deSerialize
5148 (is, this, p2d, m_sectors);
5151 sector->differs_from_disk = false;
5156 bool ServerMap::loadSectorFull(v2s16 p2d)
5158 DSTACK(__FUNCTION_NAME);
5159 std::string sectorsubdir = getSectorSubDir(p2d);
5161 MapSector *sector = NULL;
5163 JMutexAutoLock lock(m_sector_mutex);
5166 sector = loadSectorMeta(sectorsubdir);
5168 catch(InvalidFilenameException &e)
5172 catch(FileNotGoodException &e)
5176 catch(std::exception &e)
5184 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5185 (m_savedir+"/sectors/"+sectorsubdir);
5186 std::vector<fs::DirListNode>::iterator i2;
5187 for(i2=list2.begin(); i2!=list2.end(); i2++)
5193 loadBlock(sectorsubdir, i2->name, sector);
5195 catch(InvalidFilenameException &e)
5197 // This catches unknown crap in directory
5203 void ServerMap::saveBlock(MapBlock *block)
5205 DSTACK(__FUNCTION_NAME);
5207 Dummy blocks are not written
5209 if(block->isDummy())
5211 /*v3s16 p = block->getPos();
5212 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5213 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5217 // Format used for writing
5218 u8 version = SER_FMT_VER_HIGHEST;
5220 v3s16 p3d = block->getPos();
5221 v2s16 p2d(p3d.X, p3d.Z);
5222 createDir(m_savedir);
5223 createDir(m_savedir+"/sectors");
5224 std::string dir = getSectorDir(p2d);
5227 // Block file is map/sectors/xxxxxxxx/xxxx
5229 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5230 std::string fullpath = dir + "/" + cc;
5231 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5232 if(o.good() == false)
5233 throw FileNotGoodException("Cannot open block data");
5236 [0] u8 serialization version
5239 o.write((char*)&version, 1);
5241 block->serialize(o, version);
5244 Versions up from 9 have block objects.
5248 block->serializeObjects(o, version);
5252 Versions up from 15 have static objects.
5256 block->m_static_objects.serialize(o);
5259 // We just wrote it to the disk
5260 block->resetChangedFlag();
5263 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5265 DSTACK(__FUNCTION_NAME);
5267 // Block file is map/sectors/xxxxxxxx/xxxx
5268 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5271 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5272 if(is.good() == false)
5273 throw FileNotGoodException("Cannot open block file");
5275 v3s16 p3d = getBlockPos(sectordir, blockfile);
5276 v2s16 p2d(p3d.X, p3d.Z);
5278 assert(sector->getPos() == p2d);
5280 u8 version = SER_FMT_VER_INVALID;
5281 is.read((char*)&version, 1);
5284 throw SerializationError("ServerMap::loadBlock(): Failed"
5285 " to read MapBlock version");
5287 /*u32 block_size = MapBlock::serializedLength(version);
5288 SharedBuffer<u8> data(block_size);
5289 is.read((char*)*data, block_size);*/
5291 // This will always return a sector because we're the server
5292 //MapSector *sector = emergeSector(p2d);
5294 MapBlock *block = NULL;
5295 bool created_new = false;
5297 block = sector->getBlockNoCreate(p3d.Y);
5299 catch(InvalidPositionException &e)
5301 block = sector->createBlankBlockNoInsert(p3d.Y);
5305 // deserialize block data
5306 block->deSerialize(is, version);
5309 Versions up from 9 have block objects.
5313 block->updateObjects(is, version, NULL, 0);
5317 Versions up from 15 have static objects.
5321 block->m_static_objects.deSerialize(is);
5325 sector->insertBlock(block);
5328 Convert old formats to new and save
5331 // Save old format blocks in new format
5332 if(version < SER_FMT_VER_HIGHEST)
5337 // We just loaded it from the disk, so it's up-to-date.
5338 block->resetChangedFlag();
5341 catch(SerializationError &e)
5343 dstream<<"WARNING: Invalid block data on disk "
5344 "(SerializationError). Ignoring. "
5345 "A new one will be generated."
5348 // TODO: Backup file; name is in fullpath.
5352 void ServerMap::PrintInfo(std::ostream &out)
5363 ClientMap::ClientMap(
5365 MapDrawControl &control,
5366 scene::ISceneNode* parent,
5367 scene::ISceneManager* mgr,
5371 scene::ISceneNode(parent, mgr, id),
5374 m_camera_position(0,0,0),
5375 m_camera_direction(0,0,1)
5377 m_camera_mutex.Init();
5378 assert(m_camera_mutex.IsInitialized());
5380 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5381 BS*1000000,BS*1000000,BS*1000000);
5384 ClientMap::~ClientMap()
5386 /*JMutexAutoLock lock(mesh_mutex);
5395 MapSector * ClientMap::emergeSector(v2s16 p2d)
5397 DSTACK(__FUNCTION_NAME);
5398 // Check that it doesn't exist already
5400 return getSectorNoGenerate(p2d);
5402 catch(InvalidPositionException &e)
5407 ClientMapSector *sector = new ClientMapSector(this, p2d);
5410 JMutexAutoLock lock(m_sector_mutex);
5411 m_sectors.insert(p2d, sector);
5417 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5419 DSTACK(__FUNCTION_NAME);
5420 ClientMapSector *sector = NULL;
5422 JMutexAutoLock lock(m_sector_mutex);
5424 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5428 sector = (ClientMapSector*)n->getValue();
5429 assert(sector->getId() == MAPSECTOR_CLIENT);
5433 sector = new ClientMapSector(this, p2d);
5435 JMutexAutoLock lock(m_sector_mutex);
5436 m_sectors.insert(p2d, sector);
5440 sector->deSerialize(is);
5443 void ClientMap::OnRegisterSceneNode()
5447 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5448 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5451 ISceneNode::OnRegisterSceneNode();
5454 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5456 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5457 DSTACK(__FUNCTION_NAME);
5459 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5462 Get time for measuring timeout.
5464 Measuring time is very useful for long delays when the
5465 machine is swapping a lot.
5467 int time1 = time(0);
5469 //u32 daynight_ratio = m_client->getDayNightRatio();
5471 m_camera_mutex.Lock();
5472 v3f camera_position = m_camera_position;
5473 v3f camera_direction = m_camera_direction;
5474 m_camera_mutex.Unlock();
5477 Get all blocks and draw all visible ones
5480 v3s16 cam_pos_nodes(
5481 camera_position.X / BS,
5482 camera_position.Y / BS,
5483 camera_position.Z / BS);
5485 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5487 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5488 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5490 // Take a fair amount as we will be dropping more out later
5492 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5493 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5494 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5496 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5497 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5498 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5500 u32 vertex_count = 0;
5502 // For limiting number of mesh updates per frame
5503 u32 mesh_update_count = 0;
5505 u32 blocks_would_have_drawn = 0;
5506 u32 blocks_drawn = 0;
5508 //NOTE: The sectors map should be locked but we're not doing it
5509 // because it'd cause too much delays
5511 int timecheck_counter = 0;
5512 core::map<v2s16, MapSector*>::Iterator si;
5513 si = m_sectors.getIterator();
5514 for(; si.atEnd() == false; si++)
5517 timecheck_counter++;
5518 if(timecheck_counter > 50)
5520 timecheck_counter = 0;
5521 int time2 = time(0);
5522 if(time2 > time1 + 4)
5524 dstream<<"ClientMap::renderMap(): "
5525 "Rendering takes ages, returning."
5532 MapSector *sector = si.getNode()->getValue();
5533 v2s16 sp = sector->getPos();
5535 if(m_control.range_all == false)
5537 if(sp.X < p_blocks_min.X
5538 || sp.X > p_blocks_max.X
5539 || sp.Y < p_blocks_min.Z
5540 || sp.Y > p_blocks_max.Z)
5544 core::list< MapBlock * > sectorblocks;
5545 sector->getBlocks(sectorblocks);
5551 core::list< MapBlock * >::Iterator i;
5552 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5554 MapBlock *block = *i;
5557 Compare block position to camera position, skip
5558 if not seen on display
5561 float range = 100000 * BS;
5562 if(m_control.range_all == false)
5563 range = m_control.wanted_range * BS;
5566 if(isBlockInSight(block->getPos(), camera_position,
5567 camera_direction, range, &d) == false)
5572 // This is ugly (spherical distance limit?)
5573 /*if(m_control.range_all == false &&
5574 d - 0.5*BS*MAP_BLOCKSIZE > range)
5579 Update expired mesh (used for day/night change)
5581 It doesn't work exactly like it should now with the
5582 tasked mesh update but whatever.
5585 bool mesh_expired = false;
5588 JMutexAutoLock lock(block->mesh_mutex);
5590 mesh_expired = block->getMeshExpired();
5592 // Mesh has not been expired and there is no mesh:
5593 // block has no content
5594 if(block->mesh == NULL && mesh_expired == false)
5598 f32 faraway = BS*50;
5599 //f32 faraway = m_control.wanted_range * BS;
5602 This has to be done with the mesh_mutex unlocked
5604 // Pretty random but this should work somewhat nicely
5605 if(mesh_expired && (
5606 (mesh_update_count < 3
5607 && (d < faraway || mesh_update_count < 2)
5610 (m_control.range_all && mesh_update_count < 20)
5613 /*if(mesh_expired && mesh_update_count < 6
5614 && (d < faraway || mesh_update_count < 3))*/
5616 mesh_update_count++;
5618 // Mesh has been expired: generate new mesh
5619 //block->updateMesh(daynight_ratio);
5620 m_client->addUpdateMeshTask(block->getPos());
5622 mesh_expired = false;
5627 Draw the faces of the block
5630 JMutexAutoLock lock(block->mesh_mutex);
5632 scene::SMesh *mesh = block->mesh;
5637 blocks_would_have_drawn++;
5638 if(blocks_drawn >= m_control.wanted_max_blocks
5639 && m_control.range_all == false
5640 && d > m_control.wanted_min_range * BS)
5644 u32 c = mesh->getMeshBufferCount();
5646 for(u32 i=0; i<c; i++)
5648 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5649 const video::SMaterial& material = buf->getMaterial();
5650 video::IMaterialRenderer* rnd =
5651 driver->getMaterialRenderer(material.MaterialType);
5652 bool transparent = (rnd && rnd->isTransparent());
5653 // Render transparent on transparent pass and likewise.
5654 if(transparent == is_transparent_pass)
5657 This *shouldn't* hurt too much because Irrlicht
5658 doesn't change opengl textures if the old
5659 material is set again.
5661 driver->setMaterial(buf->getMaterial());
5662 driver->drawMeshBuffer(buf);
5663 vertex_count += buf->getVertexCount();
5667 } // foreach sectorblocks
5670 m_control.blocks_drawn = blocks_drawn;
5671 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5673 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5674 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5677 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5678 core::map<v3s16, MapBlock*> *affected_blocks)
5680 bool changed = false;
5682 Add it to all blocks touching it
5685 v3s16(0,0,0), // this
5686 v3s16(0,0,1), // back
5687 v3s16(0,1,0), // top
5688 v3s16(1,0,0), // right
5689 v3s16(0,0,-1), // front
5690 v3s16(0,-1,0), // bottom
5691 v3s16(-1,0,0), // left
5693 for(u16 i=0; i<7; i++)
5695 v3s16 p2 = p + dirs[i];
5696 // Block position of neighbor (or requested) node
5697 v3s16 blockpos = getNodeBlockPos(p2);
5698 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5699 if(blockref == NULL)
5701 // Relative position of requested node
5702 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5703 if(blockref->setTempMod(relpos, mod))
5708 if(changed && affected_blocks!=NULL)
5710 for(u16 i=0; i<7; i++)
5712 v3s16 p2 = p + dirs[i];
5713 // Block position of neighbor (or requested) node
5714 v3s16 blockpos = getNodeBlockPos(p2);
5715 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5716 if(blockref == NULL)
5718 affected_blocks->insert(blockpos, blockref);
5724 bool ClientMap::clearTempMod(v3s16 p,
5725 core::map<v3s16, MapBlock*> *affected_blocks)
5727 bool changed = false;
5729 v3s16(0,0,0), // this
5730 v3s16(0,0,1), // back
5731 v3s16(0,1,0), // top
5732 v3s16(1,0,0), // right
5733 v3s16(0,0,-1), // front
5734 v3s16(0,-1,0), // bottom
5735 v3s16(-1,0,0), // left
5737 for(u16 i=0; i<7; i++)
5739 v3s16 p2 = p + dirs[i];
5740 // Block position of neighbor (or requested) node
5741 v3s16 blockpos = getNodeBlockPos(p2);
5742 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5743 if(blockref == NULL)
5745 // Relative position of requested node
5746 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5747 if(blockref->clearTempMod(relpos))
5752 if(changed && affected_blocks!=NULL)
5754 for(u16 i=0; i<7; i++)
5756 v3s16 p2 = p + dirs[i];
5757 // Block position of neighbor (or requested) node
5758 v3s16 blockpos = getNodeBlockPos(p2);
5759 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5760 if(blockref == NULL)
5762 affected_blocks->insert(blockpos, blockref);
5768 void ClientMap::expireMeshes(bool only_daynight_diffed)
5770 TimeTaker timer("expireMeshes()");
5772 core::map<v2s16, MapSector*>::Iterator si;
5773 si = m_sectors.getIterator();
5774 for(; si.atEnd() == false; si++)
5776 MapSector *sector = si.getNode()->getValue();
5778 core::list< MapBlock * > sectorblocks;
5779 sector->getBlocks(sectorblocks);
5781 core::list< MapBlock * >::Iterator i;
5782 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5784 MapBlock *block = *i;
5786 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5792 JMutexAutoLock lock(block->mesh_mutex);
5793 if(block->mesh != NULL)
5795 /*block->mesh->drop();
5796 block->mesh = NULL;*/
5797 block->setMeshExpired(true);
5804 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5806 assert(mapType() == MAPTYPE_CLIENT);
5809 v3s16 p = blockpos + v3s16(0,0,0);
5810 MapBlock *b = getBlockNoCreate(p);
5811 b->updateMesh(daynight_ratio);
5812 //b->setMeshExpired(true);
5814 catch(InvalidPositionException &e){}
5817 v3s16 p = blockpos + v3s16(-1,0,0);
5818 MapBlock *b = getBlockNoCreate(p);
5819 b->updateMesh(daynight_ratio);
5820 //b->setMeshExpired(true);
5822 catch(InvalidPositionException &e){}
5824 v3s16 p = blockpos + v3s16(0,-1,0);
5825 MapBlock *b = getBlockNoCreate(p);
5826 b->updateMesh(daynight_ratio);
5827 //b->setMeshExpired(true);
5829 catch(InvalidPositionException &e){}
5831 v3s16 p = blockpos + v3s16(0,0,-1);
5832 MapBlock *b = getBlockNoCreate(p);
5833 b->updateMesh(daynight_ratio);
5834 //b->setMeshExpired(true);
5836 catch(InvalidPositionException &e){}
5841 Update mesh of block in which the node is, and if the node is at the
5842 leading edge, update the appropriate leading blocks too.
5844 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
5852 v3s16 blockposes[4];
5853 for(u32 i=0; i<4; i++)
5855 v3s16 np = nodepos + dirs[i];
5856 blockposes[i] = getNodeBlockPos(np);
5857 // Don't update mesh of block if it has been done already
5858 bool already_updated = false;
5859 for(u32 j=0; j<i; j++)
5861 if(blockposes[j] == blockposes[i])
5863 already_updated = true;
5870 MapBlock *b = getBlockNoCreate(blockposes[i]);
5871 b->updateMesh(daynight_ratio);
5876 void ClientMap::PrintInfo(std::ostream &out)
5887 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5892 MapVoxelManipulator::~MapVoxelManipulator()
5894 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5898 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5900 TimeTaker timer1("emerge", &emerge_time);
5902 // Units of these are MapBlocks
5903 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5904 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5906 VoxelArea block_area_nodes
5907 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5909 addArea(block_area_nodes);
5911 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5912 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5913 for(s32 x=p_min.X; x<=p_max.X; x++)
5916 core::map<v3s16, bool>::Node *n;
5917 n = m_loaded_blocks.find(p);
5921 bool block_data_inexistent = false;
5924 TimeTaker timer1("emerge load", &emerge_load_time);
5926 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5927 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5930 dstream<<std::endl;*/
5932 MapBlock *block = m_map->getBlockNoCreate(p);
5933 if(block->isDummy())
5934 block_data_inexistent = true;
5936 block->copyTo(*this);
5938 catch(InvalidPositionException &e)
5940 block_data_inexistent = true;
5943 if(block_data_inexistent)
5945 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5946 // Fill with VOXELFLAG_INEXISTENT
5947 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5948 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5950 s32 i = m_area.index(a.MinEdge.X,y,z);
5951 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5955 m_loaded_blocks.insert(p, !block_data_inexistent);
5958 //dstream<<"emerge done"<<std::endl;
5962 SUGG: Add an option to only update eg. water and air nodes.
5963 This will make it interfere less with important stuff if
5966 void MapVoxelManipulator::blitBack
5967 (core::map<v3s16, MapBlock*> & modified_blocks)
5969 if(m_area.getExtent() == v3s16(0,0,0))
5972 //TimeTaker timer1("blitBack");
5974 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5975 <<m_loaded_blocks.size()<<std::endl;*/
5978 Initialize block cache
5980 v3s16 blockpos_last;
5981 MapBlock *block = NULL;
5982 bool block_checked_in_modified = false;
5984 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5985 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5986 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5990 u8 f = m_flags[m_area.index(p)];
5991 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5994 MapNode &n = m_data[m_area.index(p)];
5996 v3s16 blockpos = getNodeBlockPos(p);
6001 if(block == NULL || blockpos != blockpos_last){
6002 block = m_map->getBlockNoCreate(blockpos);
6003 blockpos_last = blockpos;
6004 block_checked_in_modified = false;
6007 // Calculate relative position in block
6008 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6010 // Don't continue if nothing has changed here
6011 if(block->getNode(relpos) == n)
6014 //m_map->setNode(m_area.MinEdge + p, n);
6015 block->setNode(relpos, n);
6018 Make sure block is in modified_blocks
6020 if(block_checked_in_modified == false)
6022 modified_blocks[blockpos] = block;
6023 block_checked_in_modified = true;
6026 catch(InvalidPositionException &e)
6032 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6033 MapVoxelManipulator(map),
6034 m_create_area(false)
6038 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6042 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6044 // Just create the area so that it can be pointed to
6045 VoxelManipulator::emerge(a, caller_id);
6048 void ManualMapVoxelManipulator::initialEmerge(
6049 v3s16 blockpos_min, v3s16 blockpos_max)
6051 TimeTaker timer1("initialEmerge", &emerge_time);
6053 // Units of these are MapBlocks
6054 v3s16 p_min = blockpos_min;
6055 v3s16 p_max = blockpos_max;
6057 VoxelArea block_area_nodes
6058 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6060 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6063 dstream<<"initialEmerge: area: ";
6064 block_area_nodes.print(dstream);
6065 dstream<<" ("<<size_MB<<"MB)";
6069 addArea(block_area_nodes);
6071 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6072 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6073 for(s32 x=p_min.X; x<=p_max.X; x++)
6076 core::map<v3s16, bool>::Node *n;
6077 n = m_loaded_blocks.find(p);
6081 bool block_data_inexistent = false;
6084 TimeTaker timer1("emerge load", &emerge_load_time);
6086 MapBlock *block = m_map->getBlockNoCreate(p);
6087 if(block->isDummy())
6088 block_data_inexistent = true;
6090 block->copyTo(*this);
6092 catch(InvalidPositionException &e)
6094 block_data_inexistent = true;
6097 if(block_data_inexistent)
6100 Mark area inexistent
6102 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6103 // Fill with VOXELFLAG_INEXISTENT
6104 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6105 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6107 s32 i = m_area.index(a.MinEdge.X,y,z);
6108 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6112 m_loaded_blocks.insert(p, !block_data_inexistent);
6116 void ManualMapVoxelManipulator::blitBackAll(
6117 core::map<v3s16, MapBlock*> * modified_blocks)
6119 if(m_area.getExtent() == v3s16(0,0,0))
6123 Copy data of all blocks
6125 for(core::map<v3s16, bool>::Iterator
6126 i = m_loaded_blocks.getIterator();
6127 i.atEnd() == false; i++)
6129 bool existed = i.getNode()->getValue();
6130 if(existed == false)
6132 v3s16 p = i.getNode()->getKey();
6133 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6136 dstream<<"WARNING: "<<__FUNCTION_NAME
6137 <<": got NULL block "
6138 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6143 block->copyFrom(*this);
6146 modified_blocks->insert(p, block);