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;
2049 Noise functions. Make sure seed is mangled differently in each one.
2052 // Amount of trees per area in nodes
2053 double tree_amount_2d(u64 seed, v2s16 p)
2055 double noise = noise2d_perlin(
2056 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2058 double zeroval = -0.3;
2062 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2065 #define AVERAGE_MUD_AMOUNT 4
2067 double base_rock_level_2d(u64 seed, v2s16 p)
2069 // The base ground level
2070 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
2071 + 20. * noise2d_perlin(
2072 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2073 (seed>>32)+654879876, 6, 0.6);
2075 /*// A bit hillier one
2076 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
2077 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2078 (seed>>27)+90340, 6, 0.69);
2082 // Higher ground level
2083 double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin(
2084 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2085 seed+85039, 5, 0.69);
2086 //higher = 30; // For debugging
2088 // Limit higher to at least base
2092 // Steepness factor of cliffs
2093 double b = 1.0 + 1.0 * noise2d_perlin(
2094 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2096 b = rangelim(b, 0.0, 1000.0);
2099 b = rangelim(b, 3.0, 1000.0);
2100 //dstream<<"b="<<b<<std::endl;
2103 // Offset to more low
2104 double a_off = -0.2;
2105 // High/low selector
2106 /*double a = 0.5 + b * (a_off + noise2d_perlin(
2107 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
2108 seed-359, 6, 0.7));*/
2109 double a = (double)0.5 + b * (a_off + noise2d_perlin(
2110 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
2111 seed-359, 5, 0.60));
2113 a = rangelim(a, 0.0, 1.0);
2115 //dstream<<"a="<<a<<std::endl;
2117 double h = base*(1.0-a) + higher*a;
2124 double get_mud_add_amount(u64 seed, v2s16 p)
2126 return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
2127 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
2128 seed+91013, 3, 0.55));
2132 Adds random objects to block, depending on the content of the block
2134 void addRandomObjects(MapBlock *block)
2136 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
2137 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
2139 bool last_node_walkable = false;
2140 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
2143 MapNode n = block->getNodeNoEx(p);
2144 if(n.d == CONTENT_IGNORE)
2146 if(content_features(n.d).liquid_type != LIQUID_NONE)
2148 if(content_features(n.d).walkable)
2150 last_node_walkable = true;
2153 if(last_node_walkable)
2155 // If block contains light information
2156 if(content_features(n.d).param_type == CPT_LIGHT)
2158 if(n.getLight(LIGHTBANK_DAY) <= 3)
2160 if(myrand() % 300 == 0)
2162 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2164 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
2165 std::string data = obj->getStaticData();
2166 StaticObject s_obj(obj->getType(),
2167 obj->getBasePosition(), data);
2169 block->m_static_objects.insert(0, s_obj);
2170 block->m_static_objects.insert(0, s_obj);
2171 block->m_static_objects.insert(0, s_obj);
2172 block->m_static_objects.insert(0, s_obj);
2173 block->m_static_objects.insert(0, s_obj);
2174 block->m_static_objects.insert(0, s_obj);
2177 if(myrand() % 300 == 0)
2179 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
2181 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
2182 std::string data = obj->getStaticData();
2183 StaticObject s_obj(obj->getType(),
2184 obj->getBasePosition(), data);
2186 block->m_static_objects.insert(0, s_obj);
2192 last_node_walkable = false;
2195 block->setChangedFlag();
2198 #define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
2201 This is the main map generation method
2204 void makeChunk(ChunkMakeData *data)
2209 s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
2210 s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
2211 s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
2212 u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2213 *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
2214 *(u32)h_blocks*MAP_BLOCKSIZE;
2215 v3s16 bigarea_blocks_min(
2216 data->sectorpos_bigbase.X,
2218 data->sectorpos_bigbase.Y
2220 v3s16 bigarea_blocks_max(
2221 data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
2223 data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
2225 s16 lighting_min_d = 0-data->max_spread_amount;
2226 s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
2227 + data->max_spread_amount-1;
2230 data->vmanip.clearFlag(0xff);
2232 TimeTaker timer_generate("makeChunk() generate");
2234 // Maximum height of the stone surface and obstacles.
2235 // This is used to disable cave generation from going too high.
2236 s16 stone_surface_max_y = 0;
2239 Generate general ground level to full area
2243 TimeTaker timer1("Generating ground level");
2246 NoiseBuffer noisebuf1;
2247 //NoiseBuffer noisebuf2;
2250 data->sectorpos_bigbase.X*MAP_BLOCKSIZE,
2252 data->sectorpos_bigbase.Y*MAP_BLOCKSIZE
2254 v3f maxpos_f = minpos_f + v3f(
2255 data->sectorpos_bigbase_size*MAP_BLOCKSIZE,
2256 y_nodes_max-y_nodes_min,
2257 data->sectorpos_bigbase_size*MAP_BLOCKSIZE
2259 v3f samplelength_f = v3f(4.0, 4.0, 4.0);
2261 TimeTaker timer("noisebuf.create");
2263 noisebuf1.create(data->seed+25104, 6, 0.60, 200.0,
2264 minpos_f.X, minpos_f.Y, minpos_f.Z,
2265 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2266 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2267 /*noisebuf1.create(data->seed+25104, 3, 0.60, 25.0,
2268 minpos_f.X, minpos_f.Y, minpos_f.Z,
2269 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2270 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
2271 noisebuf2.create(data->seed+25105, 4, 0.50, 200.0,
2272 minpos_f.X, minpos_f.Y, minpos_f.Z,
2273 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
2274 samplelength_f.X, samplelength_f.Y, samplelength_f.Z);*/
2277 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2278 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2281 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2283 // Ground height at this point
2284 float surface_y_f = 0.0;
2286 // Use perlin noise for ground height
2287 surface_y_f = base_rock_level_2d(data->seed, p2d);
2288 //surface_y_f = base_rock_level_2d(data->seed, p2d);
2290 // Convert to integer
2291 s16 surface_y = (s16)surface_y_f;
2294 if(surface_y > stone_surface_max_y)
2295 stone_surface_max_y = surface_y;
2298 Fill ground with stone
2301 // Use fast index incrementing
2302 v3s16 em = data->vmanip.m_area.getExtent();
2303 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2304 for(s16 y=y_nodes_min; y<=y_nodes_max; y++)
2306 // Skip if already generated.
2307 // This is done here because there might be a cave at
2308 // any point in ground, which could look like it
2309 // wasn't generated.
2310 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2313 /*s16 noiseval = 50.0 * noise3d_perlin(
2314 0.5+(float)p2d.X/100.0,
2316 0.5+(float)p2d.Y/100.0,
2317 data->seed+123, 5, 0.5);*/
2318 double noiseval = 64.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2319 /*double noiseval = 30.0 * noisebuf1.get(p2d.X, y, p2d.Y);
2320 noiseval *= MYMAX(0, -0.2 + noisebuf2.get(p2d.X, y, p2d.Y));*/
2322 //if(y < surface_y + noiseval)
2325 data->vmanip.m_data[i].d = CONTENT_STONE;
2327 data->vmanip.m_area.add_y(em, i, 1);
2334 for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
2335 for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
2338 v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
2341 Skip of already generated
2344 v3s16 p(p2d.X, y_nodes_min, p2d.Y);
2345 if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
2349 // Ground height at this point
2350 float surface_y_f = 0.0;
2352 // Use perlin noise for ground height
2353 surface_y_f = base_rock_level_2d(data->seed, p2d);
2355 /*// Experimental stuff
2357 float a = highlands_level_2d(data->seed, p2d);
2362 // Convert to integer
2363 s16 surface_y = (s16)surface_y_f;
2366 if(surface_y > stone_surface_max_y)
2367 stone_surface_max_y = surface_y;
2370 Fill ground with stone
2373 // Use fast index incrementing
2374 v3s16 em = data->vmanip.m_area.getExtent();
2375 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
2376 for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
2378 // Skip if already generated.
2379 // This is done here because there might be a cave at
2380 // any point in ground, which could look like it
2381 // wasn't generated.
2382 if(data->vmanip.m_data[i].d != CONTENT_AIR)
2385 data->vmanip.m_data[i].d = CONTENT_STONE;
2387 data->vmanip.m_area.add_y(em, i, 1);
2396 Randomize some parameters
2399 //s32 stone_obstacle_count = 0;
2400 /*s32 stone_obstacle_count =
2401 rangelim((1.0+noise2d(data->seed+897,
2402 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2404 //s16 stone_obstacle_max_height = 0;
2405 /*s16 stone_obstacle_max_height =
2406 rangelim((1.0+noise2d(data->seed+5902,
2407 data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
2410 Loop this part, it will make stuff look older and newer nicely
2412 const u32 age_loops = 2;
2413 for(u32 i_age=0; i_age<age_loops; i_age++)
2415 /******************************
2416 BEGINNING OF AGING LOOP
2417 ******************************/
2422 //TimeTaker timer1("caves");
2427 u32 caves_count = relative_volume / 400000;
2428 u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
2429 if(stone_surface_max_y < WATER_LEVEL)
2431 /*u32 caves_count = 0;
2432 u32 bruises_count = 0;*/
2433 for(u32 jj=0; jj<caves_count+bruises_count; jj++)
2435 s16 min_tunnel_diameter = 3;
2436 s16 max_tunnel_diameter = 5;
2437 u16 tunnel_routepoints = 20;
2439 v3f main_direction(0,0,0);
2441 bool bruise_surface = (jj > caves_count);
2445 min_tunnel_diameter = 5;
2446 max_tunnel_diameter = myrand_range(10, 20);
2447 /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
2448 max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
2450 /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
2451 data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
2453 tunnel_routepoints = 5;
2459 // Allowed route area size in nodes
2461 data->sectorpos_base_size*MAP_BLOCKSIZE,
2462 h_blocks*MAP_BLOCKSIZE,
2463 data->sectorpos_base_size*MAP_BLOCKSIZE
2466 // Area starting point in nodes
2468 data->sectorpos_base.X*MAP_BLOCKSIZE,
2469 data->y_blocks_min*MAP_BLOCKSIZE,
2470 data->sectorpos_base.Y*MAP_BLOCKSIZE
2474 //(this should be more than the maximum radius of the tunnel)
2475 //s16 insure = 5; // Didn't work with max_d = 20
2477 s16 more = data->max_spread_amount - max_tunnel_diameter/2 - insure;
2478 ar += v3s16(1,0,1) * more * 2;
2479 of -= v3s16(1,0,1) * more;
2481 s16 route_y_min = 0;
2482 // Allow half a diameter + 7 over stone surface
2483 s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7;
2485 /*// If caves, don't go through surface too often
2486 if(bruise_surface == false)
2487 route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
2489 // Limit maximum to area
2490 route_y_max = rangelim(route_y_max, 0, ar.Y-1);
2494 /*// Minimum is at y=0
2495 route_y_min = -of.Y - 0;*/
2496 // Minimum is at y=max_tunnel_diameter/4
2497 //route_y_min = -of.Y + max_tunnel_diameter/4;
2498 //s16 min = -of.Y + max_tunnel_diameter/4;
2499 s16 min = -of.Y + 0;
2500 route_y_min = myrand_range(min, min + max_tunnel_diameter);
2501 route_y_min = rangelim(route_y_min, 0, route_y_max);
2504 /*dstream<<"route_y_min = "<<route_y_min
2505 <<", route_y_max = "<<route_y_max<<std::endl;*/
2507 s16 route_start_y_min = route_y_min;
2508 s16 route_start_y_max = route_y_max;
2510 // Start every 2nd cave from surface
2511 bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
2513 if(coming_from_surface)
2515 route_start_y_min = -of.Y + stone_surface_max_y + 10;
2518 route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
2519 route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
2521 // Randomize starting position
2523 (float)(myrand()%ar.X)+0.5,
2524 (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
2525 (float)(myrand()%ar.Z)+0.5
2528 MapNode airnode(CONTENT_AIR);
2531 Generate some tunnel starting from orp
2534 for(u16 j=0; j<tunnel_routepoints; j++)
2536 if(j%7==0 && bruise_surface == false)
2538 main_direction = v3f(
2539 ((float)(myrand()%20)-(float)10)/10,
2540 ((float)(myrand()%20)-(float)10)/30,
2541 ((float)(myrand()%20)-(float)10)/10
2543 main_direction *= (float)myrand_range(1, 3);
2547 s16 min_d = min_tunnel_diameter;
2548 s16 max_d = max_tunnel_diameter;
2549 s16 rs = myrand_range(min_d, max_d);
2554 maxlen = v3s16(rs*7,rs*7,rs*7);
2558 maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
2563 if(coming_from_surface && j < 3)
2566 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2567 (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
2568 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2574 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2575 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2576 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2580 vec += main_direction;
2585 else if(rp.X >= ar.X)
2587 if(rp.Y < route_y_min)
2589 else if(rp.Y >= route_y_max)
2590 rp.Y = route_y_max-1;
2593 else if(rp.Z >= ar.Z)
2597 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2599 v3f fp = orp + vec * f;
2600 v3s16 cp(fp.X, fp.Y, fp.Z);
2603 s16 d1 = d0 + rs - 1;
2604 for(s16 z0=d0; z0<=d1; z0++)
2606 //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
2607 s16 si = rs - MYMAX(0, abs(z0)-rs/7);
2608 for(s16 x0=-si; x0<=si-1; x0++)
2610 s16 maxabsxz = MYMAX(abs(x0), abs(z0));
2611 //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
2612 s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
2613 //s16 si2 = rs - abs(x0);
2614 for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
2620 /*if(isInArea(p, ar) == false)
2622 // Check only height
2623 if(y < 0 || y >= ar.Y)
2627 //assert(data->vmanip.m_area.contains(p));
2628 if(data->vmanip.m_area.contains(p) == false)
2630 dstream<<"WARNING: "<<__FUNCTION_NAME
2631 <<":"<<__LINE__<<": "
2632 <<"point not in area"
2637 // Just set it to air, it will be changed to
2639 u32 i = data->vmanip.m_area.index(p);
2640 data->vmanip.m_data[i] = airnode;
2642 if(bruise_surface == false)
2645 data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
2663 //TimeTaker timer1("ore veins");
2668 for(u32 jj=0; jj<relative_volume/1000; jj++)
2670 s16 max_vein_diameter = 3;
2672 // Allowed route area size in nodes
2674 data->sectorpos_base_size*MAP_BLOCKSIZE,
2675 h_blocks*MAP_BLOCKSIZE,
2676 data->sectorpos_base_size*MAP_BLOCKSIZE
2679 // Area starting point in nodes
2681 data->sectorpos_base.X*MAP_BLOCKSIZE,
2682 data->y_blocks_min*MAP_BLOCKSIZE,
2683 data->sectorpos_base.Y*MAP_BLOCKSIZE
2687 //(this should be more than the maximum radius of the tunnel)
2689 s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
2690 ar += v3s16(1,0,1) * more * 2;
2691 of -= v3s16(1,0,1) * more;
2693 // Randomize starting position
2695 (float)(myrand()%ar.X)+0.5,
2696 (float)(myrand()%ar.Y)+0.5,
2697 (float)(myrand()%ar.Z)+0.5
2700 // Randomize mineral
2703 mineral = MINERAL_COAL;
2705 mineral = MINERAL_IRON;
2708 Generate some vein starting from orp
2711 for(u16 j=0; j<2; j++)
2714 (float)(myrand()%ar.X)+0.5,
2715 (float)(myrand()%ar.Y)+0.5,
2716 (float)(myrand()%ar.Z)+0.5
2718 v3f vec = rp - orp;*/
2720 v3s16 maxlen(5, 5, 5);
2722 (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
2723 (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
2724 (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
2729 else if(rp.X >= ar.X)
2733 else if(rp.Y >= ar.Y)
2737 else if(rp.Z >= ar.Z)
2743 s16 max_d = max_vein_diameter;
2744 s16 rs = myrand_range(min_d, max_d);
2746 for(float f=0; f<1.0; f+=1.0/vec.getLength())
2748 v3f fp = orp + vec * f;
2749 v3s16 cp(fp.X, fp.Y, fp.Z);
2751 s16 d1 = d0 + rs - 1;
2752 for(s16 z0=d0; z0<=d1; z0++)
2754 s16 si = rs - abs(z0);
2755 for(s16 x0=-si; x0<=si-1; x0++)
2757 s16 si2 = rs - abs(x0);
2758 for(s16 y0=-si2+1; y0<=si2-1; y0++)
2760 // Don't put mineral to every place
2768 /*if(isInArea(p, ar) == false)
2770 // Check only height
2771 if(y < 0 || y >= ar.Y)
2775 assert(data->vmanip.m_area.contains(p));
2777 // Just set it to air, it will be changed to
2779 u32 i = data->vmanip.m_area.index(p);
2780 MapNode *n = &data->vmanip.m_data[i];
2781 if(n->d == CONTENT_STONE)
2799 TimeTaker timer1("add mud");
2802 Add mud to the central chunk
2805 for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
2806 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
2808 // Node position in 2d
2809 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2811 // Randomize mud amount
2812 s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0;
2814 // Find ground level
2815 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
2818 If topmost node is grass, change it to mud.
2819 It might be if it was flown to there from a neighboring
2820 chunk and then converted.
2823 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
2824 MapNode *n = &data->vmanip.m_data[i];
2825 if(n->d == CONTENT_GRASS)
2826 *n = MapNode(CONTENT_MUD);
2827 //n->d = CONTENT_MUD;
2835 v3s16 em = data->vmanip.m_area.getExtent();
2836 s16 y_start = surface_y+1;
2837 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
2838 for(s16 y=y_start; y<=y_nodes_max; y++)
2840 if(mudcount >= mud_add_amount)
2843 MapNode &n = data->vmanip.m_data[i];
2844 n = MapNode(CONTENT_MUD);
2845 //n.d = CONTENT_MUD;
2848 data->vmanip.m_area.add_y(em, i, 1);
2860 TimeTaker timer1("flow mud");
2863 Flow mud away from steep edges
2866 // Limit area by 1 because mud is flown into neighbors.
2867 s16 mudflow_minpos = 0-data->max_spread_amount+1;
2868 s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
2870 // Iterate a few times
2871 for(s16 k=0; k<3; k++)
2874 for(s16 x=mudflow_minpos;
2877 for(s16 z=mudflow_minpos;
2881 // Invert coordinates every 2nd iteration
2884 x = mudflow_maxpos - (x-mudflow_minpos);
2885 z = mudflow_maxpos - (z-mudflow_minpos);
2888 // Node position in 2d
2889 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
2891 v3s16 em = data->vmanip.m_area.getExtent();
2892 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2899 for(; y>=y_nodes_min; y--)
2901 n = &data->vmanip.m_data[i];
2902 //if(content_walkable(n->d))
2904 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
2907 data->vmanip.m_area.add_y(em, i, -1);
2910 // Stop if out of area
2911 //if(data->vmanip.m_area.contains(i) == false)
2915 /*// If not mud, do nothing to it
2916 MapNode *n = &data->vmanip.m_data[i];
2917 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
2921 Don't flow it if the stuff under it is not mud
2925 data->vmanip.m_area.add_y(em, i2, -1);
2926 // Cancel if out of area
2927 if(data->vmanip.m_area.contains(i2) == false)
2929 MapNode *n2 = &data->vmanip.m_data[i2];
2930 if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
2934 // Make it exactly mud
2937 /*s16 recurse_count = 0;
2941 v3s16(0,0,1), // back
2942 v3s16(1,0,0), // right
2943 v3s16(0,0,-1), // front
2944 v3s16(-1,0,0), // left
2947 // Theck that upper is air or doesn't exist.
2948 // Cancel dropping if upper keeps it in place
2950 data->vmanip.m_area.add_y(em, i3, 1);
2951 if(data->vmanip.m_area.contains(i3) == true
2952 && content_walkable(data->vmanip.m_data[i3].d) == true)
2959 for(u32 di=0; di<4; di++)
2961 v3s16 dirp = dirs4[di];
2964 data->vmanip.m_area.add_p(em, i2, dirp);
2965 // Fail if out of area
2966 if(data->vmanip.m_area.contains(i2) == false)
2968 // Check that side is air
2969 MapNode *n2 = &data->vmanip.m_data[i2];
2970 if(content_walkable(n2->d))
2972 // Check that under side is air
2973 data->vmanip.m_area.add_y(em, i2, -1);
2974 if(data->vmanip.m_area.contains(i2) == false)
2976 n2 = &data->vmanip.m_data[i2];
2977 if(content_walkable(n2->d))
2979 /*// Check that under that is air (need a drop of 2)
2980 data->vmanip.m_area.add_y(em, i2, -1);
2981 if(data->vmanip.m_area.contains(i2) == false)
2983 n2 = &data->vmanip.m_data[i2];
2984 if(content_walkable(n2->d))
2986 // Loop further down until not air
2988 data->vmanip.m_area.add_y(em, i2, -1);
2989 // Fail if out of area
2990 if(data->vmanip.m_area.contains(i2) == false)
2992 n2 = &data->vmanip.m_data[i2];
2993 }while(content_walkable(n2->d) == false);
2994 // Loop one up so that we're in air
2995 data->vmanip.m_area.add_y(em, i2, 1);
2996 n2 = &data->vmanip.m_data[i2];
2998 // Move mud to new place
3000 // Set old place to be air
3001 *n = MapNode(CONTENT_AIR);
3017 TimeTaker timer1("add water");
3020 Add water to the central chunk (and a bit more)
3023 for(s16 x=0-data->max_spread_amount;
3024 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3026 for(s16 z=0-data->max_spread_amount;
3027 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3030 // Node position in 2d
3031 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3033 // Find ground level
3034 //s16 surface_y = find_ground_level(data->vmanip, p2d);
3037 If ground level is over water level, skip.
3038 NOTE: This leaves caves near water without water,
3039 which looks especially crappy when the nearby water
3040 won't start flowing either for some reason
3042 /*if(surface_y > WATER_LEVEL)
3049 v3s16 em = data->vmanip.m_area.getExtent();
3050 u8 light = LIGHT_MAX;
3051 // Start at global water surface level
3052 s16 y_start = WATER_LEVEL;
3053 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3054 MapNode *n = &data->vmanip.m_data[i];
3056 for(s16 y=y_start; y>=y_nodes_min; y--)
3058 n = &data->vmanip.m_data[i];
3060 // Stop when there is no water and no air
3061 if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
3062 && n->d != CONTENT_WATER)
3068 // Make water only not in caves
3069 if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
3071 n->d = CONTENT_WATERSOURCE;
3072 //n->setLight(LIGHTBANK_DAY, light);
3074 // Add to transforming liquid queue (in case it'd
3076 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3077 data->transforming_liquid.push_back(p);
3081 data->vmanip.m_area.add_y(em, i, -1);
3093 /***********************
3095 ************************/
3099 //TimeTaker timer1("convert mud to sand");
3105 //s16 mud_add_amount = myrand_range(2, 4);
3106 //s16 mud_add_amount = 0;
3108 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3109 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3110 for(s16 x=0-data->max_spread_amount+1;
3111 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3113 for(s16 z=0-data->max_spread_amount+1;
3114 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3117 // Node position in 2d
3118 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3120 // Determine whether to have sand here
3121 double sandnoise = noise2d_perlin(
3122 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3123 data->seed+59420, 3, 0.50);
3125 bool have_sand = (sandnoise > -0.15);
3127 if(have_sand == false)
3130 // Find ground level
3131 s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
3133 if(surface_y > WATER_LEVEL + 2)
3137 v3s16 em = data->vmanip.m_area.getExtent();
3138 s16 y_start = surface_y;
3139 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3140 u32 not_sand_counter = 0;
3141 for(s16 y=y_start; y>=y_nodes_min; y--)
3143 MapNode *n = &data->vmanip.m_data[i];
3144 if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
3146 n->d = CONTENT_SAND;
3151 if(not_sand_counter > 3)
3155 data->vmanip.m_area.add_y(em, i, -1);
3167 //TimeTaker timer1("generate trees");
3173 // Divide area into parts
3175 s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
3176 double area = sidelen * sidelen;
3177 for(s16 x0=0; x0<div; x0++)
3178 for(s16 z0=0; z0<div; z0++)
3180 // Center position of part of division
3182 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
3183 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
3185 // Minimum edge of part of division
3187 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
3188 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
3190 // Maximum edge of part of division
3192 data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
3193 data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
3196 u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
3197 // Put trees in random places on part of division
3198 for(u32 i=0; i<tree_count; i++)
3200 s16 x = myrand_range(p2d_min.X, p2d_max.X);
3201 s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
3202 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3203 // Don't make a tree under water level
3206 // Don't make a tree so high that it doesn't fit
3207 if(y > y_nodes_max - 6)
3211 Trees grow only on mud and grass
3214 u32 i = data->vmanip.m_area.index(v3s16(p));
3215 MapNode *n = &data->vmanip.m_data[i];
3216 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3221 make_tree(data->vmanip, p);
3224 /*u32 tree_max = relative_area / 60;
3225 //u32 count = myrand_range(0, tree_max);
3226 for(u32 i=0; i<count; i++)
3228 s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3229 s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
3230 x += data->sectorpos_base.X*MAP_BLOCKSIZE;
3231 z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
3232 s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3233 // Don't make a tree under water level
3238 make_tree(data->vmanip, p);
3248 //TimeTaker timer1("grow grass");
3254 /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
3255 for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
3256 for(s16 x=0-data->max_spread_amount;
3257 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3259 for(s16 z=0-data->max_spread_amount;
3260 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
3263 // Node position in 2d
3264 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3267 Find the lowest surface to which enough light ends up
3270 Basically just wait until not air and not leaves.
3274 v3s16 em = data->vmanip.m_area.getExtent();
3275 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
3277 // Go to ground level
3278 for(y=y_nodes_max; y>=y_nodes_min; y--)
3280 MapNode &n = data->vmanip.m_data[i];
3281 if(n.d != CONTENT_AIR
3282 && n.d != CONTENT_LEAVES)
3284 data->vmanip.m_area.add_y(em, i, -1);
3286 if(y >= y_nodes_min)
3289 surface_y = y_nodes_min;
3292 u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
3293 MapNode *n = &data->vmanip.m_data[i];
3294 if(n->d == CONTENT_MUD)
3295 n->d = CONTENT_GRASS;
3302 Initial lighting (sunlight)
3305 core::map<v3s16, bool> light_sources;
3308 // 750ms @cs=8, can't optimize more
3309 TimeTaker timer1("initial lighting");
3311 // NOTE: This is no used... umm... for some reason!
3314 Go through the edges and add all nodes that have light to light_sources
3318 for(s16 i=0; i<4; i++)
3320 for(s16 j=lighting_min_d;
3327 if(i == 0 || i == 1)
3329 x = (i==0) ? lighting_min_d : lighting_max_d;
3338 z = (i==0) ? lighting_min_d : lighting_max_d;
3345 // Node position in 2d
3346 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3349 v3s16 em = data->vmanip.m_area.getExtent();
3350 s16 y_start = y_nodes_max;
3351 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3352 for(s16 y=y_start; y>=y_nodes_min; y--)
3354 MapNode *n = &data->vmanip.m_data[i];
3355 if(n->getLight(LIGHTBANK_DAY) != 0)
3357 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3359 //NOTE: This is broken, at least the index has to
3368 Go through the edges and apply sunlight to them, not caring
3373 for(s16 i=0; i<4; i++)
3375 for(s16 j=lighting_min_d;
3382 if(i == 0 || i == 1)
3384 x = (i==0) ? lighting_min_d : lighting_max_d;
3393 z = (i==0) ? lighting_min_d : lighting_max_d;
3400 // Node position in 2d
3401 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3403 // Loop from top to down
3405 u8 light = LIGHT_SUN;
3406 v3s16 em = data->vmanip.m_area.getExtent();
3407 s16 y_start = y_nodes_max;
3408 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3409 for(s16 y=y_start; y>=y_nodes_min; y--)
3411 MapNode *n = &data->vmanip.m_data[i];
3412 if(light_propagates_content(n->d) == false)
3416 else if(light != LIGHT_SUN
3417 || sunlight_propagates_content(n->d) == false)
3423 n->setLight(LIGHTBANK_DAY, light);
3424 n->setLight(LIGHTBANK_NIGHT, 0);
3428 // Insert light source
3429 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3432 // Increment index by y
3433 data->vmanip.m_area.add_y(em, i, -1);
3439 /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
3440 for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
3441 /*for(s16 x=0-data->max_spread_amount+1;
3442 x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3444 for(s16 z=0-data->max_spread_amount+1;
3445 z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
3449 This has to be 1 smaller than the actual area, because
3450 neighboring nodes are checked.
3452 for(s16 x=lighting_min_d+1;
3453 x<=lighting_max_d-1;
3455 for(s16 z=lighting_min_d+1;
3456 z<=lighting_max_d-1;
3459 // Node position in 2d
3460 v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
3463 Apply initial sunlight
3466 u8 light = LIGHT_SUN;
3467 bool add_to_sources = false;
3468 v3s16 em = data->vmanip.m_area.getExtent();
3469 s16 y_start = y_nodes_max;
3470 u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
3471 for(s16 y=y_start; y>=y_nodes_min; y--)
3473 MapNode *n = &data->vmanip.m_data[i];
3475 if(light_propagates_content(n->d) == false)
3479 else if(light != LIGHT_SUN
3480 || sunlight_propagates_content(n->d) == false)
3486 // This doesn't take much time
3487 if(add_to_sources == false)
3490 Check sides. If side is not air or water, start
3491 adding to light_sources.
3494 v3s16(0,0,1), // back
3495 v3s16(1,0,0), // right
3496 v3s16(0,0,-1), // front
3497 v3s16(-1,0,0), // left
3499 for(u32 di=0; di<4; di++)
3501 v3s16 dirp = dirs4[di];
3503 data->vmanip.m_area.add_p(em, i2, dirp);
3504 MapNode *n2 = &data->vmanip.m_data[i2];
3506 n2->d != CONTENT_AIR
3507 && n2->d != CONTENT_WATERSOURCE
3508 && n2->d != CONTENT_WATER
3510 add_to_sources = true;
3516 n->setLight(LIGHTBANK_DAY, light);
3517 n->setLight(LIGHTBANK_NIGHT, 0);
3519 // This doesn't take much time
3520 if(light != 0 && add_to_sources)
3522 // Insert light source
3523 light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
3526 // Increment index by y
3527 data->vmanip.m_area.add_y(em, i, -1);
3535 // Spread light around
3537 TimeTaker timer("makeChunk() spreadLight");
3538 data->vmanip.spreadLight(LIGHTBANK_DAY, light_sources);
3545 timer_generate.stop();
3548 //###################################################################
3549 //###################################################################
3550 //###################################################################
3551 //###################################################################
3552 //###################################################################
3553 //###################################################################
3554 //###################################################################
3555 //###################################################################
3556 //###################################################################
3557 //###################################################################
3558 //###################################################################
3559 //###################################################################
3560 //###################################################################
3561 //###################################################################
3562 //###################################################################
3564 void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
3566 if(m_chunksize == 0)
3574 // The distance how far into the neighbors the generator is allowed to go.
3575 s16 max_spread_amount_sectors = 2;
3576 assert(max_spread_amount_sectors <= m_chunksize);
3577 s16 max_spread_amount = max_spread_amount_sectors * MAP_BLOCKSIZE;
3579 s16 y_blocks_min = -4;
3580 s16 y_blocks_max = 3;
3582 v2s16 sectorpos_base = chunk_to_sector(chunkpos);
3583 s16 sectorpos_base_size = m_chunksize;
3585 v2s16 sectorpos_bigbase =
3586 sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
3587 s16 sectorpos_bigbase_size =
3588 sectorpos_base_size + 2 * max_spread_amount_sectors;
3591 data.chunkpos = chunkpos;
3592 data.y_blocks_min = y_blocks_min;
3593 data.y_blocks_max = y_blocks_max;
3594 data.sectorpos_base = sectorpos_base;
3595 data.sectorpos_base_size = sectorpos_base_size;
3596 data.sectorpos_bigbase = sectorpos_bigbase;
3597 data.sectorpos_bigbase_size = sectorpos_bigbase_size;
3598 data.max_spread_amount = max_spread_amount;
3601 Create the whole area of this and the neighboring chunks
3604 TimeTaker timer("initChunkMake() create area");
3606 for(s16 x=0; x<sectorpos_bigbase_size; x++)
3607 for(s16 z=0; z<sectorpos_bigbase_size; z++)
3609 v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z);
3610 ServerMapSector *sector = createSector(sectorpos);
3613 for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
3615 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3616 MapBlock *block = createBlock(blockpos);
3618 // Lighting won't be calculated
3619 //block->setLightingExpired(true);
3620 // Lighting will be calculated
3621 block->setLightingExpired(false);
3624 Block gets sunlight if this is true.
3626 This should be set to true when the top side of a block
3627 is completely exposed to the sky.
3629 Actually this doesn't matter now because the
3630 initial lighting is done here.
3632 block->setIsUnderground(y != y_blocks_max);
3638 Now we have a big empty area.
3640 Make a ManualMapVoxelManipulator that contains this and the
3644 v3s16 bigarea_blocks_min(
3645 sectorpos_bigbase.X,
3649 v3s16 bigarea_blocks_max(
3650 sectorpos_bigbase.X + sectorpos_bigbase_size - 1,
3652 sectorpos_bigbase.Y + sectorpos_bigbase_size - 1
3655 data.vmanip.setMap(this);
3658 TimeTaker timer("initChunkMake() initialEmerge");
3659 data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3664 MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
3665 core::map<v3s16, MapBlock*> &changed_blocks)
3671 Blit generated stuff to map
3675 //TimeTaker timer("generateChunkRaw() blitBackAll");
3676 data.vmanip.blitBackAll(&changed_blocks);
3680 Update day/night difference cache of the MapBlocks
3683 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
3684 i.atEnd() == false; i++)
3686 MapBlock *block = i.getNode()->getValue();
3687 block->updateDayNightDiff();
3692 Copy transforming liquid information
3694 while(data.transforming_liquid.size() > 0)
3696 v3s16 p = data.transforming_liquid.pop_front();
3697 m_transforming_liquid.push_back(p);
3701 Add random objects to blocks
3704 for(s16 x=0; x<data.sectorpos_base_size; x++)
3705 for(s16 z=0; z<data.sectorpos_base_size; z++)
3707 v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
3708 ServerMapSector *sector = createSector(sectorpos);
3711 for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
3713 v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
3714 MapBlock *block = createBlock(blockpos);
3715 addRandomObjects(block);
3721 Create chunk metadata
3724 for(s16 x=-1; x<=1; x++)
3725 for(s16 y=-1; y<=1; y++)
3727 v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
3728 // Add chunk meta information
3729 MapChunk *chunk = getChunk(chunkpos0);
3732 chunk = new MapChunk();
3733 m_chunks.insert(chunkpos0, chunk);
3735 //chunk->setIsVolatile(true);
3736 if(chunk->getGenLevel() > GENERATED_PARTLY)
3737 chunk->setGenLevel(GENERATED_PARTLY);
3741 Set central chunk non-volatile
3743 MapChunk *chunk = getChunk(data.chunkpos);
3746 //chunk->setIsVolatile(false);
3747 chunk->setGenLevel(GENERATED_FULLY);
3750 Save changed parts of map
3759 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
3760 core::map<v3s16, MapBlock*> &changed_blocks,
3763 DSTACK(__FUNCTION_NAME);
3766 Don't generate if already fully generated
3770 MapChunk *chunk = getChunk(chunkpos);
3771 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3773 dstream<<"generateChunkRaw(): Chunk "
3774 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3775 <<" already generated"<<std::endl;
3780 dstream<<"generateChunkRaw(): Generating chunk "
3781 <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
3784 TimeTaker timer("generateChunkRaw()");
3788 // Initialize generation
3789 initChunkMake(data, chunkpos);
3794 // Finalize generation
3795 MapChunk *chunk = finishChunkMake(data, changed_blocks);
3798 Return central chunk (which was requested)
3804 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
3805 core::map<v3s16, MapBlock*> &changed_blocks)
3807 dstream<<"generateChunk(): Generating chunk "
3808 <<"("<<chunkpos1.X<<","<<chunkpos1.Y<<")"
3811 /*for(s16 x=-1; x<=1; x++)
3812 for(s16 y=-1; y<=1; y++)*/
3813 for(s16 x=-0; x<=0; x++)
3814 for(s16 y=-0; y<=0; y++)
3816 v2s16 chunkpos0 = chunkpos1 + v2s16(x,y);
3817 MapChunk *chunk = getChunk(chunkpos0);
3818 // Skip if already generated
3819 if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
3821 generateChunkRaw(chunkpos0, changed_blocks);
3824 assert(chunkNonVolatile(chunkpos1));
3826 MapChunk *chunk = getChunk(chunkpos1);
3831 ServerMapSector * ServerMap::createSector(v2s16 p2d)
3833 DSTACKF("%s: p2d=(%d,%d)",
3838 Check if it exists already in memory
3840 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3845 Try to load it from disk (with blocks)
3847 if(loadSectorFull(p2d) == true)
3849 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
3852 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
3853 throw InvalidPositionException("");
3859 Do not create over-limit
3861 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3862 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3863 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
3864 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
3865 throw InvalidPositionException("createSector(): pos. over limit");
3868 Generate blank sector
3871 sector = new ServerMapSector(this, p2d);
3873 // Sector position on map in nodes
3874 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
3879 m_sectors.insert(p2d, sector);
3885 MapSector * ServerMap::emergeSector(v2s16 p2d,
3886 core::map<v3s16, MapBlock*> &changed_blocks)
3888 DSTACK("%s: p2d=(%d,%d)",
3895 v2s16 chunkpos = sector_to_chunk(p2d);
3896 /*bool chunk_nonvolatile = false;
3897 MapChunk *chunk = getChunk(chunkpos);
3898 if(chunk && chunk->getIsVolatile() == false)
3899 chunk_nonvolatile = true;*/
3900 bool chunk_nonvolatile = chunkNonVolatile(chunkpos);
3903 If chunk is not fully generated, generate chunk
3905 if(chunk_nonvolatile == false)
3907 // Generate chunk and neighbors
3908 generateChunk(chunkpos, changed_blocks);
3912 Return sector if it exists now
3914 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3919 Try to load it from disk
3921 if(loadSectorFull(p2d) == true)
3923 MapSector *sector = getSectorNoGenerateNoEx(p2d);
3926 dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
3927 throw InvalidPositionException("");
3933 generateChunk should have generated the sector
3937 dstream<<"WARNING: ServerMap::emergeSector: Cannot find sector ("
3938 <<p2d.X<<","<<p2d.Y<<" and chunk is already generated. "
3942 dstream<<"WARNING: Creating an empty sector."<<std::endl;
3944 return createSector(p2d);
3949 dstream<<"WARNING: Forcing regeneration of chunk."<<std::endl;
3952 generateChunkRaw(chunkpos, changed_blocks, true);
3955 Return sector if it exists now
3957 sector = getSectorNoGenerateNoEx(p2d);
3961 dstream<<"ERROR: Could not get sector from anywhere."<<std::endl;
3969 //return generateSector();
3974 NOTE: This is not used for main map generation, only for blocks
3975 that are very high or low
3977 MapBlock * ServerMap::generateBlock(
3979 MapBlock *original_dummy,
3980 ServerMapSector *sector,
3981 core::map<v3s16, MapBlock*> &changed_blocks,
3982 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
3985 DSTACKF("%s: p=(%d,%d,%d)",
3989 // If chunks are disabled
3990 /*if(m_chunksize == 0)
3992 dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
3993 <<"not generating."<<std::endl;
3997 /*dstream<<"generateBlock(): "
3998 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4001 MapBlock *block = original_dummy;
4003 v2s16 p2d(p.X, p.Z);
4005 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4008 Do not generate over-limit
4010 if(blockpos_over_limit(p))
4012 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4013 throw InvalidPositionException("generateBlock(): pos. over limit");
4017 If block doesn't exist, create one.
4018 If it exists, it is a dummy. In that case unDummify() it.
4020 NOTE: This already sets the map as the parent of the block
4024 block = sector->createBlankBlockNoInsert(block_y);
4028 // Remove the block so that nobody can get a half-generated one.
4029 sector->removeBlock(block);
4030 // Allocate the block to contain the generated data
4036 Generate a completely empty block
4038 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4039 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4041 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4045 block->setNode(v3s16(x0,y0,z0), n);
4050 Generate a proper block
4053 u8 water_material = CONTENT_WATERSOURCE;
4055 s32 lowest_ground_y = 32767;
4056 s32 highest_ground_y = -32768;
4058 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4059 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4061 //dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
4063 //s16 surface_y = 0;
4065 s16 mud_add_amount = get_mud_add_amount(m_seed, p2d_nodes+v2s16(x0,z0));
4067 s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
4069 // If chunks are disabled
4070 if(m_chunksize == 0)
4071 surface_y = WATER_LEVEL + 1;
4073 if(surface_y < lowest_ground_y)
4074 lowest_ground_y = surface_y;
4075 if(surface_y > highest_ground_y)
4076 highest_ground_y = surface_y;
4078 s32 surface_depth = AVERAGE_MUD_AMOUNT;
4080 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4082 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
4087 NOTE: If there are some man-made structures above the
4088 newly created block, they won't be taken into account.
4090 if(real_y > surface_y)
4091 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
4097 // If node is over heightmap y, it's air or water
4098 if(real_y > surface_y)
4100 // If under water level, it's water
4101 if(real_y < WATER_LEVEL)
4103 n.d = water_material;
4104 n.setLight(LIGHTBANK_DAY,
4105 diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
4107 Add to transforming liquid queue (in case it'd
4110 v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
4111 m_transforming_liquid.push_back(real_pos);
4117 // Else it's ground or caves (air)
4120 // If it's surface_depth under ground, it's stone
4121 if(real_y <= surface_y - surface_depth)
4123 n.d = CONTENT_STONE;
4127 // It is mud if it is under the first ground
4128 // level or under water
4129 if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
4135 n.d = CONTENT_GRASS;
4138 //n.d = CONTENT_MUD;
4140 /*// If under water level, it's mud
4141 if(real_y < WATER_LEVEL)
4143 // Only the topmost node is grass
4144 else if(real_y <= surface_y - 1)
4147 n.d = CONTENT_GRASS;*/
4151 block->setNode(v3s16(x0,y0,z0), n);
4156 Calculate some helper variables
4159 // Completely underground if the highest part of block is under lowest
4161 // This has to be very sure; it's probably one too strict now but
4162 // that's just better.
4163 bool completely_underground =
4164 block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
4166 bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
4168 bool mostly_underwater_surface = false;
4169 if(highest_ground_y < WATER_LEVEL
4170 && some_part_underground && !completely_underground)
4171 mostly_underwater_surface = true;
4174 Get local attributes
4177 //dstream<<"generateBlock(): Getting local attributes"<<std::endl;
4179 float caves_amount = 0.5;
4184 NOTE: BEWARE: Too big amount of attribute points slows verything
4186 1 interpolation from 5000 points takes 2-3ms.
4188 //TimeTaker timer("generateBlock() local attribute retrieval");
4189 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4190 PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
4191 caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
4195 //dstream<<"generateBlock(): Done"<<std::endl;
4201 // Initialize temporary table
4202 const s32 ued = MAP_BLOCKSIZE;
4203 bool underground_emptiness[ued*ued*ued];
4204 for(s32 i=0; i<ued*ued*ued; i++)
4206 underground_emptiness[i] = 0;
4213 Initialize orp and ors. Try to find if some neighboring
4214 MapBlock has a tunnel ended in its side
4218 (float)(myrand()%ued)+0.5,
4219 (float)(myrand()%ued)+0.5,
4220 (float)(myrand()%ued)+0.5
4223 bool found_existing = false;
4229 for(s16 y=0; y<ued; y++)
4230 for(s16 x=0; x<ued; x++)
4232 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4233 if(getNode(ap).d == CONTENT_AIR)
4235 orp = v3f(x+1,y+1,0);
4236 found_existing = true;
4237 goto continue_generating;
4241 catch(InvalidPositionException &e){}
4247 for(s16 y=0; y<ued; y++)
4248 for(s16 x=0; x<ued; x++)
4250 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4251 if(getNode(ap).d == CONTENT_AIR)
4253 orp = v3f(x+1,y+1,ued-1);
4254 found_existing = true;
4255 goto continue_generating;
4259 catch(InvalidPositionException &e){}
4265 for(s16 y=0; y<ued; y++)
4266 for(s16 z=0; z<ued; z++)
4268 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4269 if(getNode(ap).d == CONTENT_AIR)
4271 orp = v3f(0,y+1,z+1);
4272 found_existing = true;
4273 goto continue_generating;
4277 catch(InvalidPositionException &e){}
4283 for(s16 y=0; y<ued; y++)
4284 for(s16 z=0; z<ued; z++)
4286 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4287 if(getNode(ap).d == CONTENT_AIR)
4289 orp = v3f(ued-1,y+1,z+1);
4290 found_existing = true;
4291 goto continue_generating;
4295 catch(InvalidPositionException &e){}
4301 for(s16 x=0; x<ued; x++)
4302 for(s16 z=0; z<ued; z++)
4304 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4305 if(getNode(ap).d == CONTENT_AIR)
4307 orp = v3f(x+1,0,z+1);
4308 found_existing = true;
4309 goto continue_generating;
4313 catch(InvalidPositionException &e){}
4319 for(s16 x=0; x<ued; x++)
4320 for(s16 z=0; z<ued; z++)
4322 v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
4323 if(getNode(ap).d == CONTENT_AIR)
4325 orp = v3f(x+1,ued-1,z+1);
4326 found_existing = true;
4327 goto continue_generating;
4331 catch(InvalidPositionException &e){}
4333 continue_generating:
4336 Choose whether to actually generate cave
4338 bool do_generate_caves = true;
4339 // Don't generate if no part is underground
4340 if(!some_part_underground)
4342 do_generate_caves = false;
4344 // Don't generate if mostly underwater surface
4345 /*else if(mostly_underwater_surface)
4347 do_generate_caves = false;
4349 // Partly underground = cave
4350 else if(!completely_underground)
4352 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4354 // Found existing cave underground
4355 else if(found_existing && completely_underground)
4357 do_generate_caves = (rand() % 100 <= (s32)(caves_amount*100));
4359 // Underground and no caves found
4362 do_generate_caves = (rand() % 300 <= (s32)(caves_amount*100));
4365 if(do_generate_caves)
4368 Generate some tunnel starting from orp and ors
4370 for(u16 i=0; i<3; i++)
4373 (float)(myrand()%ued)+0.5,
4374 (float)(myrand()%ued)+0.5,
4375 (float)(myrand()%ued)+0.5
4379 s16 rs = (myrand()%(max_d-min_d+1))+min_d;
4383 for(float f=0; f<1.0; f+=0.04)
4385 v3f fp = orp + vec * f;
4386 v3s16 cp(fp.X, fp.Y, fp.Z);
4388 s16 d1 = d0 + rs - 1;
4389 for(s16 z0=d0; z0<=d1; z0++)
4391 s16 si = rs - abs(z0);
4392 for(s16 x0=-si; x0<=si-1; x0++)
4394 s16 si2 = rs - abs(x0);
4395 for(s16 y0=-si2+1; y0<=si2-1; y0++)
4401 if(isInArea(p, ued) == false)
4403 underground_emptiness[ued*ued*z + ued*y + x] = 1;
4415 // Set to true if has caves.
4416 // Set when some non-air is changed to air when making caves.
4417 bool has_caves = false;
4420 Apply temporary cave data to block
4423 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4424 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4426 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4428 MapNode n = block->getNode(v3s16(x0,y0,z0));
4431 if(underground_emptiness[
4432 ued*ued*(z0*ued/MAP_BLOCKSIZE)
4433 +ued*(y0*ued/MAP_BLOCKSIZE)
4434 +(x0*ued/MAP_BLOCKSIZE)])
4436 if(content_features(n.d).walkable/*is_ground_content(n.d)*/)
4445 block->setNode(v3s16(x0,y0,z0), n);
4450 This is used for guessing whether or not the block should
4451 receive sunlight from the top if the block above doesn't exist
4453 block->setIsUnderground(completely_underground);
4456 Force lighting update if some part of block is partly
4457 underground and has caves.
4459 /*if(some_part_underground && !completely_underground && has_caves)
4461 //dstream<<"Half-ground caves"<<std::endl;
4462 lighting_invalidated_blocks[block->getPos()] = block;
4465 // DEBUG: Always update lighting
4466 //lighting_invalidated_blocks[block->getPos()] = block;
4472 if(some_part_underground)
4474 s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
4479 for(s16 i=0; i<underground_level/4 + 1; i++)
4481 if(myrand()%50 == 0)
4484 (myrand()%(MAP_BLOCKSIZE-2))+1,
4485 (myrand()%(MAP_BLOCKSIZE-2))+1,
4486 (myrand()%(MAP_BLOCKSIZE-2))+1
4492 for(u16 i=0; i<27; i++)
4494 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4496 block->setNode(cp+g_27dirs[i], n);
4504 u16 coal_amount = 30;
4505 u16 coal_rareness = 60 / coal_amount;
4506 if(coal_rareness == 0)
4508 if(myrand()%coal_rareness == 0)
4510 u16 a = myrand() % 16;
4511 u16 amount = coal_amount * a*a*a / 1000;
4512 for(s16 i=0; i<amount; i++)
4515 (myrand()%(MAP_BLOCKSIZE-2))+1,
4516 (myrand()%(MAP_BLOCKSIZE-2))+1,
4517 (myrand()%(MAP_BLOCKSIZE-2))+1
4521 n.d = CONTENT_STONE;
4522 n.param = MINERAL_COAL;
4524 for(u16 i=0; i<27; i++)
4526 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4528 block->setNode(cp+g_27dirs[i], n);
4536 //TODO: change to iron_amount or whatever
4537 u16 iron_amount = 15;
4538 u16 iron_rareness = 60 / iron_amount;
4539 if(iron_rareness == 0)
4541 if(myrand()%iron_rareness == 0)
4543 u16 a = myrand() % 16;
4544 u16 amount = iron_amount * a*a*a / 1000;
4545 for(s16 i=0; i<amount; i++)
4548 (myrand()%(MAP_BLOCKSIZE-2))+1,
4549 (myrand()%(MAP_BLOCKSIZE-2))+1,
4550 (myrand()%(MAP_BLOCKSIZE-2))+1
4554 n.d = CONTENT_STONE;
4555 n.param = MINERAL_IRON;
4557 for(u16 i=0; i<27; i++)
4559 if(block->getNode(cp+g_27dirs[i]).d == CONTENT_STONE)
4561 block->setNode(cp+g_27dirs[i], n);
4568 Create a few rats in empty blocks underground
4570 if(completely_underground)
4572 //for(u16 i=0; i<2; i++)
4575 (myrand()%(MAP_BLOCKSIZE-2))+1,
4576 (myrand()%(MAP_BLOCKSIZE-2))+1,
4577 (myrand()%(MAP_BLOCKSIZE-2))+1
4580 // Check that the place is empty
4581 //if(!is_ground_content(block->getNode(cp).d))
4584 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp, BS));
4585 block->addObject(obj);
4590 #endif // end of proper block generation
4593 Add block to sector.
4595 sector->insertBlock(block);
4597 // Lighting is invalid after generation.
4598 block->setLightingExpired(true);
4605 <<"lighting_invalidated_blocks.size()"
4609 <<" "<<lighting_invalidated_blocks.size()
4611 <<", "<<completely_underground
4612 <<", "<<some_part_underground
4619 MapBlock * ServerMap::createBlock(v3s16 p)
4621 DSTACKF("%s: p=(%d,%d,%d)",
4622 __FUNCTION_NAME, p.X, p.Y, p.Z);
4625 Do not create over-limit
4627 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4628 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4629 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4630 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4631 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4632 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4633 throw InvalidPositionException("createBlock(): pos. over limit");
4635 v2s16 p2d(p.X, p.Z);
4638 This will create or load a sector if not found in memory.
4639 If block exists on disk, it will be loaded.
4641 NOTE: On old save formats, this will be slow, as it generates
4642 lighting on blocks for them.
4644 ServerMapSector *sector;
4646 sector = (ServerMapSector*)createSector(p2d);
4647 assert(sector->getId() == MAPSECTOR_SERVER);
4649 catch(InvalidPositionException &e)
4651 dstream<<"createBlock: createSector() failed"<<std::endl;
4655 NOTE: This should not be done, or at least the exception
4656 should not be passed on as std::exception, because it
4657 won't be catched at all.
4659 /*catch(std::exception &e)
4661 dstream<<"createBlock: createSector() failed: "
4662 <<e.what()<<std::endl;
4667 Try to get a block from the sector
4670 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4674 block = sector->createBlankBlock(block_y);
4678 MapBlock * ServerMap::emergeBlock(
4680 bool only_from_disk,
4681 core::map<v3s16, MapBlock*> &changed_blocks,
4682 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4685 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
4687 p.X, p.Y, p.Z, only_from_disk);
4690 Do not generate over-limit
4692 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4693 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4694 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4695 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4696 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4697 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4698 throw InvalidPositionException("emergeBlock(): pos. over limit");
4700 v2s16 p2d(p.X, p.Z);
4703 This will create or load a sector if not found in memory.
4704 If block exists on disk, it will be loaded.
4706 ServerMapSector *sector;
4708 sector = (ServerMapSector*)emergeSector(p2d, changed_blocks);
4709 assert(sector->getId() == MAPSECTOR_SERVER);
4711 catch(InvalidPositionException &e)
4713 dstream<<"emergeBlock: emergeSector() failed: "
4714 <<e.what()<<std::endl;
4715 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4717 <<"You could try to delete it."<<std::endl;
4720 catch(VersionMismatchException &e)
4722 dstream<<"emergeBlock: emergeSector() failed: "
4723 <<e.what()<<std::endl;
4724 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4726 <<"You could try to delete it."<<std::endl;
4730 NOTE: This should not be done, or at least the exception
4731 should not be passed on as std::exception, because it
4732 won't be catched at all.
4734 /*catch(std::exception &e)
4736 dstream<<"emergeBlock: emergeSector() failed: "
4737 <<e.what()<<std::endl;
4738 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4740 <<"You could try to delete it."<<std::endl;
4745 Try to get a block from the sector
4748 bool does_not_exist = false;
4749 bool lighting_expired = false;
4750 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4754 does_not_exist = true;
4756 else if(block->isDummy() == true)
4758 does_not_exist = true;
4760 else if(block->getLightingExpired())
4762 lighting_expired = true;
4767 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4772 If block was not found on disk and not going to generate a
4773 new one, make sure there is a dummy block in place.
4775 if(only_from_disk && (does_not_exist || lighting_expired))
4777 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4781 // Create dummy block
4782 block = new MapBlock(this, p, true);
4784 // Add block to sector
4785 sector->insertBlock(block);
4791 //dstream<<"Not found on disk, generating."<<std::endl;
4793 //TimeTaker("emergeBlock() generate");
4795 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4798 If the block doesn't exist, generate the block.
4802 block = generateBlock(p, block, sector, changed_blocks,
4803 lighting_invalidated_blocks);
4806 if(lighting_expired)
4808 lighting_invalidated_blocks.insert(p, block);
4812 Initially update sunlight
4816 core::map<v3s16, bool> light_sources;
4817 bool black_air_left = false;
4818 bool bottom_invalid =
4819 block->propagateSunlight(light_sources, true,
4820 &black_air_left, true);
4822 // If sunlight didn't reach everywhere and part of block is
4823 // above ground, lighting has to be properly updated
4824 //if(black_air_left && some_part_underground)
4827 lighting_invalidated_blocks[block->getPos()] = block;
4832 lighting_invalidated_blocks[block->getPos()] = block;
4839 s16 ServerMap::findGroundLevel(v2s16 p2d)
4842 Uh, just do something random...
4844 // Find existing map from top to down
4847 v3s16 p(p2d.X, max, p2d.Y);
4848 for(; p.Y>min; p.Y--)
4850 MapNode n = getNodeNoEx(p);
4851 if(n.d != CONTENT_IGNORE)
4856 // If this node is not air, go to plan b
4857 if(getNodeNoEx(p).d != CONTENT_AIR)
4859 // Search existing walkable and return it
4860 for(; p.Y>min; p.Y--)
4862 MapNode n = getNodeNoEx(p);
4863 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4869 Plan B: Get from map generator perlin noise function
4871 // This won't work if proper generation is disabled
4872 if(m_chunksize == 0)
4873 return WATER_LEVEL+2;
4874 double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4878 void ServerMap::createDirs(std::string path)
4880 if(fs::CreateAllDirs(path) == false)
4882 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4883 <<"\""<<path<<"\""<<std::endl;
4884 throw BaseException("ServerMap failed to create directory");
4888 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
4894 snprintf(cc, 9, "%.4x%.4x",
4895 (unsigned int)pos.X&0xffff,
4896 (unsigned int)pos.Y&0xffff);
4898 return m_savedir + "/sectors/" + cc;
4900 snprintf(cc, 9, "%.3x/%.3x",
4901 (unsigned int)pos.X&0xfff,
4902 (unsigned int)pos.Y&0xfff);
4904 return m_savedir + "/sectors2/" + cc;
4910 v2s16 ServerMap::getSectorPos(std::string dirname)
4914 size_t spos = dirname.rfind('/') + 1;
4915 assert(spos != std::string::npos);
4916 if(dirname.size() - spos == 8)
4919 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
4921 else if(dirname.size() - spos == 3)
4924 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
4925 // Sign-extend the 12 bit values up to 16 bits...
4926 if(x&0x800) x|=0xF000;
4927 if(y&0x800) y|=0xF000;
4934 v2s16 pos((s16)x, (s16)y);
4938 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4940 v2s16 p2d = getSectorPos(sectordir);
4942 if(blockfile.size() != 4){
4943 throw InvalidFilenameException("Invalid block filename");
4946 int r = sscanf(blockfile.c_str(), "%4x", &y);
4948 throw InvalidFilenameException("Invalid block filename");
4949 return v3s16(p2d.X, y, p2d.Y);
4952 void ServerMap::save(bool only_changed)
4954 DSTACK(__FUNCTION_NAME);
4955 if(m_map_saving_enabled == false)
4957 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4961 if(only_changed == false)
4962 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4965 if(only_changed == false || m_map_metadata_changed)
4970 // Disable saving chunk metadata if chunks are disabled
4971 if(m_chunksize != 0)
4973 if(only_changed == false || anyChunkModified())
4977 u32 sector_meta_count = 0;
4978 u32 block_count = 0;
4981 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
4983 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4984 for(; i.atEnd() == false; i++)
4986 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4987 assert(sector->getId() == MAPSECTOR_SERVER);
4989 if(sector->differs_from_disk || only_changed == false)
4991 saveSectorMeta(sector);
4992 sector_meta_count++;
4994 core::list<MapBlock*> blocks;
4995 sector->getBlocks(blocks);
4996 core::list<MapBlock*>::Iterator j;
4997 for(j=blocks.begin(); j!=blocks.end(); j++)
4999 MapBlock *block = *j;
5000 if(block->getChangedFlag() || only_changed == false)
5005 /*dstream<<"ServerMap: Written block ("
5006 <<block->getPos().X<<","
5007 <<block->getPos().Y<<","
5008 <<block->getPos().Z<<")"
5017 Only print if something happened or saved whole map
5019 if(only_changed == false || sector_meta_count != 0
5020 || block_count != 0)
5022 dstream<<DTIME<<"ServerMap: Written: "
5023 <<sector_meta_count<<" sector metadata files, "
5024 <<block_count<<" block files"
5030 // NOTE: Doing this is insane. Deprecated and probably broken.
5031 void ServerMap::loadAll()
5033 DSTACK(__FUNCTION_NAME);
5034 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
5039 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
5041 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
5043 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5046 s32 printed_counter = -100000;
5047 s32 count = list.size();
5049 std::vector<fs::DirListNode>::iterator i;
5050 for(i=list.begin(); i!=list.end(); i++)
5052 if(counter > printed_counter + 10)
5054 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
5055 printed_counter = counter;
5059 MapSector *sector = NULL;
5061 // We want directories
5065 sector = loadSectorMeta(i->name);
5067 catch(InvalidFilenameException &e)
5069 // This catches unknown crap in directory
5072 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5073 (m_savedir+"/sectors/"+i->name);
5074 std::vector<fs::DirListNode>::iterator i2;
5075 for(i2=list2.begin(); i2!=list2.end(); i2++)
5081 loadBlock(i->name, i2->name, sector);
5083 catch(InvalidFilenameException &e)
5085 // This catches unknown crap in directory
5089 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
5094 void ServerMap::saveMasterHeightmap()
5096 DSTACK(__FUNCTION_NAME);
5098 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5100 createDir(m_savedir);
5102 /*std::string fullpath = m_savedir + "/master_heightmap";
5103 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5104 if(o.good() == false)
5105 throw FileNotGoodException("Cannot open master heightmap");*/
5107 // Format used for writing
5108 //u8 version = SER_FMT_VER_HIGHEST;
5111 void ServerMap::loadMasterHeightmap()
5113 DSTACK(__FUNCTION_NAME);
5115 dstream<<"DEPRECATED: "<<__FUNCTION_NAME<<std::endl;
5117 /*std::string fullpath = m_savedir + "/master_heightmap";
5118 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5119 if(is.good() == false)
5120 throw FileNotGoodException("Cannot open master heightmap");*/
5124 void ServerMap::saveMapMeta()
5126 DSTACK(__FUNCTION_NAME);
5128 dstream<<"INFO: ServerMap::saveMapMeta(): "
5129 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5132 createDirs(m_savedir);
5134 std::string fullpath = m_savedir + "/map_meta.txt";
5135 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5136 if(os.good() == false)
5138 dstream<<"ERROR: ServerMap::saveMapMeta(): "
5139 <<"could not open"<<fullpath<<std::endl;
5140 throw FileNotGoodException("Cannot open chunk metadata");
5144 params.setU64("seed", m_seed);
5145 params.setS32("chunksize", m_chunksize);
5147 params.writeLines(os);
5149 os<<"[end_of_params]\n";
5151 m_map_metadata_changed = false;
5154 void ServerMap::loadMapMeta()
5156 DSTACK(__FUNCTION_NAME);
5158 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
5161 std::string fullpath = m_savedir + "/map_meta.txt";
5162 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5163 if(is.good() == false)
5165 dstream<<"ERROR: ServerMap::loadMapMeta(): "
5166 <<"could not open"<<fullpath<<std::endl;
5167 throw FileNotGoodException("Cannot open map metadata");
5175 throw SerializationError
5176 ("ServerMap::loadMapMeta(): [end_of_params] not found");
5178 std::getline(is, line);
5179 std::string trimmedline = trim(line);
5180 if(trimmedline == "[end_of_params]")
5182 params.parseConfigLine(line);
5185 m_seed = params.getU64("seed");
5186 m_chunksize = params.getS32("chunksize");
5188 dstream<<"INFO: ServerMap::loadMapMeta(): "
5189 <<"seed="<<m_seed<<", chunksize="<<m_chunksize
5193 void ServerMap::saveChunkMeta()
5195 DSTACK(__FUNCTION_NAME);
5197 // This should not be called if chunks are disabled.
5198 assert(m_chunksize != 0);
5200 u32 count = m_chunks.size();
5202 dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
5203 <<count<<" chunks"<<std::endl;
5205 createDirs(m_savedir);
5207 std::string fullpath = m_savedir + "/chunk_meta";
5208 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
5209 if(os.good() == false)
5211 dstream<<"ERROR: ServerMap::saveChunkMeta(): "
5212 <<"could not open"<<fullpath<<std::endl;
5213 throw FileNotGoodException("Cannot open chunk metadata");
5219 os.write((char*)&version, 1);
5224 writeU32(buf, count);
5225 os.write((char*)buf, 4);
5227 for(core::map<v2s16, MapChunk*>::Iterator
5228 i = m_chunks.getIterator();
5229 i.atEnd()==false; i++)
5231 v2s16 p = i.getNode()->getKey();
5232 MapChunk *chunk = i.getNode()->getValue();
5235 os.write((char*)buf, 4);
5237 chunk->serialize(os, version);
5240 setChunksNonModified();
5243 void ServerMap::loadChunkMeta()
5245 DSTACK(__FUNCTION_NAME);
5247 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading chunk metadata"
5250 std::string fullpath = m_savedir + "/chunk_meta";
5251 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5252 if(is.good() == false)
5254 dstream<<"ERROR: ServerMap::loadChunkMeta(): "
5255 <<"could not open"<<fullpath<<std::endl;
5256 throw FileNotGoodException("Cannot open chunk metadata");
5262 is.read((char*)&version, 1);
5267 is.read((char*)buf, 4);
5268 u32 count = readU32(buf);
5270 dstream<<"INFO: ServerMap::loadChunkMeta(): Loading metadata of "
5271 <<count<<" chunks"<<std::endl;
5273 for(u32 i=0; i<count; i++)
5276 MapChunk *chunk = new MapChunk();
5278 is.read((char*)buf, 4);
5281 chunk->deSerialize(is, version);
5282 m_chunks.insert(p, chunk);
5286 void ServerMap::saveSectorMeta(ServerMapSector *sector)
5288 DSTACK(__FUNCTION_NAME);
5289 // Format used for writing
5290 u8 version = SER_FMT_VER_HIGHEST;
5292 v2s16 pos = sector->getPos();
5293 std::string dir = getSectorDir(pos);
5296 std::string fullpath = dir + "/meta";
5297 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5298 if(o.good() == false)
5299 throw FileNotGoodException("Cannot open sector metafile");
5301 sector->serialize(o, version);
5303 sector->differs_from_disk = false;
5306 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
5308 DSTACK(__FUNCTION_NAME);
5310 v2s16 p2d = getSectorPos(sectordir);
5312 ServerMapSector *sector = NULL;
5314 std::string fullpath = sectordir + "/meta";
5315 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5316 if(is.good() == false)
5318 // If the directory exists anyway, it probably is in some old
5319 // format. Just go ahead and create the sector.
5320 if(fs::PathExists(sectordir))
5322 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
5323 <<fullpath<<" doesn't exist but directory does."
5324 <<" Continuing with a sector with no metadata."
5326 sector = new ServerMapSector(this, p2d);
5327 m_sectors.insert(p2d, sector);
5331 throw FileNotGoodException("Cannot open sector metafile");
5336 sector = ServerMapSector::deSerialize
5337 (is, this, p2d, m_sectors);
5339 saveSectorMeta(sector);
5342 sector->differs_from_disk = false;
5347 bool ServerMap::loadSectorFull(v2s16 p2d)
5349 DSTACK(__FUNCTION_NAME);
5351 MapSector *sector = NULL;
5353 // The directory layout we're going to load from.
5354 // 1 - original sectors/xxxxzzzz/
5355 // 2 - new sectors2/xxx/zzz/
5356 // If we load from anything but the latest structure, we will
5357 // immediately save to the new one, and remove the old.
5359 std::string sectordir1 = getSectorDir(p2d, 1);
5360 std::string sectordir;
5361 if(fs::PathExists(sectordir1))
5363 sectordir = sectordir1;
5368 sectordir = getSectorDir(p2d, 2);
5371 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5374 sector = loadSectorMeta(sectordir, loadlayout != 2);
5376 catch(InvalidFilenameException &e)
5380 catch(FileNotGoodException &e)
5384 catch(std::exception &e)
5392 std::vector<fs::DirListNode> list2 = fs::GetDirListing
5394 std::vector<fs::DirListNode>::iterator i2;
5395 for(i2=list2.begin(); i2!=list2.end(); i2++)
5401 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
5403 catch(InvalidFilenameException &e)
5405 // This catches unknown crap in directory
5411 dstream<<"Sector converted to new layout - deleting "<<
5412 sectordir1<<std::endl;
5413 fs::RecursiveDelete(sectordir1);
5420 void ServerMap::saveBlock(MapBlock *block)
5422 DSTACK(__FUNCTION_NAME);
5424 Dummy blocks are not written
5426 if(block->isDummy())
5428 /*v3s16 p = block->getPos();
5429 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
5430 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
5434 // Format used for writing
5435 u8 version = SER_FMT_VER_HIGHEST;
5437 v3s16 p3d = block->getPos();
5438 v2s16 p2d(p3d.X, p3d.Z);
5439 std::string dir = getSectorDir(p2d);
5443 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
5444 std::string fullpath = dir + "/" + cc;
5445 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
5446 if(o.good() == false)
5447 throw FileNotGoodException("Cannot open block data");
5450 [0] u8 serialization version
5453 o.write((char*)&version, 1);
5455 block->serialize(o, version);
5458 Versions up from 9 have block objects.
5462 block->serializeObjects(o, version);
5466 Versions up from 15 have static objects.
5470 block->m_static_objects.serialize(o);
5473 // We just wrote it to the disk
5474 block->resetChangedFlag();
5477 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
5479 DSTACK(__FUNCTION_NAME);
5481 std::string fullpath = sectordir+"/"+blockfile;
5484 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
5485 if(is.good() == false)
5486 throw FileNotGoodException("Cannot open block file");
5488 v3s16 p3d = getBlockPos(sectordir, blockfile);
5489 v2s16 p2d(p3d.X, p3d.Z);
5491 assert(sector->getPos() == p2d);
5493 u8 version = SER_FMT_VER_INVALID;
5494 is.read((char*)&version, 1);
5497 throw SerializationError("ServerMap::loadBlock(): Failed"
5498 " to read MapBlock version");
5500 /*u32 block_size = MapBlock::serializedLength(version);
5501 SharedBuffer<u8> data(block_size);
5502 is.read((char*)*data, block_size);*/
5504 // This will always return a sector because we're the server
5505 //MapSector *sector = emergeSector(p2d);
5507 MapBlock *block = NULL;
5508 bool created_new = false;
5510 block = sector->getBlockNoCreate(p3d.Y);
5512 catch(InvalidPositionException &e)
5514 block = sector->createBlankBlockNoInsert(p3d.Y);
5518 // deserialize block data
5519 block->deSerialize(is, version);
5522 Versions up from 9 have block objects.
5526 block->updateObjects(is, version, NULL, 0);
5530 Versions up from 15 have static objects.
5534 block->m_static_objects.deSerialize(is);
5538 sector->insertBlock(block);
5541 Convert old formats to new and save
5544 // Save old format blocks in new format
5545 if(version < SER_FMT_VER_HIGHEST || save_after_load)
5550 // We just loaded it from the disk, so it's up-to-date.
5551 block->resetChangedFlag();
5554 catch(SerializationError &e)
5556 dstream<<"WARNING: Invalid block data on disk "
5557 "(SerializationError). Ignoring. "
5558 "A new one will be generated."
5561 // TODO: Backup file; name is in fullpath.
5565 void ServerMap::PrintInfo(std::ostream &out)
5576 ClientMap::ClientMap(
5578 MapDrawControl &control,
5579 scene::ISceneNode* parent,
5580 scene::ISceneManager* mgr,
5584 scene::ISceneNode(parent, mgr, id),
5587 m_camera_position(0,0,0),
5588 m_camera_direction(0,0,1)
5590 m_camera_mutex.Init();
5591 assert(m_camera_mutex.IsInitialized());
5593 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5594 BS*1000000,BS*1000000,BS*1000000);
5597 ClientMap::~ClientMap()
5599 /*JMutexAutoLock lock(mesh_mutex);
5608 MapSector * ClientMap::emergeSector(v2s16 p2d)
5610 DSTACK(__FUNCTION_NAME);
5611 // Check that it doesn't exist already
5613 return getSectorNoGenerate(p2d);
5615 catch(InvalidPositionException &e)
5620 ClientMapSector *sector = new ClientMapSector(this, p2d);
5623 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5624 m_sectors.insert(p2d, sector);
5630 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5632 DSTACK(__FUNCTION_NAME);
5633 ClientMapSector *sector = NULL;
5635 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5637 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5641 sector = (ClientMapSector*)n->getValue();
5642 assert(sector->getId() == MAPSECTOR_CLIENT);
5646 sector = new ClientMapSector(this, p2d);
5648 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5649 m_sectors.insert(p2d, sector);
5653 sector->deSerialize(is);
5656 void ClientMap::OnRegisterSceneNode()
5660 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5661 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5664 ISceneNode::OnRegisterSceneNode();
5667 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5669 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5670 DSTACK(__FUNCTION_NAME);
5672 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5675 Get time for measuring timeout.
5677 Measuring time is very useful for long delays when the
5678 machine is swapping a lot.
5680 int time1 = time(0);
5682 //u32 daynight_ratio = m_client->getDayNightRatio();
5684 m_camera_mutex.Lock();
5685 v3f camera_position = m_camera_position;
5686 v3f camera_direction = m_camera_direction;
5687 m_camera_mutex.Unlock();
5690 Get all blocks and draw all visible ones
5693 v3s16 cam_pos_nodes(
5694 camera_position.X / BS,
5695 camera_position.Y / BS,
5696 camera_position.Z / BS);
5698 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5700 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5701 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5703 // Take a fair amount as we will be dropping more out later
5705 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5706 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5707 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5709 p_nodes_max.X / MAP_BLOCKSIZE + 1,
5710 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
5711 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
5713 u32 vertex_count = 0;
5715 // For limiting number of mesh updates per frame
5716 u32 mesh_update_count = 0;
5718 u32 blocks_would_have_drawn = 0;
5719 u32 blocks_drawn = 0;
5721 //NOTE: The sectors map should be locked but we're not doing it
5722 // because it'd cause too much delays
5724 int timecheck_counter = 0;
5725 core::map<v2s16, MapSector*>::Iterator si;
5726 si = m_sectors.getIterator();
5727 for(; si.atEnd() == false; si++)
5730 timecheck_counter++;
5731 if(timecheck_counter > 50)
5733 timecheck_counter = 0;
5734 int time2 = time(0);
5735 if(time2 > time1 + 4)
5737 dstream<<"ClientMap::renderMap(): "
5738 "Rendering takes ages, returning."
5745 MapSector *sector = si.getNode()->getValue();
5746 v2s16 sp = sector->getPos();
5748 if(m_control.range_all == false)
5750 if(sp.X < p_blocks_min.X
5751 || sp.X > p_blocks_max.X
5752 || sp.Y < p_blocks_min.Z
5753 || sp.Y > p_blocks_max.Z)
5757 core::list< MapBlock * > sectorblocks;
5758 sector->getBlocks(sectorblocks);
5764 core::list< MapBlock * >::Iterator i;
5765 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5767 MapBlock *block = *i;
5770 Compare block position to camera position, skip
5771 if not seen on display
5774 float range = 100000 * BS;
5775 if(m_control.range_all == false)
5776 range = m_control.wanted_range * BS;
5779 if(isBlockInSight(block->getPos(), camera_position,
5780 camera_direction, range, &d) == false)
5785 // This is ugly (spherical distance limit?)
5786 /*if(m_control.range_all == false &&
5787 d - 0.5*BS*MAP_BLOCKSIZE > range)
5792 Update expired mesh (used for day/night change)
5794 It doesn't work exactly like it should now with the
5795 tasked mesh update but whatever.
5798 bool mesh_expired = false;
5801 JMutexAutoLock lock(block->mesh_mutex);
5803 mesh_expired = block->getMeshExpired();
5805 // Mesh has not been expired and there is no mesh:
5806 // block has no content
5807 if(block->mesh == NULL && mesh_expired == false)
5811 f32 faraway = BS*50;
5812 //f32 faraway = m_control.wanted_range * BS;
5815 This has to be done with the mesh_mutex unlocked
5817 // Pretty random but this should work somewhat nicely
5818 if(mesh_expired && (
5819 (mesh_update_count < 3
5820 && (d < faraway || mesh_update_count < 2)
5823 (m_control.range_all && mesh_update_count < 20)
5826 /*if(mesh_expired && mesh_update_count < 6
5827 && (d < faraway || mesh_update_count < 3))*/
5829 mesh_update_count++;
5831 // Mesh has been expired: generate new mesh
5832 //block->updateMesh(daynight_ratio);
5833 m_client->addUpdateMeshTask(block->getPos());
5835 mesh_expired = false;
5840 Draw the faces of the block
5843 JMutexAutoLock lock(block->mesh_mutex);
5845 scene::SMesh *mesh = block->mesh;
5850 blocks_would_have_drawn++;
5851 if(blocks_drawn >= m_control.wanted_max_blocks
5852 && m_control.range_all == false
5853 && d > m_control.wanted_min_range * BS)
5857 u32 c = mesh->getMeshBufferCount();
5859 for(u32 i=0; i<c; i++)
5861 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5862 const video::SMaterial& material = buf->getMaterial();
5863 video::IMaterialRenderer* rnd =
5864 driver->getMaterialRenderer(material.MaterialType);
5865 bool transparent = (rnd && rnd->isTransparent());
5866 // Render transparent on transparent pass and likewise.
5867 if(transparent == is_transparent_pass)
5870 This *shouldn't* hurt too much because Irrlicht
5871 doesn't change opengl textures if the old
5872 material is set again.
5874 driver->setMaterial(buf->getMaterial());
5875 driver->drawMeshBuffer(buf);
5876 vertex_count += buf->getVertexCount();
5880 } // foreach sectorblocks
5883 m_control.blocks_drawn = blocks_drawn;
5884 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5886 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5887 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5890 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5891 core::map<v3s16, MapBlock*> *affected_blocks)
5893 bool changed = false;
5895 Add it to all blocks touching it
5898 v3s16(0,0,0), // this
5899 v3s16(0,0,1), // back
5900 v3s16(0,1,0), // top
5901 v3s16(1,0,0), // right
5902 v3s16(0,0,-1), // front
5903 v3s16(0,-1,0), // bottom
5904 v3s16(-1,0,0), // left
5906 for(u16 i=0; i<7; i++)
5908 v3s16 p2 = p + dirs[i];
5909 // Block position of neighbor (or requested) node
5910 v3s16 blockpos = getNodeBlockPos(p2);
5911 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5912 if(blockref == NULL)
5914 // Relative position of requested node
5915 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5916 if(blockref->setTempMod(relpos, mod))
5921 if(changed && affected_blocks!=NULL)
5923 for(u16 i=0; i<7; i++)
5925 v3s16 p2 = p + dirs[i];
5926 // Block position of neighbor (or requested) node
5927 v3s16 blockpos = getNodeBlockPos(p2);
5928 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5929 if(blockref == NULL)
5931 affected_blocks->insert(blockpos, blockref);
5937 bool ClientMap::clearTempMod(v3s16 p,
5938 core::map<v3s16, MapBlock*> *affected_blocks)
5940 bool changed = false;
5942 v3s16(0,0,0), // this
5943 v3s16(0,0,1), // back
5944 v3s16(0,1,0), // top
5945 v3s16(1,0,0), // right
5946 v3s16(0,0,-1), // front
5947 v3s16(0,-1,0), // bottom
5948 v3s16(-1,0,0), // left
5950 for(u16 i=0; i<7; i++)
5952 v3s16 p2 = p + dirs[i];
5953 // Block position of neighbor (or requested) node
5954 v3s16 blockpos = getNodeBlockPos(p2);
5955 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5956 if(blockref == NULL)
5958 // Relative position of requested node
5959 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5960 if(blockref->clearTempMod(relpos))
5965 if(changed && affected_blocks!=NULL)
5967 for(u16 i=0; i<7; i++)
5969 v3s16 p2 = p + dirs[i];
5970 // Block position of neighbor (or requested) node
5971 v3s16 blockpos = getNodeBlockPos(p2);
5972 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5973 if(blockref == NULL)
5975 affected_blocks->insert(blockpos, blockref);
5981 void ClientMap::expireMeshes(bool only_daynight_diffed)
5983 TimeTaker timer("expireMeshes()");
5985 core::map<v2s16, MapSector*>::Iterator si;
5986 si = m_sectors.getIterator();
5987 for(; si.atEnd() == false; si++)
5989 MapSector *sector = si.getNode()->getValue();
5991 core::list< MapBlock * > sectorblocks;
5992 sector->getBlocks(sectorblocks);
5994 core::list< MapBlock * >::Iterator i;
5995 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5997 MapBlock *block = *i;
5999 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
6005 JMutexAutoLock lock(block->mesh_mutex);
6006 if(block->mesh != NULL)
6008 /*block->mesh->drop();
6009 block->mesh = NULL;*/
6010 block->setMeshExpired(true);
6017 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
6019 assert(mapType() == MAPTYPE_CLIENT);
6022 v3s16 p = blockpos + v3s16(0,0,0);
6023 MapBlock *b = getBlockNoCreate(p);
6024 b->updateMesh(daynight_ratio);
6025 //b->setMeshExpired(true);
6027 catch(InvalidPositionException &e){}
6030 v3s16 p = blockpos + v3s16(-1,0,0);
6031 MapBlock *b = getBlockNoCreate(p);
6032 b->updateMesh(daynight_ratio);
6033 //b->setMeshExpired(true);
6035 catch(InvalidPositionException &e){}
6037 v3s16 p = blockpos + v3s16(0,-1,0);
6038 MapBlock *b = getBlockNoCreate(p);
6039 b->updateMesh(daynight_ratio);
6040 //b->setMeshExpired(true);
6042 catch(InvalidPositionException &e){}
6044 v3s16 p = blockpos + v3s16(0,0,-1);
6045 MapBlock *b = getBlockNoCreate(p);
6046 b->updateMesh(daynight_ratio);
6047 //b->setMeshExpired(true);
6049 catch(InvalidPositionException &e){}
6054 Update mesh of block in which the node is, and if the node is at the
6055 leading edge, update the appropriate leading blocks too.
6057 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
6065 v3s16 blockposes[4];
6066 for(u32 i=0; i<4; i++)
6068 v3s16 np = nodepos + dirs[i];
6069 blockposes[i] = getNodeBlockPos(np);
6070 // Don't update mesh of block if it has been done already
6071 bool already_updated = false;
6072 for(u32 j=0; j<i; j++)
6074 if(blockposes[j] == blockposes[i])
6076 already_updated = true;
6083 MapBlock *b = getBlockNoCreate(blockposes[i]);
6084 b->updateMesh(daynight_ratio);
6089 void ClientMap::PrintInfo(std::ostream &out)
6100 MapVoxelManipulator::MapVoxelManipulator(Map *map)
6105 MapVoxelManipulator::~MapVoxelManipulator()
6107 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
6111 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6113 TimeTaker timer1("emerge", &emerge_time);
6115 // Units of these are MapBlocks
6116 v3s16 p_min = getNodeBlockPos(a.MinEdge);
6117 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
6119 VoxelArea block_area_nodes
6120 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6122 addArea(block_area_nodes);
6124 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6125 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6126 for(s32 x=p_min.X; x<=p_max.X; x++)
6129 core::map<v3s16, bool>::Node *n;
6130 n = m_loaded_blocks.find(p);
6134 bool block_data_inexistent = false;
6137 TimeTaker timer1("emerge load", &emerge_load_time);
6139 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
6140 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6143 dstream<<std::endl;*/
6145 MapBlock *block = m_map->getBlockNoCreate(p);
6146 if(block->isDummy())
6147 block_data_inexistent = true;
6149 block->copyTo(*this);
6151 catch(InvalidPositionException &e)
6153 block_data_inexistent = true;
6156 if(block_data_inexistent)
6158 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6159 // Fill with VOXELFLAG_INEXISTENT
6160 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6161 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6163 s32 i = m_area.index(a.MinEdge.X,y,z);
6164 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6168 m_loaded_blocks.insert(p, !block_data_inexistent);
6171 //dstream<<"emerge done"<<std::endl;
6175 SUGG: Add an option to only update eg. water and air nodes.
6176 This will make it interfere less with important stuff if
6179 void MapVoxelManipulator::blitBack
6180 (core::map<v3s16, MapBlock*> & modified_blocks)
6182 if(m_area.getExtent() == v3s16(0,0,0))
6185 //TimeTaker timer1("blitBack");
6187 /*dstream<<"blitBack(): m_loaded_blocks.size()="
6188 <<m_loaded_blocks.size()<<std::endl;*/
6191 Initialize block cache
6193 v3s16 blockpos_last;
6194 MapBlock *block = NULL;
6195 bool block_checked_in_modified = false;
6197 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
6198 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
6199 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
6203 u8 f = m_flags[m_area.index(p)];
6204 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
6207 MapNode &n = m_data[m_area.index(p)];
6209 v3s16 blockpos = getNodeBlockPos(p);
6214 if(block == NULL || blockpos != blockpos_last){
6215 block = m_map->getBlockNoCreate(blockpos);
6216 blockpos_last = blockpos;
6217 block_checked_in_modified = false;
6220 // Calculate relative position in block
6221 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
6223 // Don't continue if nothing has changed here
6224 if(block->getNode(relpos) == n)
6227 //m_map->setNode(m_area.MinEdge + p, n);
6228 block->setNode(relpos, n);
6231 Make sure block is in modified_blocks
6233 if(block_checked_in_modified == false)
6235 modified_blocks[blockpos] = block;
6236 block_checked_in_modified = true;
6239 catch(InvalidPositionException &e)
6245 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
6246 MapVoxelManipulator(map),
6247 m_create_area(false)
6251 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
6255 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
6257 // Just create the area so that it can be pointed to
6258 VoxelManipulator::emerge(a, caller_id);
6261 void ManualMapVoxelManipulator::initialEmerge(
6262 v3s16 blockpos_min, v3s16 blockpos_max)
6264 TimeTaker timer1("initialEmerge", &emerge_time);
6266 // Units of these are MapBlocks
6267 v3s16 p_min = blockpos_min;
6268 v3s16 p_max = blockpos_max;
6270 VoxelArea block_area_nodes
6271 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6273 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
6276 dstream<<"initialEmerge: area: ";
6277 block_area_nodes.print(dstream);
6278 dstream<<" ("<<size_MB<<"MB)";
6282 addArea(block_area_nodes);
6284 for(s32 z=p_min.Z; z<=p_max.Z; z++)
6285 for(s32 y=p_min.Y; y<=p_max.Y; y++)
6286 for(s32 x=p_min.X; x<=p_max.X; x++)
6289 core::map<v3s16, bool>::Node *n;
6290 n = m_loaded_blocks.find(p);
6294 bool block_data_inexistent = false;
6297 TimeTaker timer1("emerge load", &emerge_load_time);
6299 MapBlock *block = m_map->getBlockNoCreate(p);
6300 if(block->isDummy())
6301 block_data_inexistent = true;
6303 block->copyTo(*this);
6305 catch(InvalidPositionException &e)
6307 block_data_inexistent = true;
6310 if(block_data_inexistent)
6313 Mark area inexistent
6315 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
6316 // Fill with VOXELFLAG_INEXISTENT
6317 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
6318 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
6320 s32 i = m_area.index(a.MinEdge.X,y,z);
6321 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
6325 m_loaded_blocks.insert(p, !block_data_inexistent);
6329 void ManualMapVoxelManipulator::blitBackAll(
6330 core::map<v3s16, MapBlock*> * modified_blocks)
6332 if(m_area.getExtent() == v3s16(0,0,0))
6336 Copy data of all blocks
6338 for(core::map<v3s16, bool>::Iterator
6339 i = m_loaded_blocks.getIterator();
6340 i.atEnd() == false; i++)
6342 bool existed = i.getNode()->getValue();
6343 if(existed == false)
6345 v3s16 p = i.getNode()->getKey();
6346 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
6349 dstream<<"WARNING: "<<__FUNCTION_NAME
6350 <<": got NULL block "
6351 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
6356 block->copyFrom(*this);
6359 modified_blocks->insert(p, block);