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); // Bulk comment-out
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); // Bulk comment-out
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); // Bulk comment-out
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):
1796 m_map_metadata_changed(true)
1798 dstream<<__FUNCTION_NAME<<std::endl;
1801 //m_chunksize = 16; // Too slow
1802 m_chunksize = 8; // Takes a few seconds
1806 m_seed = (((u64)(myrand()%0xffff)<<0)
1807 + ((u64)(myrand()%0xffff)<<16)
1808 + ((u64)(myrand()%0xffff)<<32)
1809 + ((u64)(myrand()%0xffff)<<48));
1812 Experimental and debug stuff
1819 Try to load map; if not found, create a new one.
1822 m_savedir = savedir;
1823 m_map_saving_enabled = false;
1827 // If directory exists, check contents and load if possible
1828 if(fs::PathExists(m_savedir))
1830 // If directory is empty, it is safe to save into it.
1831 if(fs::GetDirListing(m_savedir).size() == 0)
1833 dstream<<DTIME<<"Server: Empty save directory is valid."
1835 m_map_saving_enabled = true;
1840 // Load map metadata (seed, chunksize)
1843 // Load chunk metadata
1846 catch(FileNotGoodException &e){
1847 dstream<<DTIME<<"WARNING: Server: Could not load "
1848 <<"metafile(s). Disabling chunk-based "
1849 <<"generation."<<std::endl;
1853 /*// Load sector (0,0) and throw and exception on fail
1854 if(loadSectorFull(v2s16(0,0)) == false)
1855 throw LoadError("Failed to load sector (0,0)");*/
1857 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1858 "metadata and sector (0,0) from "<<savedir<<
1859 ", assuming valid save directory."
1862 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1863 <<"and chunk metadata from "<<savedir
1864 <<", assuming valid save directory."
1867 m_map_saving_enabled = true;
1868 // Map loaded, not creating new one
1872 // If directory doesn't exist, it is safe to save to it
1874 m_map_saving_enabled = true;
1877 catch(std::exception &e)
1879 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1880 <<", exception: "<<e.what()<<std::endl;
1881 dstream<<"Please remove the map or fix it."<<std::endl;
1882 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1885 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1887 // Create zero sector
1888 emergeSector(v2s16(0,0));
1890 // Initially write whole map
1894 ServerMap::~ServerMap()
1896 dstream<<__FUNCTION_NAME<<std::endl;
1900 if(m_map_saving_enabled)
1903 // Save only changed parts
1905 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1909 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1912 catch(std::exception &e)
1914 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1915 <<", exception: "<<e.what()<<std::endl;
1921 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1922 for(; i.atEnd() == false; i++)
1924 MapChunk *chunk = i.getNode()->getValue();
1930 Some helper functions for the map generator
1933 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1935 v3s16 em = vmanip.m_area.getExtent();
1936 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1937 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1938 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1940 for(y=y_nodes_max; y>=y_nodes_min; y--)
1942 MapNode &n = vmanip.m_data[i];
1943 if(content_walkable(n.d))
1946 vmanip.m_area.add_y(em, i, -1);
1948 if(y >= y_nodes_min)
1954 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1956 v3s16 em = vmanip.m_area.getExtent();
1957 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1958 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1959 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1961 for(y=y_nodes_max; y>=y_nodes_min; y--)
1963 MapNode &n = vmanip.m_data[i];
1964 if(content_walkable(n.d)
1965 && n.d != CONTENT_TREE
1966 && n.d != CONTENT_LEAVES)
1969 vmanip.m_area.add_y(em, i, -1);
1971 if(y >= y_nodes_min)
1977 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
1979 MapNode treenode(CONTENT_TREE);
1980 MapNode leavesnode(CONTENT_LEAVES);
1982 s16 trunk_h = myrand_range(3, 6);
1984 for(s16 ii=0; ii<trunk_h; ii++)
1986 if(vmanip.m_area.contains(p1))
1987 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
1991 // p1 is now the last piece of the trunk
1994 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
1995 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
1996 Buffer<u8> leaves_d(leaves_a.getVolume());
1997 for(s32 i=0; i<leaves_a.getVolume(); i++)
2000 // Force leaves at near the end of the trunk
2003 for(s16 z=-d; z<=d; z++)
2004 for(s16 y=-d; y<=d; y++)
2005 for(s16 x=-d; x<=d; x++)
2007 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2011 // Add leaves randomly
2012 for(u32 iii=0; iii<7; iii++)
2017 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2018 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2019 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2022 for(s16 z=0; z<=d; z++)
2023 for(s16 y=0; y<=d; y++)
2024 for(s16 x=0; x<=d; x++)
2026 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2030 // Blit leaves to vmanip
2031 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2032 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2033 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2037 if(vmanip.m_area.contains(p) == false)
2039 u32 vi = vmanip.m_area.index(p);
2040 if(vmanip.m_data[vi].d != CONTENT_AIR)
2042 u32 i = leaves_a.index(x,y,z);
2043 if(leaves_d[i] == 1)
2044 vmanip.m_data[vi] = leavesnode;
2049 Noise functions. Make sure seed is mangled differently in each one.
2052 // Amount of trees per area in nodes
2053 double tree_amount_2d(u64 seed, v2s16 p)
2055 double noise = noise2d_perlin(
2056 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2058 double zeroval = -0.3;
2062 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2065 #define AVERAGE_MUD_AMOUNT 4
2067 double base_rock_level_2d(u64 seed, v2s16 p)
2069 // The base ground level
2070 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2071 + 25. * noise2d_perlin(
2072 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2073 (seed>>32)+654879876, 6, 0.6);
2075 /*// A bit hillier one
2076 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2077 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2078 (seed>>27)+90340, 6, 0.69);
2082 // Higher ground level
2083 double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
2084 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2085 seed+85039, 5, 0.69);
2086 //higher = 30; // For debugging
2088 // Limit higher to at least base
2092 // Steepness factor of cliffs
2093 double b = 1.0 + 1.0 * noise2d_perlin(
2094 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2096 b = rangelim(b, 0.0, 1000.0);
2099 b = rangelim(b, 3.0, 1000.0);
2100 //dstream<<"b="<<b<<std::endl;
2103 // Offset to more low
2104 double a_off = -0.2;
2105 // High/low selector
2106 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2107 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2108 seed-359, 6, 0.7));*/
2109 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2110 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2111 seed-359, 5, 0.60));
2113 a = rangelim(a, 0.0, 1.0);
2115 //dstream<<"a="<<a<<std::endl;
2117 double h = base*(1.0-a) + higher*a;
2125 Adds random objects to block, depending on the content of the block
2127 void addRandomObjects(MapBlock *block)
2129 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2130 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2132 bool last_node_walkable = false;
2133 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2136 MapNode n = block->getNodeNoEx(p);
2137 if(n.d == CONTENT_IGNORE)
2139 if(content_features(n.d).liquid_type != LIQUID_NONE)
2141 if(content_features(n.d).walkable)
2143 last_node_walkable = true;
2146 if(last_node_walkable)
2148 // If block contains light information
2149 if(content_features(n.d).param_type == CPT_LIGHT)
2151 if(n.getLight(LIGHTBANK_DAY) <= 3)
2153 if(myrand() % 300 == 0)
2155 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2157 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
2158 std::string data = obj->getStaticData();
2159 StaticObject s_obj(obj->getType(),
2160 obj->getBasePosition(), data);
2162 block->m_static_objects.insert(0, s_obj);
2163 block->m_static_objects.insert(0, s_obj);
2164 block->m_static_objects.insert(0, s_obj);
2165 block->m_static_objects.insert(0, s_obj);
2166 block->m_static_objects.insert(0, s_obj);
2167 block->m_static_objects.insert(0, s_obj);
2170 if(myrand() % 300 == 0)
2172 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2174 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
2175 std::string data = obj->getStaticData();
2176 StaticObject s_obj(obj->getType(),
2177 obj->getBasePosition(), data);
2179 block->m_static_objects.insert(0, s_obj);
2185 last_node_walkable = false;
2188 block->setChangedFlag();
2191 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2194 This is the main map generation method
2197 void makeChunk(ChunkMakeData *data)
2202 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2203 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2204 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2205 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2206 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2207 *(u32)h_blocks*MAP_BLOCKSIZE;
2208 v3s16 bigarea_blocks_min(
2209 data->sectorpos_bigbase.X,
2211 data->sectorpos_bigbase.Y
2213 v3s16 bigarea_blocks_max(
2214 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2216 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2218 s16 lighting_min_d = 0-data->max_spread_amount;
2219 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2220 + data->max_spread_amount-1;
2223 data->vmanip.clearFlag(0xff);
2225 TimeTaker timer_generate("makeChunk() generate");
2227 // Maximum height of the stone surface and obstacles.
2228 // This is used to disable cave generation from going too high.
2229 s16 stone_surface_max_y = 0;
2232 Generate general ground level to full area
2237 //TimeTaker timer1("ground level");
2239 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2240 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2243 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2246 Skip of already generated
2249 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2250 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2254 // Ground height at this point
2255 float surface_y_f = 0.0;
2257 // Use perlin noise for ground height
2258 surface_y_f = base_rock_level_2d(data->seed, p2d);
2260 /*// Experimental stuff
2262 float a = highlands_level_2d(data->seed, p2d);
2267 // Convert to integer
2268 s16 surface_y = (s16)surface_y_f;
2271 if(surface_y > stone_surface_max_y)
2272 stone_surface_max_y = surface_y;
2275 Fill ground with stone
2278 // Use fast index incrementing
2279 v3s16 em = data->vmanip.m_area.getExtent();
2280 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2281 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2283 // Skip if already generated.
2284 // This is done here because there might be a cave at
2285 // any point in ground, which could look like it
2286 // wasn't generated.
2287 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2290 data->vmanip.m_data[i].d = CONTENT_STONE;
2292 data->vmanip.m_area.add_y(em, i, 1);
2300 Randomize some parameters
2303 //s32 stone_obstacle_count = 0;
2304 /*s32 stone_obstacle_count =
2305 rangelim((1.0+noise2d(data->seed+897,
2306 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2308 //s16 stone_obstacle_max_height = 0;
2309 /*s16 stone_obstacle_max_height =
2310 rangelim((1.0+noise2d(data->seed+5902,
2311 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2314 Loop this part, it will make stuff look older and newer nicely
2316 //for(u32 i_age=0; i_age<1; i_age++)
2317 for(u32 i_age=0; i_age<2; i_age++)
2319 /******************************
2320 BEGINNING OF AGING LOOP
2321 ******************************/
2325 //TimeTaker timer1("caves");
2330 u32 caves_count = relative_volume / 400000;
2331 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2332 if(stone_surface_max_y < WATER_LEVEL)
2334 /*u32 caves_count = 0;
2335 u32 bruises_count = 0;*/
2336 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2338 s16 min_tunnel_diameter = 3;
2339 s16 max_tunnel_diameter = 5;
2340 u16 tunnel_routepoints = 20;
2342 v3f main_direction(0,0,0);
2344 bool bruise_surface = (jj > caves_count);
2348 min_tunnel_diameter = 5;
2349 max_tunnel_diameter = myrand_range(10, 20);
2350 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2351 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2353 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2354 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2356 tunnel_routepoints = 5;
2362 // Allowed route area size in nodes
2364 data->sectorpos_base_size*MAP_BLOCKSIZE,
2365 h_blocks*MAP_BLOCKSIZE,
2366 data->sectorpos_base_size*MAP_BLOCKSIZE
2369 // Area starting point in nodes
2371 data->sectorpos_base.X*MAP_BLOCKSIZE,
2372 data->y_blocks_min*MAP_BLOCKSIZE,
2373 data->sectorpos_base.Y*MAP_BLOCKSIZE
2377 //(this should be more than the maximum radius of the tunnel)
2378 //s16 insure = 5; // Didn't work with max_d = 20
2380 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2381 ar += v3s16(1,0,1) * more * 2;
2382 of -= v3s16(1,0,1) * more;
2384 s16 route_y_min = 0;
2385 // Allow half a diameter + 7 over stone surface
2386 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2388 /*// If caves, don't go through surface too often
2389 if(bruise_surface == false)
2390 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2392 // Limit maximum to area
2393 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2397 /*// Minimum is at y=0
2398 route_y_min = -of.Y - 0;*/
2399 // Minimum is at y=max_tunnel_diameter/4
2400 //route_y_min = -of.Y + max_tunnel_diameter/4;
2401 //s16 min = -of.Y + max_tunnel_diameter/4;
2402 s16 min = -of.Y + 0;
2403 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2404 route_y_min = rangelim(route_y_min, 0, route_y_max);
2407 /*dstream<<"route_y_min = "<<route_y_min
2408 <<", route_y_max = "<<route_y_max<<std::endl;*/
2410 s16 route_start_y_min = route_y_min;
2411 s16 route_start_y_max = route_y_max;
2413 // Start every 2nd cave from surface
2414 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2416 if(coming_from_surface)
2418 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2421 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2422 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2424 // Randomize starting position
2426 (float)(myrand()%ar.X)+0.5,
2427 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2428 (float)(myrand()%ar.Z)+0.5
2431 MapNode airnode(CONTENT_AIR);
2434 Generate some tunnel starting from orp
2437 for(u16 j=0; j<tunnel_routepoints; j++)
2439 if(j%7==0 && bruise_surface == false)
2441 main_direction = v3f(
2442 ((float)(myrand()%20)-(float)10)/10,
2443 ((float)(myrand()%20)-(float)10)/30,
2444 ((float)(myrand()%20)-(float)10)/10
2446 main_direction *= (float)myrand_range(1, 3);
2450 s16 min_d = min_tunnel_diameter;
2451 s16 max_d = max_tunnel_diameter;
2452 s16 rs = myrand_range(min_d, max_d);
2457 maxlen = v3s16(rs*7,rs*7,rs*7);
2461 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2466 if(coming_from_surface && j < 3)
2469 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2470 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2471 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2477 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2478 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2479 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2483 vec += main_direction;
2488 else if(rp.X >= ar.X)
2490 if(rp.Y < route_y_min)
2492 else if(rp.Y >= route_y_max)
2493 rp.Y = route_y_max-1;
2496 else if(rp.Z >= ar.Z)
2500 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2502 v3f fp = orp + vec * f;
2503 v3s16 cp(fp.X, fp.Y, fp.Z);
2506 s16 d1 = d0 + rs - 1;
2507 for(s16 z0=d0; z0<=d1; z0++)
2509 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2510 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2511 for(s16 x0=-si; x0<=si-1; x0++)
2513 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2514 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2515 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2516 //s16 si2 = rs - abs(x0);
2517 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2523 /*if(isInArea(p, ar) == false)
2525 // Check only height
2526 if(y < 0 || y >= ar.Y)
2530 //assert(data->vmanip.m_area.contains(p));
2531 if(data->vmanip.m_area.contains(p) == false)
2533 dstream<<"WARNING: "<<__FUNCTION_NAME
2534 <<":"<<__LINE__<<": "
2535 <<"point not in area"
2540 // Just set it to air, it will be changed to
2542 u32 i = data->vmanip.m_area.index(p);
2543 data->vmanip.m_data[i] = airnode;
2545 if(bruise_surface == false)
2548 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2563 //TimeTaker timer1("ore veins");
2568 for(u32 jj=0; jj<relative_volume/1000; jj++)
2570 s16 max_vein_diameter = 3;
2572 // Allowed route area size in nodes
2574 data->sectorpos_base_size*MAP_BLOCKSIZE,
2575 h_blocks*MAP_BLOCKSIZE,
2576 data->sectorpos_base_size*MAP_BLOCKSIZE
2579 // Area starting point in nodes
2581 data->sectorpos_base.X*MAP_BLOCKSIZE,
2582 data->y_blocks_min*MAP_BLOCKSIZE,
2583 data->sectorpos_base.Y*MAP_BLOCKSIZE
2587 //(this should be more than the maximum radius of the tunnel)
2589 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2590 ar += v3s16(1,0,1) * more * 2;
2591 of -= v3s16(1,0,1) * more;
2593 // Randomize starting position
2595 (float)(myrand()%ar.X)+0.5,
2596 (float)(myrand()%ar.Y)+0.5,
2597 (float)(myrand()%ar.Z)+0.5
2600 // Randomize mineral
2603 mineral = MINERAL_COAL;
2605 mineral = MINERAL_IRON;
2608 Generate some vein starting from orp
2611 for(u16 j=0; j<2; j++)
2614 (float)(myrand()%ar.X)+0.5,
2615 (float)(myrand()%ar.Y)+0.5,
2616 (float)(myrand()%ar.Z)+0.5
2618 v3f vec = rp - orp;*/
2620 v3s16 maxlen(5, 5, 5);
2622 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2623 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2624 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2629 else if(rp.X >= ar.X)
2633 else if(rp.Y >= ar.Y)
2637 else if(rp.Z >= ar.Z)
2643 s16 max_d = max_vein_diameter;
2644 s16 rs = myrand_range(min_d, max_d);
2646 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2648 v3f fp = orp + vec * f;
2649 v3s16 cp(fp.X, fp.Y, fp.Z);
2651 s16 d1 = d0 + rs - 1;
2652 for(s16 z0=d0; z0<=d1; z0++)
2654 s16 si = rs - abs(z0);
2655 for(s16 x0=-si; x0<=si-1; x0++)
2657 s16 si2 = rs - abs(x0);
2658 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2660 // Don't put mineral to every place
2668 /*if(isInArea(p, ar) == false)
2670 // Check only height
2671 if(y < 0 || y >= ar.Y)
2675 assert(data->vmanip.m_area.contains(p));
2677 // Just set it to air, it will be changed to
2679 u32 i = data->vmanip.m_area.index(p);
2680 MapNode *n = &data->vmanip.m_data[i];
2681 if(n->d == CONTENT_STONE)
2696 //TimeTaker timer1("add mud");
2699 Add mud to the central chunk
2702 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2703 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2705 // Node position in 2d
2706 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2708 // Randomize mud amount
2709 s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
2710 0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
2711 data->seed+1, 3, 0.55));
2713 // Find ground level
2714 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2717 If topmost node is grass, change it to mud.
2718 It might be if it was flown to there from a neighboring
2719 chunk and then converted.
2722 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2723 MapNode *n = &data->vmanip.m_data[i];
2724 if(n->d == CONTENT_GRASS)
2733 v3s16 em = data->vmanip.m_area.getExtent();
2734 s16 y_start = surface_y+1;
2735 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2736 for(s16 y=y_start; y<=y_nodes_max; y++)
2738 if(mudcount >= mud_add_amount)
2741 MapNode &n = data->vmanip.m_data[i];
2745 data->vmanip.m_area.add_y(em, i, 1);
2754 TimeTaker timer1("flow mud");
2757 Flow mud away from steep edges
2760 // Limit area by 1 because mud is flown into neighbors.
2761 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2762 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2764 // Iterate a few times
2765 for(s16 k=0; k<3; k++)
2768 for(s16 x=mudflow_minpos;
2771 for(s16 z=mudflow_minpos;
2775 // Invert coordinates every 2nd iteration
2778 x = mudflow_maxpos - (x-mudflow_minpos);
2779 z = mudflow_maxpos - (z-mudflow_minpos);
2782 // Node position in 2d
2783 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2785 v3s16 em = data->vmanip.m_area.getExtent();
2786 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2793 for(; y>=y_nodes_min; y--)
2795 n = &data->vmanip.m_data[i];
2796 //if(content_walkable(n->d))
2798 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2801 data->vmanip.m_area.add_y(em, i, -1);
2804 // Stop if out of area
2805 //if(data->vmanip.m_area.contains(i) == false)
2809 /*// If not mud, do nothing to it
2810 MapNode *n = &data->vmanip.m_data[i];
2811 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2815 Don't flow it if the stuff under it is not mud
2819 data->vmanip.m_area.add_y(em, i2, -1);
2820 // Cancel if out of area
2821 if(data->vmanip.m_area.contains(i2) == false)
2823 MapNode *n2 = &data->vmanip.m_data[i2];
2824 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2828 // Make it exactly mud
2831 /*s16 recurse_count = 0;
2835 v3s16(0,0,1), // back
2836 v3s16(1,0,0), // right
2837 v3s16(0,0,-1), // front
2838 v3s16(-1,0,0), // left
2841 // Theck that upper is air or doesn't exist.
2842 // Cancel dropping if upper keeps it in place
2844 data->vmanip.m_area.add_y(em, i3, 1);
2845 if(data->vmanip.m_area.contains(i3) == true
2846 && content_walkable(data->vmanip.m_data[i3].d) == true)
2853 for(u32 di=0; di<4; di++)
2855 v3s16 dirp = dirs4[di];
2858 data->vmanip.m_area.add_p(em, i2, dirp);
2859 // Fail if out of area
2860 if(data->vmanip.m_area.contains(i2) == false)
2862 // Check that side is air
2863 MapNode *n2 = &data->vmanip.m_data[i2];
2864 if(content_walkable(n2->d))
2866 // Check that under side is air
2867 data->vmanip.m_area.add_y(em, i2, -1);
2868 if(data->vmanip.m_area.contains(i2) == false)
2870 n2 = &data->vmanip.m_data[i2];
2871 if(content_walkable(n2->d))
2873 /*// Check that under that is air (need a drop of 2)
2874 data->vmanip.m_area.add_y(em, i2, -1);
2875 if(data->vmanip.m_area.contains(i2) == false)
2877 n2 = &data->vmanip.m_data[i2];
2878 if(content_walkable(n2->d))
2880 // Loop further down until not air
2882 data->vmanip.m_area.add_y(em, i2, -1);
2883 // Fail if out of area
2884 if(data->vmanip.m_area.contains(i2) == false)
2886 n2 = &data->vmanip.m_data[i2];
2887 }while(content_walkable(n2->d) == false);
2888 // Loop one up so that we're in air
2889 data->vmanip.m_area.add_y(em, i2, 1);
2890 n2 = &data->vmanip.m_data[i2];
2892 // Move mud to new place
2894 // Set old place to be air
2895 *n = MapNode(CONTENT_AIR);
2908 //TimeTaker timer1("add water");
2911 Add water to the central chunk (and a bit more)
2914 for(s16 x=0-data->max_spread_amount;
2915 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
2917 for(s16 z=0-data->max_spread_amount;
2918 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
2921 // Node position in 2d
2922 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2924 // Find ground level
2925 //s16 surface_y = find_ground_level(data->vmanip, p2d);
2928 If ground level is over water level, skip.
2929 NOTE: This leaves caves near water without water,
2930 which looks especially crappy when the nearby water
2931 won't start flowing either for some reason
2933 /*if(surface_y > WATER_LEVEL)
2940 v3s16 em = data->vmanip.m_area.getExtent();
2941 u8 light = LIGHT_MAX;
2942 // Start at global water surface level
2943 s16 y_start = WATER_LEVEL;
2944 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2945 MapNode *n = &data->vmanip.m_data[i];
2947 for(s16 y=y_start; y>=y_nodes_min; y--)
2949 n = &data->vmanip.m_data[i];
2951 // Stop when there is no water and no air
2952 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
2953 && n->d != CONTENT_WATER)
2959 // Make water only not in caves
2960 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
2962 n->d = CONTENT_WATERSOURCE;
2963 //n->setLight(LIGHTBANK_DAY, light);
2965 // Add to transforming liquid queue (in case it'd
2967 v3s16 p = v3s16(p2d.X, y, p2d.Y);
2968 data->transforming_liquid.push_back(p);
2972 data->vmanip.m_area.add_y(em, i, -1);
2983 /***********************
2985 ************************/
2988 //TimeTaker timer1("convert mud to sand");
2994 //s16 mud_add_amount = myrand_range(2, 4);
2995 //s16 mud_add_amount = 0;
2997 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2998 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
2999 for(s16 x=0-data->max_spread_amount+1;
3000 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3002 for(s16 z=0-data->max_spread_amount+1;
3003 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3006 // Node position in 2d
3007 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3009 // Determine whether to have sand here
3010 double sandnoise = noise2d_perlin(
3011 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3012 data->seed+59420, 3, 0.50);
3014 bool have_sand = (sandnoise > -0.15);
3016 if(have_sand == false)
3019 // Find ground level
3020 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3022 if(surface_y > WATER_LEVEL + 2)
3026 v3s16 em = data->vmanip.m_area.getExtent();
3027 s16 y_start = surface_y;
3028 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3029 u32 not_sand_counter = 0;
3030 for(s16 y=y_start; y>=y_nodes_min; y--)
3032 MapNode *n = &data->vmanip.m_data[i];
3033 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3035 n->d = CONTENT_SAND;
3040 if(not_sand_counter > 3)
3044 data->vmanip.m_area.add_y(em, i, -1);
3053 //TimeTaker timer1("generate trees");
3059 // Divide area into parts
3061 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3062 double area = sidelen * sidelen;
3063 for(s16 x0=0; x0<div; x0++)
3064 for(s16 z0=0; z0<div; z0++)
3066 // Center position of part of division
3068 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3069 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3071 // Minimum edge of part of division
3073 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3074 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3076 // Maximum edge of part of division
3078 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3079 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3082 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3083 // Put trees in random places on part of division
3084 for(u32 i=0; i<tree_count; i++)
3086 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3087 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3088 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3089 // Don't make a tree under water level
3092 // Don't make a tree so high that it doesn't fit
3093 if(y > y_nodes_max - 6)
3097 Trees grow only on mud and grass
3100 u32 i = data->vmanip.m_area.index(v3s16(p));
3101 MapNode *n = &data->vmanip.m_data[i];
3102 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3107 make_tree(data->vmanip, p);
3110 /*u32 tree_max = relative_area / 60;
3111 //u32 count = myrand_range(0, tree_max);
3112 for(u32 i=0; i<count; i++)
3114 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3115 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3116 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3117 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3118 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3119 // Don't make a tree under water level
3124 make_tree(data->vmanip, p);
3132 //TimeTaker timer1("grow grass");
3138 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3139 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3140 for(s16 x=0-data->max_spread_amount;
3141 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3143 for(s16 z=0-data->max_spread_amount;
3144 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3147 // Node position in 2d
3148 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3151 Find the lowest surface to which enough light ends up
3154 Basically just wait until not air and not leaves.
3158 v3s16 em = data->vmanip.m_area.getExtent();
3159 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3161 // Go to ground level
3162 for(y=y_nodes_max; y>=y_nodes_min; y--)
3164 MapNode &n = data->vmanip.m_data[i];
3165 if(n.d != CONTENT_AIR
3166 && n.d != CONTENT_LEAVES)
3168 data->vmanip.m_area.add_y(em, i, -1);
3170 if(y >= y_nodes_min)
3173 surface_y = y_nodes_min;
3176 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3177 MapNode *n = &data->vmanip.m_data[i];
3178 if(n->d == CONTENT_MUD)
3179 n->d = CONTENT_GRASS;
3185 Initial lighting (sunlight)
3188 core::map<v3s16, bool> light_sources;
3191 // 750ms @cs=8, can't optimize more
3192 TimeTaker timer1("initial lighting");
3196 Go through the edges and add all nodes that have light to light_sources
3200 for(s16 i=0; i<4; i++)
3202 for(s16 j=lighting_min_d;
3209 if(i == 0 || i == 1)
3211 x = (i==0) ? lighting_min_d : lighting_max_d;
3220 z = (i==0) ? lighting_min_d : lighting_max_d;
3227 // Node position in 2d
3228 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3231 v3s16 em = data->vmanip.m_area.getExtent();
3232 s16 y_start = y_nodes_max;
3233 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3234 for(s16 y=y_start; y>=y_nodes_min; y--)
3236 MapNode *n = &data->vmanip.m_data[i];
3237 if(n->getLight(LIGHTBANK_DAY) != 0)
3239 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3241 //NOTE: This is broken, at least the index has to
3250 Go through the edges and apply sunlight to them, not caring
3255 for(s16 i=0; i<4; i++)
3257 for(s16 j=lighting_min_d;
3264 if(i == 0 || i == 1)
3266 x = (i==0) ? lighting_min_d : lighting_max_d;
3275 z = (i==0) ? lighting_min_d : lighting_max_d;
3282 // Node position in 2d
3283 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3285 // Loop from top to down
3287 u8 light = LIGHT_SUN;
3288 v3s16 em = data->vmanip.m_area.getExtent();
3289 s16 y_start = y_nodes_max;
3290 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3291 for(s16 y=y_start; y>=y_nodes_min; y--)
3293 MapNode *n = &data->vmanip.m_data[i];
3294 if(light_propagates_content(n->d) == false)
3298 else if(light != LIGHT_SUN
3299 || sunlight_propagates_content(n->d) == false)
3305 n->setLight(LIGHTBANK_DAY, light);
3306 n->setLight(LIGHTBANK_NIGHT, 0);
3310 // Insert light source
3311 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3314 // Increment index by y
3315 data->vmanip.m_area.add_y(em, i, -1);
3321 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3322 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3323 /*for(s16 x=0-data->max_spread_amount+1;
3324 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3326 for(s16 z=0-data->max_spread_amount+1;
3327 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3331 This has to be 1 smaller than the actual area, because
3332 neighboring nodes are checked.
3334 for(s16 x=lighting_min_d+1;
3335 x<=lighting_max_d-1;
3337 for(s16 z=lighting_min_d+1;
3338 z<=lighting_max_d-1;
3341 // Node position in 2d
3342 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3345 Apply initial sunlight
3348 u8 light = LIGHT_SUN;
3349 bool add_to_sources = false;
3350 v3s16 em = data->vmanip.m_area.getExtent();
3351 s16 y_start = y_nodes_max;
3352 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3353 for(s16 y=y_start; y>=y_nodes_min; y--)
3355 MapNode *n = &data->vmanip.m_data[i];
3357 if(light_propagates_content(n->d) == false)
3361 else if(light != LIGHT_SUN
3362 || sunlight_propagates_content(n->d) == false)
3368 // This doesn't take much time
3369 if(add_to_sources == false)
3372 Check sides. If side is not air or water, start
3373 adding to light_sources.
3376 v3s16(0,0,1), // back
3377 v3s16(1,0,0), // right
3378 v3s16(0,0,-1), // front
3379 v3s16(-1,0,0), // left
3381 for(u32 di=0; di<4; di++)
3383 v3s16 dirp = dirs4[di];
3385 data->vmanip.m_area.add_p(em, i2, dirp);
3386 MapNode *n2 = &data->vmanip.m_data[i2];
3388 n2->d != CONTENT_AIR
3389 && n2->d != CONTENT_WATERSOURCE
3390 && n2->d != CONTENT_WATER
3392 add_to_sources = true;
3398 n->setLight(LIGHTBANK_DAY, light);
3399 n->setLight(LIGHTBANK_NIGHT, 0);
3401 // This doesn't take much time
3402 if(light != 0 && add_to_sources)
3404 // Insert light source
3405 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3408 // Increment index by y
3409 data->vmanip.m_area.add_y(em, i, -1);
3417 // Spread light around
3419 TimeTaker timer("makeChunk() spreadLight");
3420 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3427 timer_generate.stop();
3430 //###################################################################
3431 //###################################################################
3432 //###################################################################
3433 //###################################################################
3434 //###################################################################
3435 //###################################################################
3436 //###################################################################
3437 //###################################################################
3438 //###################################################################
3439 //###################################################################
3440 //###################################################################
3441 //###################################################################
3442 //###################################################################
3443 //###################################################################
3444 //###################################################################
3446 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3448 if(m_chunksize == 0)
3456 // The distance how far into the neighbors the generator is allowed to go.
3457 s16 max_spread_amount_sectors = 2;
3458 assert(max_spread_amount_sectors <= m_chunksize);
3459 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3461 s16 y_blocks_min = -4;
3462 s16 y_blocks_max = 3;
3464 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3465 s16 sectorpos_base_size = m_chunksize;
3467 v2s16 sectorpos_bigbase =
3468 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3469 s16 sectorpos_bigbase_size =
3470 sectorpos_base_size + 2 * max_spread_amount_sectors;
3473 data.chunkpos = chunkpos;
3474 data.y_blocks_min = y_blocks_min;
3475 data.y_blocks_max = y_blocks_max;
3476 data.sectorpos_base = sectorpos_base;
3477 data.sectorpos_base_size = sectorpos_base_size;
3478 data.sectorpos_bigbase = sectorpos_bigbase;
3479 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3480 data.max_spread_amount = max_spread_amount;
3483 Create the whole area of this and the neighboring chunks
3486 TimeTaker timer("initChunkMake() create area");
3488 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3489 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3491 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3492 ServerMapSector *sector = createSector(sectorpos);
3495 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3497 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3498 MapBlock *block = createBlock(blockpos);
3500 // Lighting won't be calculated
3501 //block->setLightingExpired(true);
3502 // Lighting will be calculated
3503 block->setLightingExpired(false);
3506 Block gets sunlight if this is true.
3508 This should be set to true when the top side of a block
3509 is completely exposed to the sky.
3511 Actually this doesn't matter now because the
3512 initial lighting is done here.
3514 block->setIsUnderground(y != y_blocks_max);
3520 Now we have a big empty area.
3522 Make a ManualMapVoxelManipulator that contains this and the
3526 v3s16 bigarea_blocks_min(
3527 sectorpos_bigbase.X,
3531 v3s16 bigarea_blocks_max(
3532 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3534 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3537 data.vmanip.setMap(this);
3540 TimeTaker timer("initChunkMake() initialEmerge");
3541 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3546 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3547 core::map<v3s16, MapBlock*> &changed_blocks)
3553 Blit generated stuff to map
3557 //TimeTaker timer("generateChunkRaw() blitBackAll");
3558 data.vmanip.blitBackAll(&changed_blocks);
3562 Update day/night difference cache of the MapBlocks
3565 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3566 i.atEnd() == false; i++)
3568 MapBlock *block = i.getNode()->getValue();
3569 block->updateDayNightDiff();
3574 Copy transforming liquid information
3576 while(data.transforming_liquid.size() > 0)
3578 v3s16 p = data.transforming_liquid.pop_front();
3579 m_transforming_liquid.push_back(p);
3583 Add random objects to blocks
3586 for(s16 x=0; x<data.sectorpos_base_size; x++)
3587 for(s16 z=0; z<data.sectorpos_base_size; z++)
3589 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3590 ServerMapSector *sector = createSector(sectorpos);
3593 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3595 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3596 MapBlock *block = createBlock(blockpos);
3597 addRandomObjects(block);
3603 Create chunk metadata
3606 for(s16 x=-1; x<=1; x++)
3607 for(s16 y=-1; y<=1; y++)
3609 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3610 // Add chunk meta information
3611 MapChunk *chunk = getChunk(chunkpos0);
3614 chunk = new MapChunk();
3615 m_chunks.insert(chunkpos0, chunk);
3617 //chunk->setIsVolatile(true);
3618 if(chunk->getGenLevel() > GENERATED_PARTLY)
3619 chunk->setGenLevel(GENERATED_PARTLY);
3623 Set central chunk non-volatile
3625 MapChunk *chunk = getChunk(data.chunkpos);
3628 //chunk->setIsVolatile(false);
3629 chunk->setGenLevel(GENERATED_FULLY);
3632 Save changed parts of map
3641 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3642 core::map<v3s16, MapBlock*> &changed_blocks,
3645 DSTACK(__FUNCTION_NAME);
3648 Don't generate if already fully generated
3652 MapChunk *chunk = getChunk(chunkpos);
3653 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3655 dstream<<"generateChunkRaw(): Chunk "
3656 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3657 <<" already generated"<<std::endl;
3662 dstream<<"generateChunkRaw(): Generating chunk "
3663 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3666 TimeTaker timer("generateChunkRaw()");
3670 // Initialize generation
3671 initChunkMake(data, chunkpos);
3676 // Finalize generation
3677 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3680 Return central chunk (which was requested)
3686 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3687 core::map<v3s16, MapBlock*> &changed_blocks)
3689 dstream<<"generateChunk(): Generating chunk "
3690 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3693 /*for(s16 x=-1; x<=1; x++)
3694 for(s16 y=-1; y<=1; y++)*/
3695 for(s16 x=-0; x<=0; x++)
3696 for(s16 y=-0; y<=0; y++)
3698 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3699 MapChunk *chunk = getChunk(chunkpos0);
3700 // Skip if already generated
3701 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3703 generateChunkRaw(chunkpos0, changed_blocks);
3706 assert(chunkNonVolatile(chunkpos1));
3708 MapChunk *chunk = getChunk(chunkpos1);
3713 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3715 DSTACK("%s: p2d=(%d,%d)",
3720 Check if it exists already in memory
3722 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3727 Try to load it from disk (with blocks)
3729 if(loadSectorFull(p2d) == true)
3731 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3734 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3735 throw InvalidPositionException("");
3741 Do not create over-limit
3743 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3744 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3745 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3746 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3747 throw InvalidPositionException("createSector(): pos. over limit");
3750 Generate blank sector
3753 sector = new ServerMapSector(this, p2d);
3755 // Sector position on map in nodes
3756 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3761 m_sectors.insert(p2d, sector);
3767 MapSector * ServerMap::emergeSector(v2s16 p2d,
3768 core::map<v3s16, MapBlock*> &changed_blocks)
3770 DSTACK("%s: p2d=(%d,%d)",
3777 v2s16 chunkpos = sector_to_chunk(p2d);
3778 /*bool chunk_nonvolatile = false;
3779 MapChunk *chunk = getChunk(chunkpos);
3780 if(chunk && chunk->getIsVolatile() == false)
3781 chunk_nonvolatile = true;*/
3782 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3785 If chunk is not fully generated, generate chunk
3787 if(chunk_nonvolatile == false)
3789 // Generate chunk and neighbors
3790 generateChunk(chunkpos, changed_blocks);
3794 Return sector if it exists now
3796 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3801 Try to load it from disk
3803 if(loadSectorFull(p2d) == true)
3805 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3808 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3809 throw InvalidPositionException("");
3815 generateChunk should have generated the sector
3819 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3820 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3824 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3826 return createSector(p2d);
3831 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3834 generateChunkRaw(chunkpos, changed_blocks, true);
3837 Return sector if it exists now
3839 sector = getSectorNoGenerateNoEx(p2d);
3843 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3851 //return generateSector();
3856 NOTE: This is not used for main map generation, only for blocks
3857 that are very high or low
3859 MapBlock * ServerMap::generateBlock(
3861 MapBlock *original_dummy,
3862 ServerMapSector *sector,
3863 core::map<v3s16, MapBlock*> &changed_blocks,
3864 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3867 DSTACK("%s: p=(%d,%d,%d)",
3871 // If chunks are disabled
3872 /*if(m_chunksize == 0)
3874 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
3875 <<"not generating."<<std::endl;
3879 /*dstream<<"generateBlock(): "
3880 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3883 MapBlock *block = original_dummy;
3885 v2s16 p2d(p.X, p.Z);
3887 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
3890 Do not generate over-limit
3892 if(blockpos_over_limit(p))
3894 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
3895 throw InvalidPositionException("generateBlock(): pos. over limit");
3899 If block doesn't exist, create one.
3900 If it exists, it is a dummy. In that case unDummify() it.
3902 NOTE: This already sets the map as the parent of the block
3906 block = sector->createBlankBlockNoInsert(block_y);
3910 // Remove the block so that nobody can get a half-generated one.
3911 sector->removeBlock(block);
3912 // Allocate the block to contain the generated data
3916 u8 water_material = CONTENT_WATERSOURCE;
3918 s32 lowest_ground_y = 32767;
3919 s32 highest_ground_y = -32768;
3921 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3922 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3924 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
3926 //s16 surface_y = 0;
3928 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
3929 + AVERAGE_MUD_AMOUNT;
3930 // If chunks are disabled
3931 if(m_chunksize == 0)
3932 surface_y = WATER_LEVEL + 1;
3934 if(surface_y < lowest_ground_y)
3935 lowest_ground_y = surface_y;
3936 if(surface_y > highest_ground_y)
3937 highest_ground_y = surface_y;
3939 s32 surface_depth = AVERAGE_MUD_AMOUNT;
3941 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3943 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
3948 NOTE: If there are some man-made structures above the
3949 newly created block, they won't be taken into account.
3951 if(real_y > surface_y)
3952 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
3958 // If node is over heightmap y, it's air or water
3959 if(real_y > surface_y)
3961 // If under water level, it's water
3962 if(real_y < WATER_LEVEL)
3964 n.d = water_material;
3965 n.setLight(LIGHTBANK_DAY,
3966 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
3968 Add to transforming liquid queue (in case it'd
3971 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
3972 m_transforming_liquid.push_back(real_pos);
3978 // Else it's ground or caves (air)
3981 // If it's surface_depth under ground, it's stone
3982 if(real_y <= surface_y - surface_depth)
3984 n.d = CONTENT_STONE;
3988 // It is mud if it is under the first ground
3989 // level or under water
3990 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
3996 n.d = CONTENT_GRASS;
3999 //n.d = CONTENT_MUD;
4001 /*// If under water level, it's mud
4002 if(real_y < WATER_LEVEL)
4004 // Only the topmost node is grass
4005 else if(real_y <= surface_y - 1)
4008 n.d = CONTENT_GRASS;*/
4012 block->setNode(v3s16(x0,y0,z0), n);
4017 Calculate some helper variables
4020 // Completely underground if the highest part of block is under lowest
4022 // This has to be very sure; it's probably one too strict now but
4023 // that's just better.
4024 bool completely_underground =
4025 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4027 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4029 bool mostly_underwater_surface = false;
4030 if(highest_ground_y < WATER_LEVEL
4031 && some_part_underground && !completely_underground)
4032 mostly_underwater_surface = true;
4035 Get local attributes
4038 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4040 float caves_amount = 0.5;
4045 NOTE: BEWARE: Too big amount of attribute points slows verything
4047 1 interpolation from 5000 points takes 2-3ms.
4049 //TimeTaker timer("generateBlock() local attribute retrieval");
4050 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4051 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4052 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4056 //dstream<<"generateBlock(): Done"<<std::endl;
4062 // Initialize temporary table
4063 const s32 ued = MAP_BLOCKSIZE;
4064 bool underground_emptiness[ued*ued*ued];
4065 for(s32 i=0; i<ued*ued*ued; i++)
4067 underground_emptiness[i] = 0;
4074 Initialize orp and ors. Try to find if some neighboring
4075 MapBlock has a tunnel ended in its side
4079 (float)(myrand()%ued)+0.5,
4080 (float)(myrand()%ued)+0.5,
4081 (float)(myrand()%ued)+0.5
4084 bool found_existing = false;
4090 for(s16 y=0; y<ued; y++)
4091 for(s16 x=0; x<ued; x++)
4093 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4094 if(getNode(ap).d == CONTENT_AIR)
4096 orp = v3f(x+1,y+1,0);
4097 found_existing = true;
4098 goto continue_generating;
4102 catch(InvalidPositionException &e){}
4108 for(s16 y=0; y<ued; y++)
4109 for(s16 x=0; x<ued; x++)
4111 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4112 if(getNode(ap).d == CONTENT_AIR)
4114 orp = v3f(x+1,y+1,ued-1);
4115 found_existing = true;
4116 goto continue_generating;
4120 catch(InvalidPositionException &e){}
4126 for(s16 y=0; y<ued; y++)
4127 for(s16 z=0; z<ued; z++)
4129 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4130 if(getNode(ap).d == CONTENT_AIR)
4132 orp = v3f(0,y+1,z+1);
4133 found_existing = true;
4134 goto continue_generating;
4138 catch(InvalidPositionException &e){}
4144 for(s16 y=0; y<ued; y++)
4145 for(s16 z=0; z<ued; z++)
4147 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4148 if(getNode(ap).d == CONTENT_AIR)
4150 orp = v3f(ued-1,y+1,z+1);
4151 found_existing = true;
4152 goto continue_generating;
4156 catch(InvalidPositionException &e){}
4162 for(s16 x=0; x<ued; x++)
4163 for(s16 z=0; z<ued; z++)
4165 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4166 if(getNode(ap).d == CONTENT_AIR)
4168 orp = v3f(x+1,0,z+1);
4169 found_existing = true;
4170 goto continue_generating;
4174 catch(InvalidPositionException &e){}
4180 for(s16 x=0; x<ued; x++)
4181 for(s16 z=0; z<ued; z++)
4183 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4184 if(getNode(ap).d == CONTENT_AIR)
4186 orp = v3f(x+1,ued-1,z+1);
4187 found_existing = true;
4188 goto continue_generating;
4192 catch(InvalidPositionException &e){}
4194 continue_generating:
4197 Choose whether to actually generate cave
4199 bool do_generate_caves = true;
4200 // Don't generate if no part is underground
4201 if(!some_part_underground)
4203 do_generate_caves = false;
4205 // Don't generate if mostly underwater surface
4206 /*else if(mostly_underwater_surface)
4208 do_generate_caves = false;
4210 // Partly underground = cave
4211 else if(!completely_underground)
4213 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4215 // Found existing cave underground
4216 else if(found_existing && completely_underground)
4218 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4220 // Underground and no caves found
4223 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4226 if(do_generate_caves)
4229 Generate some tunnel starting from orp and ors
4231 for(u16 i=0; i<3; i++)
4234 (float)(myrand()%ued)+0.5,
4235 (float)(myrand()%ued)+0.5,
4236 (float)(myrand()%ued)+0.5
4240 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4244 for(float f=0; f<1.0; f+=0.04)
4246 v3f fp = orp + vec * f;
4247 v3s16 cp(fp.X, fp.Y, fp.Z);
4249 s16 d1 = d0 + rs - 1;
4250 for(s16 z0=d0; z0<=d1; z0++)
4252 s16 si = rs - abs(z0);
4253 for(s16 x0=-si; x0<=si-1; x0++)
4255 s16 si2 = rs - abs(x0);
4256 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4262 if(isInArea(p, ued) == false)
4264 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4276 // Set to true if has caves.
4277 // Set when some non-air is changed to air when making caves.
4278 bool has_caves = false;
4281 Apply temporary cave data to block
4284 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4285 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4287 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4289 MapNode n = block->getNode(v3s16(x0,y0,z0));
4292 if(underground_emptiness[
4293 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4294 +ued*(y0*ued/MAP_BLOCKSIZE)
4295 +(x0*ued/MAP_BLOCKSIZE)])
4297 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4306 block->setNode(v3s16(x0,y0,z0), n);
4311 This is used for guessing whether or not the block should
4312 receive sunlight from the top if the block above doesn't exist
4314 block->setIsUnderground(completely_underground);
4317 Force lighting update if some part of block is partly
4318 underground and has caves.
4320 /*if(some_part_underground && !completely_underground && has_caves)
4322 //dstream<<"Half-ground caves"<<std::endl;
4323 lighting_invalidated_blocks[block->getPos()] = block;
4326 // DEBUG: Always update lighting
4327 //lighting_invalidated_blocks[block->getPos()] = block;
4333 if(some_part_underground)
4335 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4340 for(s16 i=0; i<underground_level/4 + 1; i++)
4342 if(myrand()%50 == 0)
4345 (myrand()%(MAP_BLOCKSIZE-2))+1,
4346 (myrand()%(MAP_BLOCKSIZE-2))+1,
4347 (myrand()%(MAP_BLOCKSIZE-2))+1
4353 for(u16 i=0; i<27; i++)
4355 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4357 block->setNode(cp+g_27dirs[i], n);
4365 u16 coal_amount = 30;
4366 u16 coal_rareness = 60 / coal_amount;
4367 if(coal_rareness == 0)
4369 if(myrand()%coal_rareness == 0)
4371 u16 a = myrand() % 16;
4372 u16 amount = coal_amount * a*a*a / 1000;
4373 for(s16 i=0; i<amount; i++)
4376 (myrand()%(MAP_BLOCKSIZE-2))+1,
4377 (myrand()%(MAP_BLOCKSIZE-2))+1,
4378 (myrand()%(MAP_BLOCKSIZE-2))+1
4382 n.d = CONTENT_STONE;
4383 n.param = MINERAL_COAL;
4385 for(u16 i=0; i<27; i++)
4387 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4389 block->setNode(cp+g_27dirs[i], n);
4397 //TODO: change to iron_amount or whatever
4398 u16 iron_amount = 15;
4399 u16 iron_rareness = 60 / iron_amount;
4400 if(iron_rareness == 0)
4402 if(myrand()%iron_rareness == 0)
4404 u16 a = myrand() % 16;
4405 u16 amount = iron_amount * a*a*a / 1000;
4406 for(s16 i=0; i<amount; i++)
4409 (myrand()%(MAP_BLOCKSIZE-2))+1,
4410 (myrand()%(MAP_BLOCKSIZE-2))+1,
4411 (myrand()%(MAP_BLOCKSIZE-2))+1
4415 n.d = CONTENT_STONE;
4416 n.param = MINERAL_IRON;
4418 for(u16 i=0; i<27; i++)
4420 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4422 block->setNode(cp+g_27dirs[i], n);
4429 Create a few rats in empty blocks underground
4431 if(completely_underground)
4433 //for(u16 i=0; i<2; i++)
4436 (myrand()%(MAP_BLOCKSIZE-2))+1,
4437 (myrand()%(MAP_BLOCKSIZE-2))+1,
4438 (myrand()%(MAP_BLOCKSIZE-2))+1
4441 // Check that the place is empty
4442 //if(!is_ground_content(block->getNode(cp).d))
4445 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4446 block->addObject(obj);
4452 Add block to sector.
4454 sector->insertBlock(block);
4456 // Lighting is invalid after generation.
4457 block->setLightingExpired(true);
4464 <<"lighting_invalidated_blocks.size()"
4468 <<" "<<lighting_invalidated_blocks.size()
4470 <<", "<<completely_underground
4471 <<", "<<some_part_underground
4478 MapBlock * ServerMap::createBlock(v3s16 p)
4480 DSTACK("%s: p=(%d,%d,%d)",
4481 __FUNCTION_NAME, p.X, p.Y, p.Z);
4484 Do not create over-limit
4486 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4487 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4488 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4489 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4490 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4491 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4492 throw InvalidPositionException("createBlock(): pos. over limit");
4494 v2s16 p2d(p.X, p.Z);
4497 This will create or load a sector if not found in memory.
4498 If block exists on disk, it will be loaded.
4500 NOTE: On old save formats, this will be slow, as it generates
4501 lighting on blocks for them.
4503 ServerMapSector *sector;
4505 sector = (ServerMapSector*)createSector(p2d);
4506 assert(sector->getId() == MAPSECTOR_SERVER);
4508 catch(InvalidPositionException &e)
4510 dstream<<"createBlock: createSector() failed"<<std::endl;
4514 NOTE: This should not be done, or at least the exception
4515 should not be passed on as std::exception, because it
4516 won't be catched at all.
4518 /*catch(std::exception &e)
4520 dstream<<"createBlock: createSector() failed: "
4521 <<e.what()<<std::endl;
4526 Try to get a block from the sector
4529 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4533 block = sector->createBlankBlock(block_y);
4537 MapBlock * ServerMap::emergeBlock(
4539 bool only_from_disk,
4540 core::map<v3s16, MapBlock*> &changed_blocks,
4541 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4544 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4546 p.X, p.Y, p.Z, only_from_disk);
4549 Do not generate over-limit
4551 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4552 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4553 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4554 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4555 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4556 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4557 throw InvalidPositionException("emergeBlock(): pos. over limit");
4559 v2s16 p2d(p.X, p.Z);
4562 This will create or load a sector if not found in memory.
4563 If block exists on disk, it will be loaded.
4565 ServerMapSector *sector;
4567 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4568 assert(sector->getId() == MAPSECTOR_SERVER);
4570 catch(InvalidPositionException &e)
4572 dstream<<"emergeBlock: emergeSector() failed: "
4573 <<e.what()<<std::endl;
4574 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4576 <<"You could try to delete it."<<std::endl;
4579 catch(VersionMismatchException &e)
4581 dstream<<"emergeBlock: emergeSector() failed: "
4582 <<e.what()<<std::endl;
4583 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4585 <<"You could try to delete it."<<std::endl;
4589 NOTE: This should not be done, or at least the exception
4590 should not be passed on as std::exception, because it
4591 won't be catched at all.
4593 /*catch(std::exception &e)
4595 dstream<<"emergeBlock: emergeSector() failed: "
4596 <<e.what()<<std::endl;
4597 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4599 <<"You could try to delete it."<<std::endl;
4604 Try to get a block from the sector
4607 bool does_not_exist = false;
4608 bool lighting_expired = false;
4609 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4613 does_not_exist = true;
4615 else if(block->isDummy() == true)
4617 does_not_exist = true;
4619 else if(block->getLightingExpired())
4621 lighting_expired = true;
4626 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4631 If block was not found on disk and not going to generate a
4632 new one, make sure there is a dummy block in place.
4634 if(only_from_disk && (does_not_exist || lighting_expired))
4636 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4640 // Create dummy block
4641 block = new MapBlock(this, p, true);
4643 // Add block to sector
4644 sector->insertBlock(block);
4650 //dstream<<"Not found on disk, generating."<<std::endl;
4652 //TimeTaker("emergeBlock() generate");
4654 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4657 If the block doesn't exist, generate the block.
4661 block = generateBlock(p, block, sector, changed_blocks,
4662 lighting_invalidated_blocks);
4665 if(lighting_expired)
4667 lighting_invalidated_blocks.insert(p, block);
4671 Initially update sunlight
4675 core::map<v3s16, bool> light_sources;
4676 bool black_air_left = false;
4677 bool bottom_invalid =
4678 block->propagateSunlight(light_sources, true,
4679 &black_air_left, true);
4681 // If sunlight didn't reach everywhere and part of block is
4682 // above ground, lighting has to be properly updated
4683 //if(black_air_left && some_part_underground)
4686 lighting_invalidated_blocks[block->getPos()] = block;
4691 lighting_invalidated_blocks[block->getPos()] = block;
4698 s16 ServerMap::findGroundLevel(v2s16 p2d)
4701 Uh, just do something random...
4703 // Find existing map from top to down
4706 v3s16 p(p2d.X, max, p2d.Y);
4707 for(; p.Y>min; p.Y--)
4709 MapNode n = getNodeNoEx(p);
4710 if(n.d != CONTENT_IGNORE)
4715 // If this node is not air, go to plan b
4716 if(getNodeNoEx(p).d != CONTENT_AIR)
4718 // Search existing walkable and return it
4719 for(; p.Y>min; p.Y--)
4721 MapNode n = getNodeNoEx(p);
4722 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4728 Plan B: Get from map generator perlin noise function
4730 // This won't work if proper generation is disabled
4731 if(m_chunksize == 0)
4732 return WATER_LEVEL+2;
4733 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4737 void ServerMap::createDir(std::string path)
4739 if(fs::CreateDir(path) == false)
4741 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4742 <<"\""<<path<<"\""<<std::endl;
4743 throw BaseException("ServerMap failed to create directory");
4747 std::string ServerMap::getSectorSubDir(v2s16 pos)
4750 snprintf(cc, 9, "%.4x%.4x",
4751 (unsigned int)pos.X&0xffff,
4752 (unsigned int)pos.Y&0xffff);
4754 return std::string(cc);
4757 std::string ServerMap::getSectorDir(v2s16 pos)
4759 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4762 v2s16 ServerMap::getSectorPos(std::string dirname)
4764 if(dirname.size() != 8)
4765 throw InvalidFilenameException("Invalid sector directory name");
4767 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4769 throw InvalidFilenameException("Invalid sector directory name");
4770 v2s16 pos((s16)x, (s16)y);
4774 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4776 v2s16 p2d = getSectorPos(sectordir);
4778 if(blockfile.size() != 4){
4779 throw InvalidFilenameException("Invalid block filename");
4782 int r = sscanf(blockfile.c_str(), "%4x", &y);
4784 throw InvalidFilenameException("Invalid block filename");
4785 return v3s16(p2d.X, y, p2d.Y);
4788 void ServerMap::save(bool only_changed)
4790 DSTACK(__FUNCTION_NAME);
4791 if(m_map_saving_enabled == false)
4793 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4797 if(only_changed == false)
4798 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4801 if(only_changed == false || m_map_metadata_changed)
4804 m_map_metadata_changed = false;
4807 // Disable saving chunk metadata if chunks are disabled
4808 if(m_chunksize != 0)
4810 if(only_changed == false || anyChunkModified())
4814 u32 sector_meta_count = 0;
4815 u32 block_count = 0;
4818 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
4820 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4821 for(; i.atEnd() == false; i++)
4823 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4824 assert(sector->getId() == MAPSECTOR_SERVER);
4826 if(sector->differs_from_disk || only_changed == false)
4828 saveSectorMeta(sector);
4829 sector_meta_count++;
4831 core::list<MapBlock*> blocks;
4832 sector->getBlocks(blocks);
4833 core::list<MapBlock*>::Iterator j;
4834 for(j=blocks.begin(); j!=blocks.end(); j++)
4836 MapBlock *block = *j;
4837 if(block->getChangedFlag() || only_changed == false)
4842 /*dstream<<"ServerMap: Written block ("
4843 <<block->getPos().X<<","
4844 <<block->getPos().Y<<","
4845 <<block->getPos().Z<<")"
4854 Only print if something happened or saved whole map
4856 if(only_changed == false || sector_meta_count != 0
4857 || block_count != 0)
4859 dstream<<DTIME<<"ServerMap: Written: "
4860 <<sector_meta_count<<" sector metadata files, "
4861 <<block_count<<" block files"
4867 // NOTE: Doing this is insane. Deprecated and probably broken.
4868 void ServerMap::loadAll()
4870 DSTACK(__FUNCTION_NAME);
4871 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
4876 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
4878 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
4880 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
4883 s32 printed_counter = -100000;
4884 s32 count = list.size();
4886 std::vector<fs::DirListNode>::iterator i;
4887 for(i=list.begin(); i!=list.end(); i++)
4889 if(counter > printed_counter + 10)
4891 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
4892 printed_counter = counter;
4896 MapSector *sector = NULL;
4898 // We want directories
4902 sector = loadSectorMeta(i->name);
4904 catch(InvalidFilenameException &e)
4906 // This catches unknown crap in directory
4909 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4910 (m_savedir+"/sectors/"+i->name);
4911 std::vector<fs::DirListNode>::iterator i2;
4912 for(i2=list2.begin(); i2!=list2.end(); i2++)
4918 loadBlock(i->name, i2->name, sector);
4920 catch(InvalidFilenameException &e)
4922 // This catches unknown crap in directory
4926 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
4931 void ServerMap::saveMasterHeightmap()
4933 DSTACK(__FUNCTION_NAME);
4935 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4937 createDir(m_savedir);
4939 /*std::string fullpath = m_savedir + "/master_heightmap";
4940 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4941 if(o.good() == false)
4942 throw FileNotGoodException("Cannot open master heightmap");*/
4944 // Format used for writing
4945 //u8 version = SER_FMT_VER_HIGHEST;
4948 void ServerMap::loadMasterHeightmap()
4950 DSTACK(__FUNCTION_NAME);
4952 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
4954 /*std::string fullpath = m_savedir + "/master_heightmap";
4955 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4956 if(is.good() == false)
4957 throw FileNotGoodException("Cannot open master heightmap");*/
4961 void ServerMap::saveMapMeta()
4963 DSTACK(__FUNCTION_NAME);
4965 dstream<<"INFO: ServerMap::saveMapMeta(): "
4966 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
4969 createDir(m_savedir);
4971 std::string fullpath = m_savedir + "/map_meta.txt";
4972 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4973 if(os.good() == false)
4975 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4976 <<"could not open"<<fullpath<<std::endl;
4977 throw FileNotGoodException("Cannot open chunk metadata");
4981 params.setU64("seed", m_seed);
4982 params.setS32("chunksize", m_chunksize);
4984 params.writeLines(os);
4986 os<<"[end_of_params]\n";
4990 void ServerMap::loadMapMeta()
4992 DSTACK(__FUNCTION_NAME);
4994 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
4997 std::string fullpath = m_savedir + "/map_meta.txt";
4998 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4999 if(is.good() == false)
5001 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5002 <<"could not open"<<fullpath<<std::endl;
5003 throw FileNotGoodException("Cannot open map metadata");
5011 throw SerializationError
5012 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5014 std::getline(is, line);
5015 std::string trimmedline = trim(line);
5016 if(trimmedline == "[end_of_params]")
5018 params.parseConfigLine(line);
5021 m_seed = params.getU64("seed");
5022 m_chunksize = params.getS32("chunksize");
5024 dstream<<"INFO: ServerMap::loadMapMeta(): "
5025 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5029 void ServerMap::saveChunkMeta()
5031 DSTACK(__FUNCTION_NAME);
5033 // This should not be called if chunks are disabled.
5034 assert(m_chunksize != 0);
5036 u32 count = m_chunks.size();
5038 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5039 <<count<<" chunks"<<std::endl;
5041 createDir(m_savedir);
5043 std::string fullpath = m_savedir + "/chunk_meta";
5044 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5045 if(os.good() == false)
5047 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5048 <<"could not open"<<fullpath<<std::endl;
5049 throw FileNotGoodException("Cannot open chunk metadata");
5055 os.write((char*)&version, 1);
5060 writeU32(buf, count);
5061 os.write((char*)buf, 4);
5063 for(core::map<v2s16, MapChunk*>::Iterator
5064 i = m_chunks.getIterator();
5065 i.atEnd()==false; i++)
5067 v2s16 p = i.getNode()->getKey();
5068 MapChunk *chunk = i.getNode()->getValue();
5071 os.write((char*)buf, 4);
5073 chunk->serialize(os, version);
5077 void ServerMap::loadChunkMeta()
5079 DSTACK(__FUNCTION_NAME);
5081 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5084 std::string fullpath = m_savedir + "/chunk_meta";
5085 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5086 if(is.good() == false)
5088 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5089 <<"could not open"<<fullpath<<std::endl;
5090 throw FileNotGoodException("Cannot open chunk metadata");
5096 is.read((char*)&version, 1);
5101 is.read((char*)buf, 4);
5102 u32 count = readU32(buf);
5104 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5105 <<count<<" chunks"<<std::endl;
5107 for(u32 i=0; i<count; i++)
5110 MapChunk *chunk = new MapChunk();
5112 is.read((char*)buf, 4);
5115 chunk->deSerialize(is, version);
5116 m_chunks.insert(p, chunk);
5120 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5122 DSTACK(__FUNCTION_NAME);
5123 // Format used for writing
5124 u8 version = SER_FMT_VER_HIGHEST;
5126 v2s16 pos = sector->getPos();
5127 createDir(m_savedir);
5128 createDir(m_savedir+"/sectors");
5129 std::string dir = getSectorDir(pos);
5132 std::string fullpath = dir + "/meta";
5133 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5134 if(o.good() == false)
5135 throw FileNotGoodException("Cannot open sector metafile");
5137 sector->serialize(o, version);
5139 sector->differs_from_disk = false;
5142 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5144 DSTACK(__FUNCTION_NAME);
5146 v2s16 p2d = getSectorPos(dirname);
5147 std::string dir = m_savedir + "/sectors/" + dirname;
5149 ServerMapSector *sector = NULL;
5151 std::string fullpath = dir + "/meta";
5152 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5153 if(is.good() == false)
5155 // If the directory exists anyway, it probably is in some old
5156 // format. Just go ahead and create the sector.
5157 if(fs::PathExists(dir))
5159 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5160 <<fullpath<<" doesn't exist but directory does."
5161 <<" Continuing with a sector with no metadata."
5163 sector = new ServerMapSector(this, p2d);
5164 m_sectors.insert(p2d, sector);
5167 throw FileNotGoodException("Cannot open sector metafile");
5171 sector = ServerMapSector::deSerialize
5172 (is, this, p2d, m_sectors);
5175 sector->differs_from_disk = false;
5180 bool ServerMap::loadSectorFull(v2s16 p2d)
5182 DSTACK(__FUNCTION_NAME);
5183 std::string sectorsubdir = getSectorSubDir(p2d);
5185 MapSector *sector = NULL;
5187 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5190 sector = loadSectorMeta(sectorsubdir);
5192 catch(InvalidFilenameException &e)
5196 catch(FileNotGoodException &e)
5200 catch(std::exception &e)
5208 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5209 (m_savedir+"/sectors/"+sectorsubdir);
5210 std::vector<fs::DirListNode>::iterator i2;
5211 for(i2=list2.begin(); i2!=list2.end(); i2++)
5217 loadBlock(sectorsubdir, i2->name, sector);
5219 catch(InvalidFilenameException &e)
5221 // This catches unknown crap in directory
5227 void ServerMap::saveBlock(MapBlock *block)
5229 DSTACK(__FUNCTION_NAME);
5231 Dummy blocks are not written
5233 if(block->isDummy())
5235 /*v3s16 p = block->getPos();
5236 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5237 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5241 // Format used for writing
5242 u8 version = SER_FMT_VER_HIGHEST;
5244 v3s16 p3d = block->getPos();
5245 v2s16 p2d(p3d.X, p3d.Z);
5246 createDir(m_savedir);
5247 createDir(m_savedir+"/sectors");
5248 std::string dir = getSectorDir(p2d);
5251 // Block file is map/sectors/xxxxxxxx/xxxx
5253 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5254 std::string fullpath = dir + "/" + cc;
5255 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5256 if(o.good() == false)
5257 throw FileNotGoodException("Cannot open block data");
5260 [0] u8 serialization version
5263 o.write((char*)&version, 1);
5265 block->serialize(o, version);
5268 Versions up from 9 have block objects.
5272 block->serializeObjects(o, version);
5276 Versions up from 15 have static objects.
5280 block->m_static_objects.serialize(o);
5283 // We just wrote it to the disk
5284 block->resetChangedFlag();
5287 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5289 DSTACK(__FUNCTION_NAME);
5291 // Block file is map/sectors/xxxxxxxx/xxxx
5292 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5295 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5296 if(is.good() == false)
5297 throw FileNotGoodException("Cannot open block file");
5299 v3s16 p3d = getBlockPos(sectordir, blockfile);
5300 v2s16 p2d(p3d.X, p3d.Z);
5302 assert(sector->getPos() == p2d);
5304 u8 version = SER_FMT_VER_INVALID;
5305 is.read((char*)&version, 1);
5308 throw SerializationError("ServerMap::loadBlock(): Failed"
5309 " to read MapBlock version");
5311 /*u32 block_size = MapBlock::serializedLength(version);
5312 SharedBuffer<u8> data(block_size);
5313 is.read((char*)*data, block_size);*/
5315 // This will always return a sector because we're the server
5316 //MapSector *sector = emergeSector(p2d);
5318 MapBlock *block = NULL;
5319 bool created_new = false;
5321 block = sector->getBlockNoCreate(p3d.Y);
5323 catch(InvalidPositionException &e)
5325 block = sector->createBlankBlockNoInsert(p3d.Y);
5329 // deserialize block data
5330 block->deSerialize(is, version);
5333 Versions up from 9 have block objects.
5337 block->updateObjects(is, version, NULL, 0);
5341 Versions up from 15 have static objects.
5345 block->m_static_objects.deSerialize(is);
5349 sector->insertBlock(block);
5352 Convert old formats to new and save
5355 // Save old format blocks in new format
5356 if(version < SER_FMT_VER_HIGHEST)
5361 // We just loaded it from the disk, so it's up-to-date.
5362 block->resetChangedFlag();
5365 catch(SerializationError &e)
5367 dstream<<"WARNING: Invalid block data on disk "
5368 "(SerializationError). Ignoring. "
5369 "A new one will be generated."
5372 // TODO: Backup file; name is in fullpath.
5376 void ServerMap::PrintInfo(std::ostream &out)
5387 ClientMap::ClientMap(
5389 MapDrawControl &control,
5390 scene::ISceneNode* parent,
5391 scene::ISceneManager* mgr,
5395 scene::ISceneNode(parent, mgr, id),
5398 m_camera_position(0,0,0),
5399 m_camera_direction(0,0,1)
5401 m_camera_mutex.Init();
5402 assert(m_camera_mutex.IsInitialized());
5404 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5405 BS*1000000,BS*1000000,BS*1000000);
5408 ClientMap::~ClientMap()
5410 /*JMutexAutoLock lock(mesh_mutex);
5419 MapSector * ClientMap::emergeSector(v2s16 p2d)
5421 DSTACK(__FUNCTION_NAME);
5422 // Check that it doesn't exist already
5424 return getSectorNoGenerate(p2d);
5426 catch(InvalidPositionException &e)
5431 ClientMapSector *sector = new ClientMapSector(this, p2d);
5434 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5435 m_sectors.insert(p2d, sector);
5441 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5443 DSTACK(__FUNCTION_NAME);
5444 ClientMapSector *sector = NULL;
5446 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5448 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5452 sector = (ClientMapSector*)n->getValue();
5453 assert(sector->getId() == MAPSECTOR_CLIENT);
5457 sector = new ClientMapSector(this, p2d);
5459 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5460 m_sectors.insert(p2d, sector);
5464 sector->deSerialize(is);
5467 void ClientMap::OnRegisterSceneNode()
5471 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5472 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5475 ISceneNode::OnRegisterSceneNode();
5478 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5480 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5481 DSTACK(__FUNCTION_NAME);
5483 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5486 Get time for measuring timeout.
5488 Measuring time is very useful for long delays when the
5489 machine is swapping a lot.
5491 int time1 = time(0);
5493 //u32 daynight_ratio = m_client->getDayNightRatio();
5495 m_camera_mutex.Lock();
5496 v3f camera_position = m_camera_position;
5497 v3f camera_direction = m_camera_direction;
5498 m_camera_mutex.Unlock();
5501 Get all blocks and draw all visible ones
5504 v3s16 cam_pos_nodes(
5505 camera_position.X / BS,
5506 camera_position.Y / BS,
5507 camera_position.Z / BS);
5509 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5511 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5512 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5514 // Take a fair amount as we will be dropping more out later
5516 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5517 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5518 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5520 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5521 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5522 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5524 u32 vertex_count = 0;
5526 // For limiting number of mesh updates per frame
5527 u32 mesh_update_count = 0;
5529 u32 blocks_would_have_drawn = 0;
5530 u32 blocks_drawn = 0;
5532 //NOTE: The sectors map should be locked but we're not doing it
5533 // because it'd cause too much delays
5535 int timecheck_counter = 0;
5536 core::map<v2s16, MapSector*>::Iterator si;
5537 si = m_sectors.getIterator();
5538 for(; si.atEnd() == false; si++)
5541 timecheck_counter++;
5542 if(timecheck_counter > 50)
5544 timecheck_counter = 0;
5545 int time2 = time(0);
5546 if(time2 > time1 + 4)
5548 dstream<<"ClientMap::renderMap(): "
5549 "Rendering takes ages, returning."
5556 MapSector *sector = si.getNode()->getValue();
5557 v2s16 sp = sector->getPos();
5559 if(m_control.range_all == false)
5561 if(sp.X < p_blocks_min.X
5562 || sp.X > p_blocks_max.X
5563 || sp.Y < p_blocks_min.Z
5564 || sp.Y > p_blocks_max.Z)
5568 core::list< MapBlock * > sectorblocks;
5569 sector->getBlocks(sectorblocks);
5575 core::list< MapBlock * >::Iterator i;
5576 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5578 MapBlock *block = *i;
5581 Compare block position to camera position, skip
5582 if not seen on display
5585 float range = 100000 * BS;
5586 if(m_control.range_all == false)
5587 range = m_control.wanted_range * BS;
5590 if(isBlockInSight(block->getPos(), camera_position,
5591 camera_direction, range, &d) == false)
5596 // This is ugly (spherical distance limit?)
5597 /*if(m_control.range_all == false &&
5598 d - 0.5*BS*MAP_BLOCKSIZE > range)
5603 Update expired mesh (used for day/night change)
5605 It doesn't work exactly like it should now with the
5606 tasked mesh update but whatever.
5609 bool mesh_expired = false;
5612 JMutexAutoLock lock(block->mesh_mutex);
5614 mesh_expired = block->getMeshExpired();
5616 // Mesh has not been expired and there is no mesh:
5617 // block has no content
5618 if(block->mesh == NULL && mesh_expired == false)
5622 f32 faraway = BS*50;
5623 //f32 faraway = m_control.wanted_range * BS;
5626 This has to be done with the mesh_mutex unlocked
5628 // Pretty random but this should work somewhat nicely
5629 if(mesh_expired && (
5630 (mesh_update_count < 3
5631 && (d < faraway || mesh_update_count < 2)
5634 (m_control.range_all && mesh_update_count < 20)
5637 /*if(mesh_expired && mesh_update_count < 6
5638 && (d < faraway || mesh_update_count < 3))*/
5640 mesh_update_count++;
5642 // Mesh has been expired: generate new mesh
5643 //block->updateMesh(daynight_ratio);
5644 m_client->addUpdateMeshTask(block->getPos());
5646 mesh_expired = false;
5651 Draw the faces of the block
5654 JMutexAutoLock lock(block->mesh_mutex);
5656 scene::SMesh *mesh = block->mesh;
5661 blocks_would_have_drawn++;
5662 if(blocks_drawn >= m_control.wanted_max_blocks
5663 && m_control.range_all == false
5664 && d > m_control.wanted_min_range * BS)
5668 u32 c = mesh->getMeshBufferCount();
5670 for(u32 i=0; i<c; i++)
5672 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5673 const video::SMaterial& material = buf->getMaterial();
5674 video::IMaterialRenderer* rnd =
5675 driver->getMaterialRenderer(material.MaterialType);
5676 bool transparent = (rnd && rnd->isTransparent());
5677 // Render transparent on transparent pass and likewise.
5678 if(transparent == is_transparent_pass)
5681 This *shouldn't* hurt too much because Irrlicht
5682 doesn't change opengl textures if the old
5683 material is set again.
5685 driver->setMaterial(buf->getMaterial());
5686 driver->drawMeshBuffer(buf);
5687 vertex_count += buf->getVertexCount();
5691 } // foreach sectorblocks
5694 m_control.blocks_drawn = blocks_drawn;
5695 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5697 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5698 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5701 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5702 core::map<v3s16, MapBlock*> *affected_blocks)
5704 bool changed = false;
5706 Add it to all blocks touching it
5709 v3s16(0,0,0), // this
5710 v3s16(0,0,1), // back
5711 v3s16(0,1,0), // top
5712 v3s16(1,0,0), // right
5713 v3s16(0,0,-1), // front
5714 v3s16(0,-1,0), // bottom
5715 v3s16(-1,0,0), // left
5717 for(u16 i=0; i<7; i++)
5719 v3s16 p2 = p + dirs[i];
5720 // Block position of neighbor (or requested) node
5721 v3s16 blockpos = getNodeBlockPos(p2);
5722 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5723 if(blockref == NULL)
5725 // Relative position of requested node
5726 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5727 if(blockref->setTempMod(relpos, mod))
5732 if(changed && affected_blocks!=NULL)
5734 for(u16 i=0; i<7; i++)
5736 v3s16 p2 = p + dirs[i];
5737 // Block position of neighbor (or requested) node
5738 v3s16 blockpos = getNodeBlockPos(p2);
5739 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5740 if(blockref == NULL)
5742 affected_blocks->insert(blockpos, blockref);
5748 bool ClientMap::clearTempMod(v3s16 p,
5749 core::map<v3s16, MapBlock*> *affected_blocks)
5751 bool changed = false;
5753 v3s16(0,0,0), // this
5754 v3s16(0,0,1), // back
5755 v3s16(0,1,0), // top
5756 v3s16(1,0,0), // right
5757 v3s16(0,0,-1), // front
5758 v3s16(0,-1,0), // bottom
5759 v3s16(-1,0,0), // left
5761 for(u16 i=0; i<7; i++)
5763 v3s16 p2 = p + dirs[i];
5764 // Block position of neighbor (or requested) node
5765 v3s16 blockpos = getNodeBlockPos(p2);
5766 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5767 if(blockref == NULL)
5769 // Relative position of requested node
5770 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5771 if(blockref->clearTempMod(relpos))
5776 if(changed && affected_blocks!=NULL)
5778 for(u16 i=0; i<7; i++)
5780 v3s16 p2 = p + dirs[i];
5781 // Block position of neighbor (or requested) node
5782 v3s16 blockpos = getNodeBlockPos(p2);
5783 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5784 if(blockref == NULL)
5786 affected_blocks->insert(blockpos, blockref);
5792 void ClientMap::expireMeshes(bool only_daynight_diffed)
5794 TimeTaker timer("expireMeshes()");
5796 core::map<v2s16, MapSector*>::Iterator si;
5797 si = m_sectors.getIterator();
5798 for(; si.atEnd() == false; si++)
5800 MapSector *sector = si.getNode()->getValue();
5802 core::list< MapBlock * > sectorblocks;
5803 sector->getBlocks(sectorblocks);
5805 core::list< MapBlock * >::Iterator i;
5806 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5808 MapBlock *block = *i;
5810 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5816 JMutexAutoLock lock(block->mesh_mutex);
5817 if(block->mesh != NULL)
5819 /*block->mesh->drop();
5820 block->mesh = NULL;*/
5821 block->setMeshExpired(true);
5828 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5830 assert(mapType() == MAPTYPE_CLIENT);
5833 v3s16 p = blockpos + v3s16(0,0,0);
5834 MapBlock *b = getBlockNoCreate(p);
5835 b->updateMesh(daynight_ratio);
5836 //b->setMeshExpired(true);
5838 catch(InvalidPositionException &e){}
5841 v3s16 p = blockpos + v3s16(-1,0,0);
5842 MapBlock *b = getBlockNoCreate(p);
5843 b->updateMesh(daynight_ratio);
5844 //b->setMeshExpired(true);
5846 catch(InvalidPositionException &e){}
5848 v3s16 p = blockpos + v3s16(0,-1,0);
5849 MapBlock *b = getBlockNoCreate(p);
5850 b->updateMesh(daynight_ratio);
5851 //b->setMeshExpired(true);
5853 catch(InvalidPositionException &e){}
5855 v3s16 p = blockpos + v3s16(0,0,-1);
5856 MapBlock *b = getBlockNoCreate(p);
5857 b->updateMesh(daynight_ratio);
5858 //b->setMeshExpired(true);
5860 catch(InvalidPositionException &e){}
5865 Update mesh of block in which the node is, and if the node is at the
5866 leading edge, update the appropriate leading blocks too.
5868 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
5876 v3s16 blockposes[4];
5877 for(u32 i=0; i<4; i++)
5879 v3s16 np = nodepos + dirs[i];
5880 blockposes[i] = getNodeBlockPos(np);
5881 // Don't update mesh of block if it has been done already
5882 bool already_updated = false;
5883 for(u32 j=0; j<i; j++)
5885 if(blockposes[j] == blockposes[i])
5887 already_updated = true;
5894 MapBlock *b = getBlockNoCreate(blockposes[i]);
5895 b->updateMesh(daynight_ratio);
5900 void ClientMap::PrintInfo(std::ostream &out)
5911 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5916 MapVoxelManipulator::~MapVoxelManipulator()
5918 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5922 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5924 TimeTaker timer1("emerge", &emerge_time);
5926 // Units of these are MapBlocks
5927 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5928 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5930 VoxelArea block_area_nodes
5931 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5933 addArea(block_area_nodes);
5935 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5936 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5937 for(s32 x=p_min.X; x<=p_max.X; x++)
5940 core::map<v3s16, bool>::Node *n;
5941 n = m_loaded_blocks.find(p);
5945 bool block_data_inexistent = false;
5948 TimeTaker timer1("emerge load", &emerge_load_time);
5950 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5951 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5954 dstream<<std::endl;*/
5956 MapBlock *block = m_map->getBlockNoCreate(p);
5957 if(block->isDummy())
5958 block_data_inexistent = true;
5960 block->copyTo(*this);
5962 catch(InvalidPositionException &e)
5964 block_data_inexistent = true;
5967 if(block_data_inexistent)
5969 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5970 // Fill with VOXELFLAG_INEXISTENT
5971 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5972 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5974 s32 i = m_area.index(a.MinEdge.X,y,z);
5975 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5979 m_loaded_blocks.insert(p, !block_data_inexistent);
5982 //dstream<<"emerge done"<<std::endl;
5986 SUGG: Add an option to only update eg. water and air nodes.
5987 This will make it interfere less with important stuff if
5990 void MapVoxelManipulator::blitBack
5991 (core::map<v3s16, MapBlock*> & modified_blocks)
5993 if(m_area.getExtent() == v3s16(0,0,0))
5996 //TimeTaker timer1("blitBack");
5998 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5999 <<m_loaded_blocks.size()<<std::endl;*/
6002 Initialize block cache
6004 v3s16 blockpos_last;
6005 MapBlock *block = NULL;
6006 bool block_checked_in_modified = false;
6008 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6009 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6010 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6014 u8 f = m_flags[m_area.index(p)];
6015 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6018 MapNode &n = m_data[m_area.index(p)];
6020 v3s16 blockpos = getNodeBlockPos(p);
6025 if(block == NULL || blockpos != blockpos_last){
6026 block = m_map->getBlockNoCreate(blockpos);
6027 blockpos_last = blockpos;
6028 block_checked_in_modified = false;
6031 // Calculate relative position in block
6032 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6034 // Don't continue if nothing has changed here
6035 if(block->getNode(relpos) == n)
6038 //m_map->setNode(m_area.MinEdge + p, n);
6039 block->setNode(relpos, n);
6042 Make sure block is in modified_blocks
6044 if(block_checked_in_modified == false)
6046 modified_blocks[blockpos] = block;
6047 block_checked_in_modified = true;
6050 catch(InvalidPositionException &e)
6056 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6057 MapVoxelManipulator(map),
6058 m_create_area(false)
6062 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6066 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6068 // Just create the area so that it can be pointed to
6069 VoxelManipulator::emerge(a, caller_id);
6072 void ManualMapVoxelManipulator::initialEmerge(
6073 v3s16 blockpos_min, v3s16 blockpos_max)
6075 TimeTaker timer1("initialEmerge", &emerge_time);
6077 // Units of these are MapBlocks
6078 v3s16 p_min = blockpos_min;
6079 v3s16 p_max = blockpos_max;
6081 VoxelArea block_area_nodes
6082 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6084 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6087 dstream<<"initialEmerge: area: ";
6088 block_area_nodes.print(dstream);
6089 dstream<<" ("<<size_MB<<"MB)";
6093 addArea(block_area_nodes);
6095 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6096 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6097 for(s32 x=p_min.X; x<=p_max.X; x++)
6100 core::map<v3s16, bool>::Node *n;
6101 n = m_loaded_blocks.find(p);
6105 bool block_data_inexistent = false;
6108 TimeTaker timer1("emerge load", &emerge_load_time);
6110 MapBlock *block = m_map->getBlockNoCreate(p);
6111 if(block->isDummy())
6112 block_data_inexistent = true;
6114 block->copyTo(*this);
6116 catch(InvalidPositionException &e)
6118 block_data_inexistent = true;
6121 if(block_data_inexistent)
6124 Mark area inexistent
6126 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6127 // Fill with VOXELFLAG_INEXISTENT
6128 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6129 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6131 s32 i = m_area.index(a.MinEdge.X,y,z);
6132 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6136 m_loaded_blocks.insert(p, !block_data_inexistent);
6140 void ManualMapVoxelManipulator::blitBackAll(
6141 core::map<v3s16, MapBlock*> * modified_blocks)
6143 if(m_area.getExtent() == v3s16(0,0,0))
6147 Copy data of all blocks
6149 for(core::map<v3s16, bool>::Iterator
6150 i = m_loaded_blocks.getIterator();
6151 i.atEnd() == false; i++)
6153 bool existed = i.getNode()->getValue();
6154 if(existed == false)
6156 v3s16 p = i.getNode()->getKey();
6157 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6160 dstream<<"WARNING: "<<__FUNCTION_NAME
6161 <<": got NULL block "
6162 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6167 block->copyFrom(*this);
6170 modified_blocks->insert(p, block);