3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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;
2052 /*// -1->0, 0->1, 1->0
2053 double contour(double v)
2062 Noise functions. Make sure seed is mangled differently in each one.
2065 // This affects the shape of the contour
2066 #define CAVE_NOISE_SCALE 10.0
2068 NoiseParams get_cave_noise1_params(u64 seed)
2070 return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.7,
2071 200, CAVE_NOISE_SCALE);
2074 NoiseParams get_cave_noise2_params(u64 seed)
2076 return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.7,
2077 200, CAVE_NOISE_SCALE);
2080 #define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE)
2082 bool is_cave(u64 seed, v3s16 p)
2084 double d1 = noise3d_param(get_cave_noise1_params(seed), p.X,p.Y,p.Z);
2085 double d2 = noise3d_param(get_cave_noise2_params(seed), p.X,p.Y,p.Z);
2086 return d1*d2 > CAVE_NOISE_THRESHOLD;
2089 // Amount of trees per area in nodes
2090 double tree_amount_2d(u64 seed, v2s16 p)
2092 double noise = noise2d_perlin(
2093 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2095 double zeroval = -0.3;
2099 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2102 #define AVERAGE_MUD_AMOUNT 4
2104 double base_rock_level_2d(u64 seed, v2s16 p)
2106 // The base ground level
2107 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2108 + 20. * noise2d_perlin(
2109 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2110 (seed>>32)+654879876, 6, 0.6);
2112 /*// A bit hillier one
2113 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2114 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2115 (seed>>27)+90340, 6, 0.69);
2119 // Higher ground level
2120 double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin(
2121 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2122 seed+85039, 5, 0.69);
2123 //higher = 30; // For debugging
2125 // Limit higher to at least base
2129 // Steepness factor of cliffs
2130 double b = 1.0 + 1.0 * noise2d_perlin(
2131 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2133 b = rangelim(b, 0.0, 1000.0);
2136 b = rangelim(b, 3.0, 1000.0);
2137 //dstream<<"b="<<b<<std::endl;
2140 // Offset to more low
2141 double a_off = -0.2;
2142 // High/low selector
2143 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2144 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2145 seed-359, 6, 0.7));*/
2146 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2147 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2148 seed-359, 5, 0.60));
2150 a = rangelim(a, 0.0, 1.0);
2152 //dstream<<"a="<<a<<std::endl;
2154 double h = base*(1.0-a) + higher*a;
2161 double get_mud_add_amount(u64 seed, v2s16 p)
2163 return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
2164 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
2165 seed+91013, 3, 0.55));
2169 Adds random objects to block, depending on the content of the block
2171 void addRandomObjects(MapBlock *block)
2173 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2174 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2176 bool last_node_walkable = false;
2177 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2180 MapNode n = block->getNodeNoEx(p);
2181 if(n.d == CONTENT_IGNORE)
2183 if(content_features(n.d).liquid_type != LIQUID_NONE)
2185 if(content_features(n.d).walkable)
2187 last_node_walkable = true;
2190 if(last_node_walkable)
2192 // If block contains light information
2193 if(content_features(n.d).param_type == CPT_LIGHT)
2195 if(n.getLight(LIGHTBANK_DAY) <= 3)
2197 if(myrand() % 300 == 0)
2199 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2201 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
2202 std::string data = obj->getStaticData();
2203 StaticObject s_obj(obj->getType(),
2204 obj->getBasePosition(), data);
2206 block->m_static_objects.insert(0, s_obj);
2207 block->m_static_objects.insert(0, s_obj);
2208 block->m_static_objects.insert(0, s_obj);
2209 block->m_static_objects.insert(0, s_obj);
2210 block->m_static_objects.insert(0, s_obj);
2211 block->m_static_objects.insert(0, s_obj);
2214 if(myrand() % 300 == 0)
2216 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2218 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
2219 std::string data = obj->getStaticData();
2220 StaticObject s_obj(obj->getType(),
2221 obj->getBasePosition(), data);
2223 block->m_static_objects.insert(0, s_obj);
2229 last_node_walkable = false;
2232 block->setChangedFlag();
2236 This is the main map generation method
2239 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2241 void makeChunk(ChunkMakeData *data)
2246 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2247 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2248 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2249 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2250 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2251 *(u32)h_blocks*MAP_BLOCKSIZE;
2252 v3s16 bigarea_blocks_min(
2253 data->sectorpos_bigbase.X,
2255 data->sectorpos_bigbase.Y
2257 v3s16 bigarea_blocks_max(
2258 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2260 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2262 s16 lighting_min_d = 0-data->max_spread_amount;
2263 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2264 + data->max_spread_amount-1;
2267 data->vmanip.clearFlag(0xff);
2269 TimeTaker timer_generate("makeChunk() generate");
2271 // Maximum height of the stone surface and obstacles.
2272 // This is used to disable cave generation from going too high.
2273 s16 stone_surface_max_y = 0;
2276 Generate general ground level to full area
2280 TimeTaker timer1("Generating ground level");
2282 //NoiseBuffer noisebuf1;
2283 //NoiseBuffer noisebuf2;
2284 NoiseBuffer noisebuf_cave;
2287 data->sectorpos_bigbase.X*MAP_BLOCKSIZE,
2289 data->sectorpos_bigbase.Y*MAP_BLOCKSIZE
2291 v3f maxpos_f = minpos_f + v3f(
2292 data->sectorpos_bigbase_size*MAP_BLOCKSIZE,
2293 y_nodes_max-y_nodes_min,
2294 data->sectorpos_bigbase_size*MAP_BLOCKSIZE
2296 //v3f samplelength_f = v3f(4.0, 4.0, 4.0);
2298 TimeTaker timer("noisebuf.create");
2300 noisebuf_cave.create(get_cave_noise1_params(data->seed),
2301 minpos_f.X, minpos_f.Y, minpos_f.Z,
2302 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2305 noisebuf_cave.multiply(get_cave_noise2_params(data->seed));
2307 /*noisebuf1.create(data->seed+25104, 6, 0.60, 200.0, false,
2308 minpos_f.X, minpos_f.Y, minpos_f.Z,
2309 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2310 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);*/
2311 /*noisebuf1.create(data->seed+25104, 3, 0.60, 25.0, false,
2312 minpos_f.X, minpos_f.Y, minpos_f.Z,
2313 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2314 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2315 noisebuf2.create(data->seed+25105, 4, 0.50, 200.0, false,
2316 minpos_f.X, minpos_f.Y, minpos_f.Z,
2317 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2318 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);*/
2322 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2323 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2326 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2328 // Ground height at this point
2329 float surface_y_f = 0.0;
2331 // Use perlin noise for ground height
2332 surface_y_f = base_rock_level_2d(data->seed, p2d);
2333 //surface_y_f = base_rock_level_2d(data->seed, p2d);
2335 // Convert to integer
2336 s16 surface_y = (s16)surface_y_f;
2339 if(surface_y > stone_surface_max_y)
2340 stone_surface_max_y = surface_y;
2343 Fill ground with stone
2346 // Use fast index incrementing
2347 v3s16 em = data->vmanip.m_area.getExtent();
2348 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2349 for(s16 y=y_nodes_min; y<=y_nodes_max; y++)
2351 // Skip if already generated.
2352 // This is done here because there might be a cave at
2353 // any point in ground, which could look like it
2354 // wasn't generated.
2355 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2358 /*s16 noiseval = 50.0 * noise3d_perlin(
2359 0.5+(float)p2d.X/100.0,
2361 0.5+(float)p2d.Y/100.0,
2362 data->seed+123, 5, 0.5);*/
2363 double noiseval = 64.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2364 /*double noiseval = 30.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2365 noiseval *= MYMAX(0, -0.2 + noisebuf2.get(p2d.X, y, p2d.Y));*/
2367 //if(y < surface_y + noiseval)
2370 data->vmanip.m_data[i].d = CONTENT_STONE;
2372 data->vmanip.m_area.add_y(em, i, 1);
2379 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2380 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2383 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2386 Skip of already generated
2389 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2390 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2398 Fill ground with stone
2401 // Use fast index incrementing
2402 v3s16 em = data->vmanip.m_area.getExtent();
2403 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2404 for(s16 y=y_nodes_min; y<=y_nodes_max; y++)
2406 // Skip if already generated.
2407 // This is done here because there might be a cave at
2408 // any point in ground, which could look like it
2409 // wasn't generated.
2410 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2413 //if(is_cave(data->seed, v3s16(p2d.X, y, p2d.Y)))
2414 if(noisebuf_cave.get(p2d.X,y,p2d.Y) > CAVE_NOISE_THRESHOLD)
2416 data->vmanip.m_data[i].d = CONTENT_STONE;
2420 data->vmanip.m_data[i].d = CONTENT_AIR;
2423 data->vmanip.m_area.add_y(em, i, 1);
2427 // Ground height at this point
2428 float surface_y_f = 0.0;
2430 // Use perlin noise for ground height
2431 surface_y_f = base_rock_level_2d(data->seed, p2d);
2433 // Convert to integer
2434 s16 surface_y = (s16)surface_y_f;
2437 if(surface_y > stone_surface_max_y)
2438 stone_surface_max_y = surface_y;
2441 Fill ground with stone
2444 // Use fast index incrementing
2445 v3s16 em = data->vmanip.m_area.getExtent();
2446 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2447 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2449 // Skip if already generated.
2450 // This is done here because there might be a cave at
2451 // any point in ground, which could look like it
2452 // wasn't generated.
2453 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2456 //if(is_cave(data->seed, v3s16(p2d.X, y, p2d.Y)))
2457 if(noisebuf_cave.get(p2d.X,y,p2d.Y) > CAVE_NOISE_THRESHOLD)
2460 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2462 data->vmanip.m_data[i].d = CONTENT_AIR;
2466 data->vmanip.m_data[i].d = CONTENT_STONE;
2469 data->vmanip.m_area.add_y(em, i, 1);
2472 #endif // !CAVE_TEST
2479 Randomize some parameters
2482 //s32 stone_obstacle_count = 0;
2483 /*s32 stone_obstacle_count =
2484 rangelim((1.0+noise2d(data->seed+897,
2485 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2487 //s16 stone_obstacle_max_height = 0;
2488 /*s16 stone_obstacle_max_height =
2489 rangelim((1.0+noise2d(data->seed+5902,
2490 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2492 // Set to 0 to disable everything but lighting
2496 Loop this part, it will make stuff look older and newer nicely
2498 const u32 age_loops = 2;
2499 for(u32 i_age=0; i_age<age_loops; i_age++)
2501 /******************************
2502 BEGINNING OF AGING LOOP
2503 ******************************/
2508 //TimeTaker timer1("caves");
2513 //u32 caves_count = relative_volume / 400000;
2514 u32 caves_count = 0;
2515 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2516 if(stone_surface_max_y < WATER_LEVEL)
2518 /*u32 caves_count = 0;
2519 u32 bruises_count = 0;*/
2520 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2522 s16 min_tunnel_diameter = 3;
2523 s16 max_tunnel_diameter = 5;
2524 u16 tunnel_routepoints = 20;
2526 v3f main_direction(0,0,0);
2528 bool bruise_surface = (jj > caves_count);
2532 min_tunnel_diameter = 5;
2533 max_tunnel_diameter = myrand_range(10, 20);
2534 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2535 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2537 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2538 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2540 tunnel_routepoints = 5;
2546 // Allowed route area size in nodes
2548 data->sectorpos_base_size*MAP_BLOCKSIZE,
2549 h_blocks*MAP_BLOCKSIZE,
2550 data->sectorpos_base_size*MAP_BLOCKSIZE
2553 // Area starting point in nodes
2555 data->sectorpos_base.X*MAP_BLOCKSIZE,
2556 data->y_blocks_min*MAP_BLOCKSIZE,
2557 data->sectorpos_base.Y*MAP_BLOCKSIZE
2561 //(this should be more than the maximum radius of the tunnel)
2562 //s16 insure = 5; // Didn't work with max_d = 20
2564 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2565 ar += v3s16(1,0,1) * more * 2;
2566 of -= v3s16(1,0,1) * more;
2568 s16 route_y_min = 0;
2569 // Allow half a diameter + 7 over stone surface
2570 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2572 /*// If caves, don't go through surface too often
2573 if(bruise_surface == false)
2574 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2576 // Limit maximum to area
2577 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2581 /*// Minimum is at y=0
2582 route_y_min = -of.Y - 0;*/
2583 // Minimum is at y=max_tunnel_diameter/4
2584 //route_y_min = -of.Y + max_tunnel_diameter/4;
2585 //s16 min = -of.Y + max_tunnel_diameter/4;
2586 s16 min = -of.Y + 0;
2587 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2588 route_y_min = rangelim(route_y_min, 0, route_y_max);
2591 /*dstream<<"route_y_min = "<<route_y_min
2592 <<", route_y_max = "<<route_y_max<<std::endl;*/
2594 s16 route_start_y_min = route_y_min;
2595 s16 route_start_y_max = route_y_max;
2597 // Start every 2nd cave from surface
2598 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2600 if(coming_from_surface)
2602 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2605 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2606 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2608 // Randomize starting position
2610 (float)(myrand()%ar.X)+0.5,
2611 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2612 (float)(myrand()%ar.Z)+0.5
2615 MapNode airnode(CONTENT_AIR);
2618 Generate some tunnel starting from orp
2621 for(u16 j=0; j<tunnel_routepoints; j++)
2623 if(j%7==0 && bruise_surface == false)
2625 main_direction = v3f(
2626 ((float)(myrand()%20)-(float)10)/10,
2627 ((float)(myrand()%20)-(float)10)/30,
2628 ((float)(myrand()%20)-(float)10)/10
2630 main_direction *= (float)myrand_range(1, 3);
2634 s16 min_d = min_tunnel_diameter;
2635 s16 max_d = max_tunnel_diameter;
2636 s16 rs = myrand_range(min_d, max_d);
2641 maxlen = v3s16(rs*7,rs*7,rs*7);
2645 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2650 if(coming_from_surface && j < 3)
2653 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2654 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2655 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2661 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2662 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2663 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2667 vec += main_direction;
2672 else if(rp.X >= ar.X)
2674 if(rp.Y < route_y_min)
2676 else if(rp.Y >= route_y_max)
2677 rp.Y = route_y_max-1;
2680 else if(rp.Z >= ar.Z)
2684 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2686 v3f fp = orp + vec * f;
2687 v3s16 cp(fp.X, fp.Y, fp.Z);
2690 s16 d1 = d0 + rs - 1;
2691 for(s16 z0=d0; z0<=d1; z0++)
2693 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2694 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2695 for(s16 x0=-si; x0<=si-1; x0++)
2697 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2698 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2699 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2700 //s16 si2 = rs - abs(x0);
2701 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2707 /*if(isInArea(p, ar) == false)
2709 // Check only height
2710 if(y < 0 || y >= ar.Y)
2714 //assert(data->vmanip.m_area.contains(p));
2715 if(data->vmanip.m_area.contains(p) == false)
2717 dstream<<"WARNING: "<<__FUNCTION_NAME
2718 <<":"<<__LINE__<<": "
2719 <<"point not in area"
2724 // Just set it to air, it will be changed to
2726 u32 i = data->vmanip.m_area.index(p);
2727 data->vmanip.m_data[i] = airnode;
2729 if(bruise_surface == false)
2732 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2750 //TimeTaker timer1("ore veins");
2755 for(u32 jj=0; jj<relative_volume/1000; jj++)
2757 s16 max_vein_diameter = 3;
2759 // Allowed route area size in nodes
2761 data->sectorpos_base_size*MAP_BLOCKSIZE,
2762 h_blocks*MAP_BLOCKSIZE,
2763 data->sectorpos_base_size*MAP_BLOCKSIZE
2766 // Area starting point in nodes
2768 data->sectorpos_base.X*MAP_BLOCKSIZE,
2769 data->y_blocks_min*MAP_BLOCKSIZE,
2770 data->sectorpos_base.Y*MAP_BLOCKSIZE
2774 //(this should be more than the maximum radius of the tunnel)
2776 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2777 ar += v3s16(1,0,1) * more * 2;
2778 of -= v3s16(1,0,1) * more;
2780 // Randomize starting position
2782 (float)(myrand()%ar.X)+0.5,
2783 (float)(myrand()%ar.Y)+0.5,
2784 (float)(myrand()%ar.Z)+0.5
2787 // Randomize mineral
2790 mineral = MINERAL_COAL;
2792 mineral = MINERAL_IRON;
2795 Generate some vein starting from orp
2798 for(u16 j=0; j<2; j++)
2801 (float)(myrand()%ar.X)+0.5,
2802 (float)(myrand()%ar.Y)+0.5,
2803 (float)(myrand()%ar.Z)+0.5
2805 v3f vec = rp - orp;*/
2807 v3s16 maxlen(5, 5, 5);
2809 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2810 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2811 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2816 else if(rp.X >= ar.X)
2820 else if(rp.Y >= ar.Y)
2824 else if(rp.Z >= ar.Z)
2830 s16 max_d = max_vein_diameter;
2831 s16 rs = myrand_range(min_d, max_d);
2833 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2835 v3f fp = orp + vec * f;
2836 v3s16 cp(fp.X, fp.Y, fp.Z);
2838 s16 d1 = d0 + rs - 1;
2839 for(s16 z0=d0; z0<=d1; z0++)
2841 s16 si = rs - abs(z0);
2842 for(s16 x0=-si; x0<=si-1; x0++)
2844 s16 si2 = rs - abs(x0);
2845 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2847 // Don't put mineral to every place
2855 /*if(isInArea(p, ar) == false)
2857 // Check only height
2858 if(y < 0 || y >= ar.Y)
2862 assert(data->vmanip.m_area.contains(p));
2864 // Just set it to air, it will be changed to
2866 u32 i = data->vmanip.m_area.index(p);
2867 MapNode *n = &data->vmanip.m_data[i];
2868 if(n->d == CONTENT_STONE)
2886 TimeTaker timer1("add mud");
2889 Add mud to the central chunk
2892 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2893 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2895 // Node position in 2d
2896 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2898 // Randomize mud amount
2899 s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0;
2901 // Find ground level
2902 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2905 If topmost node is grass, change it to mud.
2906 It might be if it was flown to there from a neighboring
2907 chunk and then converted.
2910 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2911 MapNode *n = &data->vmanip.m_data[i];
2912 if(n->d == CONTENT_GRASS)
2913 *n = MapNode(CONTENT_MUD);
2914 //n->d = CONTENT_MUD;
2922 v3s16 em = data->vmanip.m_area.getExtent();
2923 s16 y_start = surface_y+1;
2924 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2925 for(s16 y=y_start; y<=y_nodes_max; y++)
2927 if(mudcount >= mud_add_amount)
2930 MapNode &n = data->vmanip.m_data[i];
2931 n = MapNode(CONTENT_MUD);
2932 //n.d = CONTENT_MUD;
2935 data->vmanip.m_area.add_y(em, i, 1);
2947 TimeTaker timer1("flow mud");
2950 Flow mud away from steep edges
2953 // Limit area by 1 because mud is flown into neighbors.
2954 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2955 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2957 // Iterate a few times
2958 for(s16 k=0; k<3; k++)
2961 for(s16 x=mudflow_minpos;
2964 for(s16 z=mudflow_minpos;
2968 // Invert coordinates every 2nd iteration
2971 x = mudflow_maxpos - (x-mudflow_minpos);
2972 z = mudflow_maxpos - (z-mudflow_minpos);
2975 // Node position in 2d
2976 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2978 v3s16 em = data->vmanip.m_area.getExtent();
2979 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2986 for(; y>=y_nodes_min; y--)
2988 n = &data->vmanip.m_data[i];
2989 //if(content_walkable(n->d))
2991 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2994 data->vmanip.m_area.add_y(em, i, -1);
2997 // Stop if out of area
2998 //if(data->vmanip.m_area.contains(i) == false)
3002 /*// If not mud, do nothing to it
3003 MapNode *n = &data->vmanip.m_data[i];
3004 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3008 Don't flow it if the stuff under it is not mud
3012 data->vmanip.m_area.add_y(em, i2, -1);
3013 // Cancel if out of area
3014 if(data->vmanip.m_area.contains(i2) == false)
3016 MapNode *n2 = &data->vmanip.m_data[i2];
3017 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
3021 // Make it exactly mud
3024 /*s16 recurse_count = 0;
3028 v3s16(0,0,1), // back
3029 v3s16(1,0,0), // right
3030 v3s16(0,0,-1), // front
3031 v3s16(-1,0,0), // left
3034 // Theck that upper is air or doesn't exist.
3035 // Cancel dropping if upper keeps it in place
3037 data->vmanip.m_area.add_y(em, i3, 1);
3038 if(data->vmanip.m_area.contains(i3) == true
3039 && content_walkable(data->vmanip.m_data[i3].d) == true)
3046 for(u32 di=0; di<4; di++)
3048 v3s16 dirp = dirs4[di];
3051 data->vmanip.m_area.add_p(em, i2, dirp);
3052 // Fail if out of area
3053 if(data->vmanip.m_area.contains(i2) == false)
3055 // Check that side is air
3056 MapNode *n2 = &data->vmanip.m_data[i2];
3057 if(content_walkable(n2->d))
3059 // Check that under side is air
3060 data->vmanip.m_area.add_y(em, i2, -1);
3061 if(data->vmanip.m_area.contains(i2) == false)
3063 n2 = &data->vmanip.m_data[i2];
3064 if(content_walkable(n2->d))
3066 /*// Check that under that is air (need a drop of 2)
3067 data->vmanip.m_area.add_y(em, i2, -1);
3068 if(data->vmanip.m_area.contains(i2) == false)
3070 n2 = &data->vmanip.m_data[i2];
3071 if(content_walkable(n2->d))
3073 // Loop further down until not air
3075 data->vmanip.m_area.add_y(em, i2, -1);
3076 // Fail if out of area
3077 if(data->vmanip.m_area.contains(i2) == false)
3079 n2 = &data->vmanip.m_data[i2];
3080 }while(content_walkable(n2->d) == false);
3081 // Loop one up so that we're in air
3082 data->vmanip.m_area.add_y(em, i2, 1);
3083 n2 = &data->vmanip.m_data[i2];
3085 // Move mud to new place
3087 // Set old place to be air
3088 *n = MapNode(CONTENT_AIR);
3104 TimeTaker timer1("add water");
3107 Add water to the central chunk (and a bit more)
3110 for(s16 x=0-data->max_spread_amount;
3111 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3113 for(s16 z=0-data->max_spread_amount;
3114 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3117 // Node position in 2d
3118 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3120 // Find ground level
3121 //s16 surface_y = find_ground_level(data->vmanip, p2d);
3124 If ground level is over water level, skip.
3125 NOTE: This leaves caves near water without water,
3126 which looks especially crappy when the nearby water
3127 won't start flowing either for some reason
3129 /*if(surface_y > WATER_LEVEL)
3136 v3s16 em = data->vmanip.m_area.getExtent();
3137 u8 light = LIGHT_MAX;
3138 // Start at global water surface level
3139 s16 y_start = WATER_LEVEL;
3140 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3141 MapNode *n = &data->vmanip.m_data[i];
3143 for(s16 y=y_start; y>=y_nodes_min; y--)
3145 n = &data->vmanip.m_data[i];
3147 // Stop when there is no water and no air
3148 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3149 && n->d != CONTENT_WATER)
3155 // Make water only not in caves
3156 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3158 n->d = CONTENT_WATERSOURCE;
3159 //n->setLight(LIGHTBANK_DAY, light);
3161 // Add to transforming liquid queue (in case it'd
3163 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3164 data->transforming_liquid.push_back(p);
3168 data->vmanip.m_area.add_y(em, i, -1);
3180 /***********************
3182 ************************/
3186 //TimeTaker timer1("convert mud to sand");
3192 //s16 mud_add_amount = myrand_range(2, 4);
3193 //s16 mud_add_amount = 0;
3195 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3196 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3197 for(s16 x=0-data->max_spread_amount+1;
3198 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3200 for(s16 z=0-data->max_spread_amount+1;
3201 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3204 // Node position in 2d
3205 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3207 // Determine whether to have sand here
3208 double sandnoise = noise2d_perlin(
3209 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3210 data->seed+59420, 3, 0.50);
3212 bool have_sand = (sandnoise > -0.15);
3214 if(have_sand == false)
3217 // Find ground level
3218 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3220 if(surface_y > WATER_LEVEL + 2)
3224 v3s16 em = data->vmanip.m_area.getExtent();
3225 s16 y_start = surface_y;
3226 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3227 u32 not_sand_counter = 0;
3228 for(s16 y=y_start; y>=y_nodes_min; y--)
3230 MapNode *n = &data->vmanip.m_data[i];
3231 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3233 n->d = CONTENT_SAND;
3238 if(not_sand_counter > 3)
3242 data->vmanip.m_area.add_y(em, i, -1);
3254 //TimeTaker timer1("generate trees");
3260 // Divide area into parts
3262 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3263 double area = sidelen * sidelen;
3264 for(s16 x0=0; x0<div; x0++)
3265 for(s16 z0=0; z0<div; z0++)
3267 // Center position of part of division
3269 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3270 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3272 // Minimum edge of part of division
3274 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3275 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3277 // Maximum edge of part of division
3279 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3280 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3283 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3284 // Put trees in random places on part of division
3285 for(u32 i=0; i<tree_count; i++)
3287 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3288 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3289 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3290 // Don't make a tree under water level
3293 // Don't make a tree so high that it doesn't fit
3294 if(y > y_nodes_max - 6)
3298 Trees grow only on mud and grass
3301 u32 i = data->vmanip.m_area.index(v3s16(p));
3302 MapNode *n = &data->vmanip.m_data[i];
3303 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3308 make_tree(data->vmanip, p);
3311 /*u32 tree_max = relative_area / 60;
3312 //u32 count = myrand_range(0, tree_max);
3313 for(u32 i=0; i<count; i++)
3315 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3316 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3317 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3318 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3319 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3320 // Don't make a tree under water level
3325 make_tree(data->vmanip, p);
3335 //TimeTaker timer1("grow grass");
3341 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3342 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3343 for(s16 x=0-data->max_spread_amount;
3344 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3346 for(s16 z=0-data->max_spread_amount;
3347 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3350 // Node position in 2d
3351 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3354 Find the lowest surface to which enough light ends up
3357 Basically just wait until not air and not leaves.
3361 v3s16 em = data->vmanip.m_area.getExtent();
3362 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3364 // Go to ground level
3365 for(y=y_nodes_max; y>=y_nodes_min; y--)
3367 MapNode &n = data->vmanip.m_data[i];
3368 if(n.d != CONTENT_AIR
3369 && n.d != CONTENT_LEAVES)
3371 data->vmanip.m_area.add_y(em, i, -1);
3373 if(y >= y_nodes_min)
3376 surface_y = y_nodes_min;
3379 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3380 MapNode *n = &data->vmanip.m_data[i];
3381 if(n->d == CONTENT_MUD)
3382 n->d = CONTENT_GRASS;
3388 #endif // Disable all but lighting
3391 Initial lighting (sunlight)
3394 core::map<v3s16, bool> light_sources;
3397 // 750ms @cs=8, can't optimize more
3398 TimeTaker timer1("initial lighting");
3400 // NOTE: This is no used... umm... for some reason!
3403 Go through the edges and add all nodes that have light to light_sources
3407 for(s16 i=0; i<4; i++)
3409 for(s16 j=lighting_min_d;
3416 if(i == 0 || i == 1)
3418 x = (i==0) ? lighting_min_d : lighting_max_d;
3427 z = (i==0) ? lighting_min_d : lighting_max_d;
3434 // Node position in 2d
3435 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3438 v3s16 em = data->vmanip.m_area.getExtent();
3439 s16 y_start = y_nodes_max;
3440 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3441 for(s16 y=y_start; y>=y_nodes_min; y--)
3443 MapNode *n = &data->vmanip.m_data[i];
3444 if(n->getLight(LIGHTBANK_DAY) != 0)
3446 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3448 //NOTE: This is broken, at least the index has to
3457 Go through the edges and apply sunlight to them, not caring
3462 for(s16 i=0; i<4; i++)
3464 for(s16 j=lighting_min_d;
3471 if(i == 0 || i == 1)
3473 x = (i==0) ? lighting_min_d : lighting_max_d;
3482 z = (i==0) ? lighting_min_d : lighting_max_d;
3489 // Node position in 2d
3490 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3492 // Loop from top to down
3494 u8 light = LIGHT_SUN;
3495 v3s16 em = data->vmanip.m_area.getExtent();
3496 s16 y_start = y_nodes_max;
3497 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3498 for(s16 y=y_start; y>=y_nodes_min; y--)
3500 MapNode *n = &data->vmanip.m_data[i];
3501 if(light_propagates_content(n->d) == false)
3505 else if(light != LIGHT_SUN
3506 || sunlight_propagates_content(n->d) == false)
3512 n->setLight(LIGHTBANK_DAY, light);
3513 n->setLight(LIGHTBANK_NIGHT, 0);
3517 // Insert light source
3518 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3521 // Increment index by y
3522 data->vmanip.m_area.add_y(em, i, -1);
3528 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3529 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3530 /*for(s16 x=0-data->max_spread_amount+1;
3531 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3533 for(s16 z=0-data->max_spread_amount+1;
3534 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3538 This has to be 1 smaller than the actual area, because
3539 neighboring nodes are checked.
3541 for(s16 x=lighting_min_d+1;
3542 x<=lighting_max_d-1;
3544 for(s16 z=lighting_min_d+1;
3545 z<=lighting_max_d-1;
3548 // Node position in 2d
3549 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3552 Apply initial sunlight
3555 u8 light = LIGHT_SUN;
3556 bool add_to_sources = false;
3557 v3s16 em = data->vmanip.m_area.getExtent();
3558 s16 y_start = y_nodes_max;
3559 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3560 for(s16 y=y_start; y>=y_nodes_min; y--)
3562 MapNode *n = &data->vmanip.m_data[i];
3564 if(light_propagates_content(n->d) == false)
3568 else if(light != LIGHT_SUN
3569 || sunlight_propagates_content(n->d) == false)
3575 // This doesn't take much time
3576 if(add_to_sources == false)
3579 Check sides. If side is not air or water, start
3580 adding to light_sources.
3583 v3s16(0,0,1), // back
3584 v3s16(1,0,0), // right
3585 v3s16(0,0,-1), // front
3586 v3s16(-1,0,0), // left
3588 for(u32 di=0; di<4; di++)
3590 v3s16 dirp = dirs4[di];
3592 data->vmanip.m_area.add_p(em, i2, dirp);
3593 MapNode *n2 = &data->vmanip.m_data[i2];
3595 n2->d != CONTENT_AIR
3596 && n2->d != CONTENT_WATERSOURCE
3597 && n2->d != CONTENT_WATER
3599 add_to_sources = true;
3605 n->setLight(LIGHTBANK_DAY, light);
3606 n->setLight(LIGHTBANK_NIGHT, 0);
3608 // This doesn't take much time
3609 if(light != 0 && add_to_sources)
3611 // Insert light source
3612 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3615 // Increment index by y
3616 data->vmanip.m_area.add_y(em, i, -1);
3624 // Spread light around
3626 TimeTaker timer("makeChunk() spreadLight");
3627 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3634 timer_generate.stop();
3637 //###################################################################
3638 //###################################################################
3639 //###################################################################
3640 //###################################################################
3641 //###################################################################
3642 //###################################################################
3643 //###################################################################
3644 //###################################################################
3645 //###################################################################
3646 //###################################################################
3647 //###################################################################
3648 //###################################################################
3649 //###################################################################
3650 //###################################################################
3651 //###################################################################
3653 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3655 if(m_chunksize == 0)
3663 // The distance how far into the neighbors the generator is allowed to go.
3664 s16 max_spread_amount_sectors = 2;
3665 assert(max_spread_amount_sectors <= m_chunksize);
3666 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3668 s16 y_blocks_min = -4;
3669 s16 y_blocks_max = 3;
3671 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3672 s16 sectorpos_base_size = m_chunksize;
3674 v2s16 sectorpos_bigbase =
3675 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3676 s16 sectorpos_bigbase_size =
3677 sectorpos_base_size + 2 * max_spread_amount_sectors;
3680 data.chunkpos = chunkpos;
3681 data.y_blocks_min = y_blocks_min;
3682 data.y_blocks_max = y_blocks_max;
3683 data.sectorpos_base = sectorpos_base;
3684 data.sectorpos_base_size = sectorpos_base_size;
3685 data.sectorpos_bigbase = sectorpos_bigbase;
3686 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3687 data.max_spread_amount = max_spread_amount;
3690 Create the whole area of this and the neighboring chunks
3693 TimeTaker timer("initChunkMake() create area");
3695 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3696 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3698 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3699 ServerMapSector *sector = createSector(sectorpos);
3702 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3704 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3705 MapBlock *block = createBlock(blockpos);
3707 // Lighting won't be calculated
3708 //block->setLightingExpired(true);
3709 // Lighting will be calculated
3710 block->setLightingExpired(false);
3713 Block gets sunlight if this is true.
3715 This should be set to true when the top side of a block
3716 is completely exposed to the sky.
3718 Actually this doesn't matter now because the
3719 initial lighting is done here.
3721 block->setIsUnderground(y != y_blocks_max);
3727 Now we have a big empty area.
3729 Make a ManualMapVoxelManipulator that contains this and the
3733 v3s16 bigarea_blocks_min(
3734 sectorpos_bigbase.X,
3738 v3s16 bigarea_blocks_max(
3739 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3741 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3744 data.vmanip.setMap(this);
3747 TimeTaker timer("initChunkMake() initialEmerge");
3748 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3753 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3754 core::map<v3s16, MapBlock*> &changed_blocks)
3760 Blit generated stuff to map
3764 //TimeTaker timer("generateChunkRaw() blitBackAll");
3765 data.vmanip.blitBackAll(&changed_blocks);
3769 Update day/night difference cache of the MapBlocks
3772 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3773 i.atEnd() == false; i++)
3775 MapBlock *block = i.getNode()->getValue();
3776 block->updateDayNightDiff();
3781 Copy transforming liquid information
3783 while(data.transforming_liquid.size() > 0)
3785 v3s16 p = data.transforming_liquid.pop_front();
3786 m_transforming_liquid.push_back(p);
3790 Add random objects to blocks
3793 for(s16 x=0; x<data.sectorpos_base_size; x++)
3794 for(s16 z=0; z<data.sectorpos_base_size; z++)
3796 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3797 ServerMapSector *sector = createSector(sectorpos);
3800 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3802 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3803 MapBlock *block = createBlock(blockpos);
3804 addRandomObjects(block);
3810 Create chunk metadata
3813 for(s16 x=-1; x<=1; x++)
3814 for(s16 y=-1; y<=1; y++)
3816 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3817 // Add chunk meta information
3818 MapChunk *chunk = getChunk(chunkpos0);
3821 chunk = new MapChunk();
3822 m_chunks.insert(chunkpos0, chunk);
3824 //chunk->setIsVolatile(true);
3825 if(chunk->getGenLevel() > GENERATED_PARTLY)
3826 chunk->setGenLevel(GENERATED_PARTLY);
3830 Set central chunk non-volatile
3832 MapChunk *chunk = getChunk(data.chunkpos);
3835 //chunk->setIsVolatile(false);
3836 chunk->setGenLevel(GENERATED_FULLY);
3839 Save changed parts of map
3848 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3849 core::map<v3s16, MapBlock*> &changed_blocks,
3852 DSTACK(__FUNCTION_NAME);
3855 Don't generate if already fully generated
3859 MapChunk *chunk = getChunk(chunkpos);
3860 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3862 dstream<<"generateChunkRaw(): Chunk "
3863 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3864 <<" already generated"<<std::endl;
3869 dstream<<"generateChunkRaw(): Generating chunk "
3870 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3873 TimeTaker timer("generateChunkRaw()");
3877 // Initialize generation
3878 initChunkMake(data, chunkpos);
3883 // Finalize generation
3884 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3887 Return central chunk (which was requested)
3893 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3894 core::map<v3s16, MapBlock*> &changed_blocks)
3896 dstream<<"generateChunk(): Generating chunk "
3897 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3900 /*for(s16 x=-1; x<=1; x++)
3901 for(s16 y=-1; y<=1; y++)*/
3902 for(s16 x=-0; x<=0; x++)
3903 for(s16 y=-0; y<=0; y++)
3905 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3906 MapChunk *chunk = getChunk(chunkpos0);
3907 // Skip if already generated
3908 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3910 generateChunkRaw(chunkpos0, changed_blocks);
3913 assert(chunkNonVolatile(chunkpos1));
3915 MapChunk *chunk = getChunk(chunkpos1);
3920 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3922 DSTACK("%s: p2d=(%d,%d)",
3927 Check if it exists already in memory
3929 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3934 Try to load it from disk (with blocks)
3936 if(loadSectorFull(p2d) == true)
3938 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3941 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3942 throw InvalidPositionException("");
3948 Do not create over-limit
3950 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3951 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3952 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3953 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3954 throw InvalidPositionException("createSector(): pos. over limit");
3957 Generate blank sector
3960 sector = new ServerMapSector(this, p2d);
3962 // Sector position on map in nodes
3963 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3968 m_sectors.insert(p2d, sector);
3974 MapSector * ServerMap::emergeSector(v2s16 p2d,
3975 core::map<v3s16, MapBlock*> &changed_blocks)
3977 DSTACK("%s: p2d=(%d,%d)",
3984 v2s16 chunkpos = sector_to_chunk(p2d);
3985 /*bool chunk_nonvolatile = false;
3986 MapChunk *chunk = getChunk(chunkpos);
3987 if(chunk && chunk->getIsVolatile() == false)
3988 chunk_nonvolatile = true;*/
3989 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3992 If chunk is not fully generated, generate chunk
3994 if(chunk_nonvolatile == false)
3996 // Generate chunk and neighbors
3997 generateChunk(chunkpos, changed_blocks);
4001 Return sector if it exists now
4003 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4008 Try to load it from disk
4010 if(loadSectorFull(p2d) == true)
4012 MapSector *sector = getSectorNoGenerateNoEx(p2d);
4015 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
4016 throw InvalidPositionException("");
4022 generateChunk should have generated the sector
4026 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
4027 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
4031 dstream<<"WARNING: Creating an empty sector."<<std::endl;
4033 return createSector(p2d);
4038 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
4041 generateChunkRaw(chunkpos, changed_blocks, true);
4044 Return sector if it exists now
4046 sector = getSectorNoGenerateNoEx(p2d);
4050 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
4058 //return generateSector();
4063 NOTE: This is not used for main map generation, only for blocks
4064 that are very high or low
4066 MapBlock * ServerMap::generateBlock(
4068 MapBlock *original_dummy,
4069 ServerMapSector *sector,
4070 core::map<v3s16, MapBlock*> &changed_blocks,
4071 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4074 DSTACK("%s: p=(%d,%d,%d)",
4078 // If chunks are disabled
4079 /*if(m_chunksize == 0)
4081 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
4082 <<"not generating."<<std::endl;
4086 /*dstream<<"generateBlock(): "
4087 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4090 MapBlock *block = original_dummy;
4092 v2s16 p2d(p.X, p.Z);
4094 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4097 Do not generate over-limit
4099 if(blockpos_over_limit(p))
4101 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4102 throw InvalidPositionException("generateBlock(): pos. over limit");
4106 If block doesn't exist, create one.
4107 If it exists, it is a dummy. In that case unDummify() it.
4109 NOTE: This already sets the map as the parent of the block
4113 block = sector->createBlankBlockNoInsert(block_y);
4117 // Remove the block so that nobody can get a half-generated one.
4118 sector->removeBlock(block);
4119 // Allocate the block to contain the generated data
4125 Generate a completely empty block
4127 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4128 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4130 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4134 block->setNode(v3s16(x0,y0,z0), n);
4139 Generate a proper block
4142 u8 water_material = CONTENT_WATERSOURCE;
4144 s32 lowest_ground_y = 32767;
4145 s32 highest_ground_y = -32768;
4147 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4148 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4150 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4152 //s16 surface_y = 0;
4154 s16 mud_add_amount = get_mud_add_amount(m_seed, p2d_nodes+v2s16(x0,z0));
4156 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4159 /*// Less mud if it is high
4166 // If chunks are disabled
4167 if(m_chunksize == 0)
4168 surface_y = WATER_LEVEL + 1;
4170 if(surface_y < lowest_ground_y)
4171 lowest_ground_y = surface_y;
4172 if(surface_y > highest_ground_y)
4173 highest_ground_y = surface_y;
4175 s32 surface_depth = AVERAGE_MUD_AMOUNT;
4177 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4179 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4184 NOTE: If there are some man-made structures above the
4185 newly created block, they won't be taken into account.
4187 if(real_y > surface_y)
4188 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4194 // If node is over heightmap y, it's air or water
4195 if(real_y > surface_y)
4197 // If under water level, it's water
4198 if(real_y < WATER_LEVEL)
4200 n.d = water_material;
4201 n.setLight(LIGHTBANK_DAY,
4202 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4204 Add to transforming liquid queue (in case it'd
4207 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4208 m_transforming_liquid.push_back(real_pos);
4214 // Else it's ground or caves (air)
4217 // If it's cave, it's cave
4218 if(is_cave(m_seed, block->getPosRelative()+v3s16(x0,y0,z0)))
4222 // If it's surface_depth under ground, it's stone
4223 else if(real_y <= surface_y - surface_depth)
4225 n.d = CONTENT_STONE;
4229 // It is mud if it is under the first ground
4230 // level or under water
4231 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4237 n.d = CONTENT_GRASS;
4240 //n.d = CONTENT_MUD;
4242 /*// If under water level, it's mud
4243 if(real_y < WATER_LEVEL)
4245 // Only the topmost node is grass
4246 else if(real_y <= surface_y - 1)
4249 n.d = CONTENT_GRASS;*/
4253 block->setNode(v3s16(x0,y0,z0), n);
4258 Calculate some helper variables
4261 // Completely underground if the highest part of block is under lowest
4263 // This has to be very sure; it's probably one too strict now but
4264 // that's just better.
4265 bool completely_underground =
4266 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4268 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4270 bool mostly_underwater_surface = false;
4271 if(highest_ground_y < WATER_LEVEL
4272 && some_part_underground && !completely_underground)
4273 mostly_underwater_surface = true;
4280 float caves_amount = 0.5;
4282 // Initialize temporary table
4283 const s32 ued = MAP_BLOCKSIZE;
4284 bool underground_emptiness[ued*ued*ued];
4285 for(s32 i=0; i<ued*ued*ued; i++)
4287 underground_emptiness[i] = 0;
4294 Initialize orp and ors. Try to find if some neighboring
4295 MapBlock has a tunnel ended in its side
4299 (float)(myrand()%ued)+0.5,
4300 (float)(myrand()%ued)+0.5,
4301 (float)(myrand()%ued)+0.5
4304 bool found_existing = false;
4310 for(s16 y=0; y<ued; y++)
4311 for(s16 x=0; x<ued; x++)
4313 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4314 if(getNode(ap).d == CONTENT_AIR)
4316 orp = v3f(x+1,y+1,0);
4317 found_existing = true;
4318 goto continue_generating;
4322 catch(InvalidPositionException &e){}
4328 for(s16 y=0; y<ued; y++)
4329 for(s16 x=0; x<ued; x++)
4331 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4332 if(getNode(ap).d == CONTENT_AIR)
4334 orp = v3f(x+1,y+1,ued-1);
4335 found_existing = true;
4336 goto continue_generating;
4340 catch(InvalidPositionException &e){}
4346 for(s16 y=0; y<ued; y++)
4347 for(s16 z=0; z<ued; z++)
4349 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4350 if(getNode(ap).d == CONTENT_AIR)
4352 orp = v3f(0,y+1,z+1);
4353 found_existing = true;
4354 goto continue_generating;
4358 catch(InvalidPositionException &e){}
4364 for(s16 y=0; y<ued; y++)
4365 for(s16 z=0; z<ued; z++)
4367 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4368 if(getNode(ap).d == CONTENT_AIR)
4370 orp = v3f(ued-1,y+1,z+1);
4371 found_existing = true;
4372 goto continue_generating;
4376 catch(InvalidPositionException &e){}
4382 for(s16 x=0; x<ued; x++)
4383 for(s16 z=0; z<ued; z++)
4385 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4386 if(getNode(ap).d == CONTENT_AIR)
4388 orp = v3f(x+1,0,z+1);
4389 found_existing = true;
4390 goto continue_generating;
4394 catch(InvalidPositionException &e){}
4400 for(s16 x=0; x<ued; x++)
4401 for(s16 z=0; z<ued; z++)
4403 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4404 if(getNode(ap).d == CONTENT_AIR)
4406 orp = v3f(x+1,ued-1,z+1);
4407 found_existing = true;
4408 goto continue_generating;
4412 catch(InvalidPositionException &e){}
4414 continue_generating:
4417 Choose whether to actually generate cave
4419 bool do_generate_caves = true;
4420 // Don't generate if no part is underground
4421 if(!some_part_underground)
4423 do_generate_caves = false;
4425 // Don't generate if mostly underwater surface
4426 /*else if(mostly_underwater_surface)
4428 do_generate_caves = false;
4430 // Partly underground = cave
4431 else if(!completely_underground)
4433 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4435 // Found existing cave underground
4436 else if(found_existing && completely_underground)
4438 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4440 // Underground and no caves found
4443 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4446 if(do_generate_caves)
4449 Generate some tunnel starting from orp and ors
4451 for(u16 i=0; i<3; i++)
4454 (float)(myrand()%ued)+0.5,
4455 (float)(myrand()%ued)+0.5,
4456 (float)(myrand()%ued)+0.5
4460 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4464 for(float f=0; f<1.0; f+=0.04)
4466 v3f fp = orp + vec * f;
4467 v3s16 cp(fp.X, fp.Y, fp.Z);
4469 s16 d1 = d0 + rs - 1;
4470 for(s16 z0=d0; z0<=d1; z0++)
4472 s16 si = rs - abs(z0);
4473 for(s16 x0=-si; x0<=si-1; x0++)
4475 s16 si2 = rs - abs(x0);
4476 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4482 if(isInArea(p, ued) == false)
4484 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4496 // Set to true if has caves.
4497 // Set when some non-air is changed to air when making caves.
4498 bool has_caves = false;
4501 Apply temporary cave data to block
4504 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4505 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4507 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4509 MapNode n = block->getNode(v3s16(x0,y0,z0));
4512 if(underground_emptiness[
4513 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4514 +ued*(y0*ued/MAP_BLOCKSIZE)
4515 +(x0*ued/MAP_BLOCKSIZE)])
4517 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4526 block->setNode(v3s16(x0,y0,z0), n);
4532 This is used for guessing whether or not the block should
4533 receive sunlight from the top if the block above doesn't exist
4535 block->setIsUnderground(completely_underground);
4538 Force lighting update if some part of block is partly
4539 underground and has caves.
4541 /*if(some_part_underground && !completely_underground && has_caves)
4543 //dstream<<"Half-ground caves"<<std::endl;
4544 lighting_invalidated_blocks[block->getPos()] = block;
4547 // DEBUG: Always update lighting
4548 //lighting_invalidated_blocks[block->getPos()] = block;
4554 if(some_part_underground)
4556 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4561 for(s16 i=0; i<underground_level/4 + 1; i++)
4563 if(myrand()%50 == 0)
4566 (myrand()%(MAP_BLOCKSIZE-2))+1,
4567 (myrand()%(MAP_BLOCKSIZE-2))+1,
4568 (myrand()%(MAP_BLOCKSIZE-2))+1
4574 for(u16 i=0; i<27; i++)
4576 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4578 block->setNode(cp+g_27dirs[i], n);
4586 u16 coal_amount = 30;
4587 u16 coal_rareness = 60 / coal_amount;
4588 if(coal_rareness == 0)
4590 if(myrand()%coal_rareness == 0)
4592 u16 a = myrand() % 16;
4593 u16 amount = coal_amount * a*a*a / 1000;
4594 for(s16 i=0; i<amount; i++)
4597 (myrand()%(MAP_BLOCKSIZE-2))+1,
4598 (myrand()%(MAP_BLOCKSIZE-2))+1,
4599 (myrand()%(MAP_BLOCKSIZE-2))+1
4603 n.d = CONTENT_STONE;
4604 n.param = MINERAL_COAL;
4606 for(u16 i=0; i<27; i++)
4608 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4610 block->setNode(cp+g_27dirs[i], n);
4618 //TODO: change to iron_amount or whatever
4619 u16 iron_amount = 15;
4620 u16 iron_rareness = 60 / iron_amount;
4621 if(iron_rareness == 0)
4623 if(myrand()%iron_rareness == 0)
4625 u16 a = myrand() % 16;
4626 u16 amount = iron_amount * a*a*a / 1000;
4627 for(s16 i=0; i<amount; i++)
4630 (myrand()%(MAP_BLOCKSIZE-2))+1,
4631 (myrand()%(MAP_BLOCKSIZE-2))+1,
4632 (myrand()%(MAP_BLOCKSIZE-2))+1
4636 n.d = CONTENT_STONE;
4637 n.param = MINERAL_IRON;
4639 for(u16 i=0; i<27; i++)
4641 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4643 block->setNode(cp+g_27dirs[i], n);
4650 Create a few rats in empty blocks underground
4652 if(completely_underground)
4654 //for(u16 i=0; i<2; i++)
4657 (myrand()%(MAP_BLOCKSIZE-2))+1,
4658 (myrand()%(MAP_BLOCKSIZE-2))+1,
4659 (myrand()%(MAP_BLOCKSIZE-2))+1
4662 // Check that the place is empty
4663 //if(!is_ground_content(block->getNode(cp).d))
4666 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4667 block->addObject(obj);
4672 #endif // end of proper block generation
4675 Add block to sector.
4677 sector->insertBlock(block);
4679 // Lighting is invalid after generation.
4680 block->setLightingExpired(true);
4687 <<"lighting_invalidated_blocks.size()"
4691 <<" "<<lighting_invalidated_blocks.size()
4693 <<", "<<completely_underground
4694 <<", "<<some_part_underground
4701 MapBlock * ServerMap::createBlock(v3s16 p)
4703 DSTACK("%s: p=(%d,%d,%d)",
4704 __FUNCTION_NAME, p.X, p.Y, p.Z);
4707 Do not create over-limit
4709 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4710 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4711 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4712 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4713 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4714 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4715 throw InvalidPositionException("createBlock(): pos. over limit");
4717 v2s16 p2d(p.X, p.Z);
4720 This will create or load a sector if not found in memory.
4721 If block exists on disk, it will be loaded.
4723 NOTE: On old save formats, this will be slow, as it generates
4724 lighting on blocks for them.
4726 ServerMapSector *sector;
4728 sector = (ServerMapSector*)createSector(p2d);
4729 assert(sector->getId() == MAPSECTOR_SERVER);
4731 catch(InvalidPositionException &e)
4733 dstream<<"createBlock: createSector() failed"<<std::endl;
4737 NOTE: This should not be done, or at least the exception
4738 should not be passed on as std::exception, because it
4739 won't be catched at all.
4741 /*catch(std::exception &e)
4743 dstream<<"createBlock: createSector() failed: "
4744 <<e.what()<<std::endl;
4749 Try to get a block from the sector
4752 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4756 block = sector->createBlankBlock(block_y);
4760 MapBlock * ServerMap::emergeBlock(
4762 bool only_from_disk,
4763 core::map<v3s16, MapBlock*> &changed_blocks,
4764 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4767 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
4769 p.X, p.Y, p.Z, only_from_disk);
4772 Do not generate over-limit
4774 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4775 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4776 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4777 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4778 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4779 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4780 throw InvalidPositionException("emergeBlock(): pos. over limit");
4782 v2s16 p2d(p.X, p.Z);
4785 This will create or load a sector if not found in memory.
4786 If block exists on disk, it will be loaded.
4788 ServerMapSector *sector;
4790 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4791 assert(sector->getId() == MAPSECTOR_SERVER);
4793 catch(InvalidPositionException &e)
4795 dstream<<"emergeBlock: emergeSector() failed: "
4796 <<e.what()<<std::endl;
4797 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4799 <<"You could try to delete it."<<std::endl;
4802 catch(VersionMismatchException &e)
4804 dstream<<"emergeBlock: emergeSector() failed: "
4805 <<e.what()<<std::endl;
4806 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4808 <<"You could try to delete it."<<std::endl;
4812 NOTE: This should not be done, or at least the exception
4813 should not be passed on as std::exception, because it
4814 won't be catched at all.
4816 /*catch(std::exception &e)
4818 dstream<<"emergeBlock: emergeSector() failed: "
4819 <<e.what()<<std::endl;
4820 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4822 <<"You could try to delete it."<<std::endl;
4827 Try to get a block from the sector
4830 bool does_not_exist = false;
4831 bool lighting_expired = false;
4832 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4836 does_not_exist = true;
4838 else if(block->isDummy() == true)
4840 does_not_exist = true;
4842 else if(block->getLightingExpired())
4844 lighting_expired = true;
4849 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4854 If block was not found on disk and not going to generate a
4855 new one, make sure there is a dummy block in place.
4857 if(only_from_disk && (does_not_exist || lighting_expired))
4859 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4863 // Create dummy block
4864 block = new MapBlock(this, p, true);
4866 // Add block to sector
4867 sector->insertBlock(block);
4873 //dstream<<"Not found on disk, generating."<<std::endl;
4875 //TimeTaker("emergeBlock() generate");
4877 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4880 If the block doesn't exist, generate the block.
4884 block = generateBlock(p, block, sector, changed_blocks,
4885 lighting_invalidated_blocks);
4888 if(lighting_expired)
4890 lighting_invalidated_blocks.insert(p, block);
4894 Initially update sunlight
4898 core::map<v3s16, bool> light_sources;
4899 bool black_air_left = false;
4900 bool bottom_invalid =
4901 block->propagateSunlight(light_sources, true,
4902 &black_air_left, true);
4904 // If sunlight didn't reach everywhere and part of block is
4905 // above ground, lighting has to be properly updated
4906 //if(black_air_left && some_part_underground)
4909 lighting_invalidated_blocks[block->getPos()] = block;
4914 lighting_invalidated_blocks[block->getPos()] = block;
4921 s16 ServerMap::findGroundLevel(v2s16 p2d)
4924 Uh, just do something random...
4926 // Find existing map from top to down
4929 v3s16 p(p2d.X, max, p2d.Y);
4930 for(; p.Y>min; p.Y--)
4932 MapNode n = getNodeNoEx(p);
4933 if(n.d != CONTENT_IGNORE)
4938 // If this node is not air, go to plan b
4939 if(getNodeNoEx(p).d != CONTENT_AIR)
4941 // Search existing walkable and return it
4942 for(; p.Y>min; p.Y--)
4944 MapNode n = getNodeNoEx(p);
4945 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4951 Plan B: Get from map generator perlin noise function
4953 // This won't work if proper generation is disabled
4954 if(m_chunksize == 0)
4955 return WATER_LEVEL+2;
4956 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4960 void ServerMap::createDir(std::string path)
4962 if(fs::CreateDir(path) == false)
4964 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4965 <<"\""<<path<<"\""<<std::endl;
4966 throw BaseException("ServerMap failed to create directory");
4970 std::string ServerMap::getSectorSubDir(v2s16 pos)
4973 snprintf(cc, 9, "%.4x%.4x",
4974 (unsigned int)pos.X&0xffff,
4975 (unsigned int)pos.Y&0xffff);
4977 return std::string(cc);
4980 std::string ServerMap::getSectorDir(v2s16 pos)
4982 return m_savedir + "/sectors/" + getSectorSubDir(pos);
4985 v2s16 ServerMap::getSectorPos(std::string dirname)
4987 if(dirname.size() != 8)
4988 throw InvalidFilenameException("Invalid sector directory name");
4990 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
4992 throw InvalidFilenameException("Invalid sector directory name");
4993 v2s16 pos((s16)x, (s16)y);
4997 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4999 v2s16 p2d = getSectorPos(sectordir);
5001 if(blockfile.size() != 4){
5002 throw InvalidFilenameException("Invalid block filename");
5005 int r = sscanf(blockfile.c_str(), "%4x", &y);
5007 throw InvalidFilenameException("Invalid block filename");
5008 return v3s16(p2d.X, y, p2d.Y);
5011 void ServerMap::save(bool only_changed)
5013 DSTACK(__FUNCTION_NAME);
5014 if(m_map_saving_enabled == false)
5016 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
5020 if(only_changed == false)
5021 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
5024 if(only_changed == false || m_map_metadata_changed)
5029 // Disable saving chunk metadata if chunks are disabled
5030 if(m_chunksize != 0)
5032 if(only_changed == false || anyChunkModified())
5036 u32 sector_meta_count = 0;
5037 u32 block_count = 0;
5040 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5042 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
5043 for(; i.atEnd() == false; i++)
5045 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
5046 assert(sector->getId() == MAPSECTOR_SERVER);
5048 if(sector->differs_from_disk || only_changed == false)
5050 saveSectorMeta(sector);
5051 sector_meta_count++;
5053 core::list<MapBlock*> blocks;
5054 sector->getBlocks(blocks);
5055 core::list<MapBlock*>::Iterator j;
5056 for(j=blocks.begin(); j!=blocks.end(); j++)
5058 MapBlock *block = *j;
5059 if(block->getChangedFlag() || only_changed == false)
5064 /*dstream<<"ServerMap: Written block ("
5065 <<block->getPos().X<<","
5066 <<block->getPos().Y<<","
5067 <<block->getPos().Z<<")"
5076 Only print if something happened or saved whole map
5078 if(only_changed == false || sector_meta_count != 0
5079 || block_count != 0)
5081 dstream<<DTIME<<"ServerMap: Written: "
5082 <<sector_meta_count<<" sector metadata files, "
5083 <<block_count<<" block files"
5089 // NOTE: Doing this is insane. Deprecated and probably broken.
5090 void ServerMap::loadAll()
5092 DSTACK(__FUNCTION_NAME);
5093 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5098 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5100 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5102 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5105 s32 printed_counter = -100000;
5106 s32 count = list.size();
5108 std::vector<fs::DirListNode>::iterator i;
5109 for(i=list.begin(); i!=list.end(); i++)
5111 if(counter > printed_counter + 10)
5113 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5114 printed_counter = counter;
5118 MapSector *sector = NULL;
5120 // We want directories
5124 sector = loadSectorMeta(i->name);
5126 catch(InvalidFilenameException &e)
5128 // This catches unknown crap in directory
5131 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5132 (m_savedir+"/sectors/"+i->name);
5133 std::vector<fs::DirListNode>::iterator i2;
5134 for(i2=list2.begin(); i2!=list2.end(); i2++)
5140 loadBlock(i->name, i2->name, sector);
5142 catch(InvalidFilenameException &e)
5144 // This catches unknown crap in directory
5148 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5153 void ServerMap::saveMasterHeightmap()
5155 DSTACK(__FUNCTION_NAME);
5157 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5159 createDir(m_savedir);
5161 /*std::string fullpath = m_savedir + "/master_heightmap";
5162 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5163 if(o.good() == false)
5164 throw FileNotGoodException("Cannot open master heightmap");*/
5166 // Format used for writing
5167 //u8 version = SER_FMT_VER_HIGHEST;
5170 void ServerMap::loadMasterHeightmap()
5172 DSTACK(__FUNCTION_NAME);
5174 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5176 /*std::string fullpath = m_savedir + "/master_heightmap";
5177 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5178 if(is.good() == false)
5179 throw FileNotGoodException("Cannot open master heightmap");*/
5183 void ServerMap::saveMapMeta()
5185 DSTACK(__FUNCTION_NAME);
5187 dstream<<"INFO: ServerMap::saveMapMeta(): "
5188 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5191 createDir(m_savedir);
5193 std::string fullpath = m_savedir + "/map_meta.txt";
5194 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5195 if(os.good() == false)
5197 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5198 <<"could not open"<<fullpath<<std::endl;
5199 throw FileNotGoodException("Cannot open chunk metadata");
5203 params.setU64("seed", m_seed);
5204 params.setS32("chunksize", m_chunksize);
5206 params.writeLines(os);
5208 os<<"[end_of_params]\n";
5210 m_map_metadata_changed = false;
5213 void ServerMap::loadMapMeta()
5215 DSTACK(__FUNCTION_NAME);
5217 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
5220 std::string fullpath = m_savedir + "/map_meta.txt";
5221 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5222 if(is.good() == false)
5224 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5225 <<"could not open"<<fullpath<<std::endl;
5226 throw FileNotGoodException("Cannot open map metadata");
5234 throw SerializationError
5235 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5237 std::getline(is, line);
5238 std::string trimmedline = trim(line);
5239 if(trimmedline == "[end_of_params]")
5241 params.parseConfigLine(line);
5244 m_seed = params.getU64("seed");
5245 m_chunksize = params.getS32("chunksize");
5247 dstream<<"INFO: ServerMap::loadMapMeta(): "
5248 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5252 void ServerMap::saveChunkMeta()
5254 DSTACK(__FUNCTION_NAME);
5256 // This should not be called if chunks are disabled.
5257 assert(m_chunksize != 0);
5259 u32 count = m_chunks.size();
5261 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5262 <<count<<" chunks"<<std::endl;
5264 createDir(m_savedir);
5266 std::string fullpath = m_savedir + "/chunk_meta";
5267 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5268 if(os.good() == false)
5270 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5271 <<"could not open"<<fullpath<<std::endl;
5272 throw FileNotGoodException("Cannot open chunk metadata");
5278 os.write((char*)&version, 1);
5283 writeU32(buf, count);
5284 os.write((char*)buf, 4);
5286 for(core::map<v2s16, MapChunk*>::Iterator
5287 i = m_chunks.getIterator();
5288 i.atEnd()==false; i++)
5290 v2s16 p = i.getNode()->getKey();
5291 MapChunk *chunk = i.getNode()->getValue();
5294 os.write((char*)buf, 4);
5296 chunk->serialize(os, version);
5299 setChunksNonModified();
5302 void ServerMap::loadChunkMeta()
5304 DSTACK(__FUNCTION_NAME);
5306 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5309 std::string fullpath = m_savedir + "/chunk_meta";
5310 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5311 if(is.good() == false)
5313 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5314 <<"could not open"<<fullpath<<std::endl;
5315 throw FileNotGoodException("Cannot open chunk metadata");
5321 is.read((char*)&version, 1);
5326 is.read((char*)buf, 4);
5327 u32 count = readU32(buf);
5329 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5330 <<count<<" chunks"<<std::endl;
5332 for(u32 i=0; i<count; i++)
5335 MapChunk *chunk = new MapChunk();
5337 is.read((char*)buf, 4);
5340 chunk->deSerialize(is, version);
5341 m_chunks.insert(p, chunk);
5345 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5347 DSTACK(__FUNCTION_NAME);
5348 // Format used for writing
5349 u8 version = SER_FMT_VER_HIGHEST;
5351 v2s16 pos = sector->getPos();
5352 createDir(m_savedir);
5353 createDir(m_savedir+"/sectors");
5354 std::string dir = getSectorDir(pos);
5357 std::string fullpath = dir + "/meta";
5358 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5359 if(o.good() == false)
5360 throw FileNotGoodException("Cannot open sector metafile");
5362 sector->serialize(o, version);
5364 sector->differs_from_disk = false;
5367 MapSector* ServerMap::loadSectorMeta(std::string dirname)
5369 DSTACK(__FUNCTION_NAME);
5371 v2s16 p2d = getSectorPos(dirname);
5372 std::string dir = m_savedir + "/sectors/" + dirname;
5374 ServerMapSector *sector = NULL;
5376 std::string fullpath = dir + "/meta";
5377 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5378 if(is.good() == false)
5380 // If the directory exists anyway, it probably is in some old
5381 // format. Just go ahead and create the sector.
5382 if(fs::PathExists(dir))
5384 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5385 <<fullpath<<" doesn't exist but directory does."
5386 <<" Continuing with a sector with no metadata."
5388 sector = new ServerMapSector(this, p2d);
5389 m_sectors.insert(p2d, sector);
5392 throw FileNotGoodException("Cannot open sector metafile");
5396 sector = ServerMapSector::deSerialize
5397 (is, this, p2d, m_sectors);
5400 sector->differs_from_disk = false;
5405 bool ServerMap::loadSectorFull(v2s16 p2d)
5407 DSTACK(__FUNCTION_NAME);
5408 std::string sectorsubdir = getSectorSubDir(p2d);
5410 MapSector *sector = NULL;
5412 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5415 sector = loadSectorMeta(sectorsubdir);
5417 catch(InvalidFilenameException &e)
5421 catch(FileNotGoodException &e)
5425 catch(std::exception &e)
5433 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5434 (m_savedir+"/sectors/"+sectorsubdir);
5435 std::vector<fs::DirListNode>::iterator i2;
5436 for(i2=list2.begin(); i2!=list2.end(); i2++)
5442 loadBlock(sectorsubdir, i2->name, sector);
5444 catch(InvalidFilenameException &e)
5446 // This catches unknown crap in directory
5452 void ServerMap::saveBlock(MapBlock *block)
5454 DSTACK(__FUNCTION_NAME);
5456 Dummy blocks are not written
5458 if(block->isDummy())
5460 /*v3s16 p = block->getPos();
5461 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5462 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5466 // Format used for writing
5467 u8 version = SER_FMT_VER_HIGHEST;
5469 v3s16 p3d = block->getPos();
5470 v2s16 p2d(p3d.X, p3d.Z);
5471 createDir(m_savedir);
5472 createDir(m_savedir+"/sectors");
5473 std::string dir = getSectorDir(p2d);
5476 // Block file is map/sectors/xxxxxxxx/xxxx
5478 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5479 std::string fullpath = dir + "/" + cc;
5480 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5481 if(o.good() == false)
5482 throw FileNotGoodException("Cannot open block data");
5485 [0] u8 serialization version
5488 o.write((char*)&version, 1);
5490 block->serialize(o, version);
5493 Versions up from 9 have block objects.
5497 block->serializeObjects(o, version);
5501 Versions up from 15 have static objects.
5505 block->m_static_objects.serialize(o);
5508 // We just wrote it to the disk
5509 block->resetChangedFlag();
5512 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
5514 DSTACK(__FUNCTION_NAME);
5516 // Block file is map/sectors/xxxxxxxx/xxxx
5517 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
5520 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5521 if(is.good() == false)
5522 throw FileNotGoodException("Cannot open block file");
5524 v3s16 p3d = getBlockPos(sectordir, blockfile);
5525 v2s16 p2d(p3d.X, p3d.Z);
5527 assert(sector->getPos() == p2d);
5529 u8 version = SER_FMT_VER_INVALID;
5530 is.read((char*)&version, 1);
5533 throw SerializationError("ServerMap::loadBlock(): Failed"
5534 " to read MapBlock version");
5536 /*u32 block_size = MapBlock::serializedLength(version);
5537 SharedBuffer<u8> data(block_size);
5538 is.read((char*)*data, block_size);*/
5540 // This will always return a sector because we're the server
5541 //MapSector *sector = emergeSector(p2d);
5543 MapBlock *block = NULL;
5544 bool created_new = false;
5546 block = sector->getBlockNoCreate(p3d.Y);
5548 catch(InvalidPositionException &e)
5550 block = sector->createBlankBlockNoInsert(p3d.Y);
5554 // deserialize block data
5555 block->deSerialize(is, version);
5558 Versions up from 9 have block objects.
5562 block->updateObjects(is, version, NULL, 0);
5566 Versions up from 15 have static objects.
5570 block->m_static_objects.deSerialize(is);
5574 sector->insertBlock(block);
5577 Convert old formats to new and save
5580 // Save old format blocks in new format
5581 if(version < SER_FMT_VER_HIGHEST)
5586 // We just loaded it from the disk, so it's up-to-date.
5587 block->resetChangedFlag();
5590 catch(SerializationError &e)
5592 dstream<<"WARNING: Invalid block data on disk "
5593 "(SerializationError). Ignoring. "
5594 "A new one will be generated."
5597 // TODO: Backup file; name is in fullpath.
5601 void ServerMap::PrintInfo(std::ostream &out)
5612 ClientMap::ClientMap(
5614 MapDrawControl &control,
5615 scene::ISceneNode* parent,
5616 scene::ISceneManager* mgr,
5620 scene::ISceneNode(parent, mgr, id),
5623 m_camera_position(0,0,0),
5624 m_camera_direction(0,0,1)
5626 m_camera_mutex.Init();
5627 assert(m_camera_mutex.IsInitialized());
5629 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5630 BS*1000000,BS*1000000,BS*1000000);
5633 ClientMap::~ClientMap()
5635 /*JMutexAutoLock lock(mesh_mutex);
5644 MapSector * ClientMap::emergeSector(v2s16 p2d)
5646 DSTACK(__FUNCTION_NAME);
5647 // Check that it doesn't exist already
5649 return getSectorNoGenerate(p2d);
5651 catch(InvalidPositionException &e)
5656 ClientMapSector *sector = new ClientMapSector(this, p2d);
5659 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5660 m_sectors.insert(p2d, sector);
5666 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5668 DSTACK(__FUNCTION_NAME);
5669 ClientMapSector *sector = NULL;
5671 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5673 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5677 sector = (ClientMapSector*)n->getValue();
5678 assert(sector->getId() == MAPSECTOR_CLIENT);
5682 sector = new ClientMapSector(this, p2d);
5684 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5685 m_sectors.insert(p2d, sector);
5689 sector->deSerialize(is);
5692 void ClientMap::OnRegisterSceneNode()
5696 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5697 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5700 ISceneNode::OnRegisterSceneNode();
5703 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5705 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5706 DSTACK(__FUNCTION_NAME);
5708 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5711 Get time for measuring timeout.
5713 Measuring time is very useful for long delays when the
5714 machine is swapping a lot.
5716 int time1 = time(0);
5718 //u32 daynight_ratio = m_client->getDayNightRatio();
5720 m_camera_mutex.Lock();
5721 v3f camera_position = m_camera_position;
5722 v3f camera_direction = m_camera_direction;
5723 m_camera_mutex.Unlock();
5726 Get all blocks and draw all visible ones
5729 v3s16 cam_pos_nodes(
5730 camera_position.X / BS,
5731 camera_position.Y / BS,
5732 camera_position.Z / BS);
5734 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5736 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5737 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5739 // Take a fair amount as we will be dropping more out later
5740 v3s16 p_blocks_min = getNodeBlockPos(p_nodes_min) - v3s16(1,1,1);
5741 v3s16 p_blocks_max = getNodeBlockPos(p_nodes_max) + v3s16(1,1,1);
5743 u32 vertex_count = 0;
5745 // For limiting number of mesh updates per frame
5746 u32 mesh_update_count = 0;
5748 u32 blocks_would_have_drawn = 0;
5749 u32 blocks_drawn = 0;
5751 //NOTE: The sectors map should be locked but we're not doing it
5752 // because it'd cause too much delays
5754 int timecheck_counter = 0;
5755 core::map<v2s16, MapSector*>::Iterator si;
5756 si = m_sectors.getIterator();
5757 for(; si.atEnd() == false; si++)
5760 timecheck_counter++;
5761 if(timecheck_counter > 50)
5763 timecheck_counter = 0;
5764 int time2 = time(0);
5765 if(time2 > time1 + 4)
5767 dstream<<"ClientMap::renderMap(): "
5768 "Rendering takes ages, returning."
5775 MapSector *sector = si.getNode()->getValue();
5776 v2s16 sp = sector->getPos();
5778 if(m_control.range_all == false)
5780 if(sp.X < p_blocks_min.X
5781 || sp.X > p_blocks_max.X
5782 || sp.Y < p_blocks_min.Z
5783 || sp.Y > p_blocks_max.Z)
5787 core::list< MapBlock * > sectorblocks;
5788 sector->getBlocks(sectorblocks);
5794 core::list< MapBlock * >::Iterator i;
5795 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5797 MapBlock *block = *i;
5800 Compare block position to camera position, skip
5801 if not seen on display
5804 float range = 100000 * BS;
5805 if(m_control.range_all == false)
5806 range = m_control.wanted_range * BS;
5809 if(isBlockInSight(block->getPos(), camera_position,
5810 camera_direction, range, &d) == false)
5815 // This is ugly (spherical distance limit?)
5816 /*if(m_control.range_all == false &&
5817 d - 0.5*BS*MAP_BLOCKSIZE > range)
5822 Update expired mesh (used for day/night change)
5824 It doesn't work exactly like it should now with the
5825 tasked mesh update but whatever.
5828 bool mesh_expired = false;
5831 JMutexAutoLock lock(block->mesh_mutex);
5833 mesh_expired = block->getMeshExpired();
5835 // Mesh has not been expired and there is no mesh:
5836 // block has no content
5837 if(block->mesh == NULL && mesh_expired == false)
5841 f32 faraway = BS*50;
5842 //f32 faraway = m_control.wanted_range * BS;
5845 This has to be done with the mesh_mutex unlocked
5847 // Pretty random but this should work somewhat nicely
5848 if(mesh_expired && (
5849 (mesh_update_count < 3
5850 && (d < faraway || mesh_update_count < 2)
5853 (m_control.range_all && mesh_update_count < 20)
5856 /*if(mesh_expired && mesh_update_count < 6
5857 && (d < faraway || mesh_update_count < 3))*/
5859 mesh_update_count++;
5861 // Mesh has been expired: generate new mesh
5862 //block->updateMesh(daynight_ratio);
5863 m_client->addUpdateMeshTask(block->getPos());
5865 mesh_expired = false;
5870 Draw the faces of the block
5873 JMutexAutoLock lock(block->mesh_mutex);
5875 scene::SMesh *mesh = block->mesh;
5880 blocks_would_have_drawn++;
5881 if(blocks_drawn >= m_control.wanted_max_blocks
5882 && m_control.range_all == false
5883 && d > m_control.wanted_min_range * BS)
5887 u32 c = mesh->getMeshBufferCount();
5889 for(u32 i=0; i<c; i++)
5891 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5892 const video::SMaterial& material = buf->getMaterial();
5893 video::IMaterialRenderer* rnd =
5894 driver->getMaterialRenderer(material.MaterialType);
5895 bool transparent = (rnd && rnd->isTransparent());
5896 // Render transparent on transparent pass and likewise.
5897 if(transparent == is_transparent_pass)
5900 This *shouldn't* hurt too much because Irrlicht
5901 doesn't change opengl textures if the old
5902 material is set again.
5904 driver->setMaterial(buf->getMaterial());
5905 driver->drawMeshBuffer(buf);
5906 vertex_count += buf->getVertexCount();
5910 } // foreach sectorblocks
5913 m_control.blocks_drawn = blocks_drawn;
5914 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5916 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5917 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5920 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5921 core::map<v3s16, MapBlock*> *affected_blocks)
5923 bool changed = false;
5925 Add it to all blocks touching it
5928 v3s16(0,0,0), // this
5929 v3s16(0,0,1), // back
5930 v3s16(0,1,0), // top
5931 v3s16(1,0,0), // right
5932 v3s16(0,0,-1), // front
5933 v3s16(0,-1,0), // bottom
5934 v3s16(-1,0,0), // left
5936 for(u16 i=0; i<7; i++)
5938 v3s16 p2 = p + dirs[i];
5939 // Block position of neighbor (or requested) node
5940 v3s16 blockpos = getNodeBlockPos(p2);
5941 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5942 if(blockref == NULL)
5944 // Relative position of requested node
5945 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5946 if(blockref->setTempMod(relpos, mod))
5951 if(changed && affected_blocks!=NULL)
5953 for(u16 i=0; i<7; i++)
5955 v3s16 p2 = p + dirs[i];
5956 // Block position of neighbor (or requested) node
5957 v3s16 blockpos = getNodeBlockPos(p2);
5958 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5959 if(blockref == NULL)
5961 affected_blocks->insert(blockpos, blockref);
5967 bool ClientMap::clearTempMod(v3s16 p,
5968 core::map<v3s16, MapBlock*> *affected_blocks)
5970 bool changed = false;
5972 v3s16(0,0,0), // this
5973 v3s16(0,0,1), // back
5974 v3s16(0,1,0), // top
5975 v3s16(1,0,0), // right
5976 v3s16(0,0,-1), // front
5977 v3s16(0,-1,0), // bottom
5978 v3s16(-1,0,0), // left
5980 for(u16 i=0; i<7; i++)
5982 v3s16 p2 = p + dirs[i];
5983 // Block position of neighbor (or requested) node
5984 v3s16 blockpos = getNodeBlockPos(p2);
5985 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5986 if(blockref == NULL)
5988 // Relative position of requested node
5989 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5990 if(blockref->clearTempMod(relpos))
5995 if(changed && affected_blocks!=NULL)
5997 for(u16 i=0; i<7; i++)
5999 v3s16 p2 = p + dirs[i];
6000 // Block position of neighbor (or requested) node
6001 v3s16 blockpos = getNodeBlockPos(p2);
6002 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
6003 if(blockref == NULL)
6005 affected_blocks->insert(blockpos, blockref);
6011 void ClientMap::expireMeshes(bool only_daynight_diffed)
6013 TimeTaker timer("expireMeshes()");
6015 core::map<v2s16, MapSector*>::Iterator si;
6016 si = m_sectors.getIterator();
6017 for(; si.atEnd() == false; si++)
6019 MapSector *sector = si.getNode()->getValue();
6021 core::list< MapBlock * > sectorblocks;
6022 sector->getBlocks(sectorblocks);
6024 core::list< MapBlock * >::Iterator i;
6025 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
6027 MapBlock *block = *i;
6029 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
6035 JMutexAutoLock lock(block->mesh_mutex);
6036 if(block->mesh != NULL)
6038 /*block->mesh->drop();
6039 block->mesh = NULL;*/
6040 block->setMeshExpired(true);
6047 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
6049 assert(mapType() == MAPTYPE_CLIENT);
6052 v3s16 p = blockpos + v3s16(0,0,0);
6053 MapBlock *b = getBlockNoCreate(p);
6054 b->updateMesh(daynight_ratio);
6055 //b->setMeshExpired(true);
6057 catch(InvalidPositionException &e){}
6060 v3s16 p = blockpos + v3s16(-1,0,0);
6061 MapBlock *b = getBlockNoCreate(p);
6062 b->updateMesh(daynight_ratio);
6063 //b->setMeshExpired(true);
6065 catch(InvalidPositionException &e){}
6067 v3s16 p = blockpos + v3s16(0,-1,0);
6068 MapBlock *b = getBlockNoCreate(p);
6069 b->updateMesh(daynight_ratio);
6070 //b->setMeshExpired(true);
6072 catch(InvalidPositionException &e){}
6074 v3s16 p = blockpos + v3s16(0,0,-1);
6075 MapBlock *b = getBlockNoCreate(p);
6076 b->updateMesh(daynight_ratio);
6077 //b->setMeshExpired(true);
6079 catch(InvalidPositionException &e){}
6084 Update mesh of block in which the node is, and if the node is at the
6085 leading edge, update the appropriate leading blocks too.
6087 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
6095 v3s16 blockposes[4];
6096 for(u32 i=0; i<4; i++)
6098 v3s16 np = nodepos + dirs[i];
6099 blockposes[i] = getNodeBlockPos(np);
6100 // Don't update mesh of block if it has been done already
6101 bool already_updated = false;
6102 for(u32 j=0; j<i; j++)
6104 if(blockposes[j] == blockposes[i])
6106 already_updated = true;
6113 MapBlock *b = getBlockNoCreate(blockposes[i]);
6114 b->updateMesh(daynight_ratio);
6119 void ClientMap::PrintInfo(std::ostream &out)
6130 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6135 MapVoxelManipulator::~MapVoxelManipulator()
6137 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6141 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6143 TimeTaker timer1("emerge", &emerge_time);
6145 // Units of these are MapBlocks
6146 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6147 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6149 VoxelArea block_area_nodes
6150 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6152 addArea(block_area_nodes);
6154 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6155 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6156 for(s32 x=p_min.X; x<=p_max.X; x++)
6159 core::map<v3s16, bool>::Node *n;
6160 n = m_loaded_blocks.find(p);
6164 bool block_data_inexistent = false;
6167 TimeTaker timer1("emerge load", &emerge_load_time);
6169 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6170 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6173 dstream<<std::endl;*/
6175 MapBlock *block = m_map->getBlockNoCreate(p);
6176 if(block->isDummy())
6177 block_data_inexistent = true;
6179 block->copyTo(*this);
6181 catch(InvalidPositionException &e)
6183 block_data_inexistent = true;
6186 if(block_data_inexistent)
6188 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6189 // Fill with VOXELFLAG_INEXISTENT
6190 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6191 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6193 s32 i = m_area.index(a.MinEdge.X,y,z);
6194 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6198 m_loaded_blocks.insert(p, !block_data_inexistent);
6201 //dstream<<"emerge done"<<std::endl;
6205 SUGG: Add an option to only update eg. water and air nodes.
6206 This will make it interfere less with important stuff if
6209 void MapVoxelManipulator::blitBack
6210 (core::map<v3s16, MapBlock*> & modified_blocks)
6212 if(m_area.getExtent() == v3s16(0,0,0))
6215 //TimeTaker timer1("blitBack");
6217 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6218 <<m_loaded_blocks.size()<<std::endl;*/
6221 Initialize block cache
6223 v3s16 blockpos_last;
6224 MapBlock *block = NULL;
6225 bool block_checked_in_modified = false;
6227 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6228 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6229 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6233 u8 f = m_flags[m_area.index(p)];
6234 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6237 MapNode &n = m_data[m_area.index(p)];
6239 v3s16 blockpos = getNodeBlockPos(p);
6244 if(block == NULL || blockpos != blockpos_last){
6245 block = m_map->getBlockNoCreate(blockpos);
6246 blockpos_last = blockpos;
6247 block_checked_in_modified = false;
6250 // Calculate relative position in block
6251 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6253 // Don't continue if nothing has changed here
6254 if(block->getNode(relpos) == n)
6257 //m_map->setNode(m_area.MinEdge + p, n);
6258 block->setNode(relpos, n);
6261 Make sure block is in modified_blocks
6263 if(block_checked_in_modified == false)
6265 modified_blocks[blockpos] = block;
6266 block_checked_in_modified = true;
6269 catch(InvalidPositionException &e)
6275 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6276 MapVoxelManipulator(map),
6277 m_create_area(false)
6281 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6285 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6287 // Just create the area so that it can be pointed to
6288 VoxelManipulator::emerge(a, caller_id);
6291 void ManualMapVoxelManipulator::initialEmerge(
6292 v3s16 blockpos_min, v3s16 blockpos_max)
6294 TimeTaker timer1("initialEmerge", &emerge_time);
6296 // Units of these are MapBlocks
6297 v3s16 p_min = blockpos_min;
6298 v3s16 p_max = blockpos_max;
6300 VoxelArea block_area_nodes
6301 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6303 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6306 dstream<<"initialEmerge: area: ";
6307 block_area_nodes.print(dstream);
6308 dstream<<" ("<<size_MB<<"MB)";
6312 addArea(block_area_nodes);
6314 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6315 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6316 for(s32 x=p_min.X; x<=p_max.X; x++)
6319 core::map<v3s16, bool>::Node *n;
6320 n = m_loaded_blocks.find(p);
6324 bool block_data_inexistent = false;
6327 TimeTaker timer1("emerge load", &emerge_load_time);
6329 MapBlock *block = m_map->getBlockNoCreate(p);
6330 if(block->isDummy())
6331 block_data_inexistent = true;
6333 block->copyTo(*this);
6335 catch(InvalidPositionException &e)
6337 block_data_inexistent = true;
6340 if(block_data_inexistent)
6343 Mark area inexistent
6345 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6346 // Fill with VOXELFLAG_INEXISTENT
6347 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6348 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6350 s32 i = m_area.index(a.MinEdge.X,y,z);
6351 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6355 m_loaded_blocks.insert(p, !block_data_inexistent);
6359 void ManualMapVoxelManipulator::blitBackAll(
6360 core::map<v3s16, MapBlock*> * modified_blocks)
6362 if(m_area.getExtent() == v3s16(0,0,0))
6366 Copy data of all blocks
6368 for(core::map<v3s16, bool>::Iterator
6369 i = m_loaded_blocks.getIterator();
6370 i.atEnd() == false; i++)
6372 bool existed = i.getNode()->getValue();
6373 if(existed == false)
6375 v3s16 p = i.getNode()->getKey();
6376 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6379 dstream<<"WARNING: "<<__FUNCTION_NAME
6380 <<": got NULL block "
6381 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6386 block->copyFrom(*this);
6389 modified_blocks->insert(p, block);