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"
31 #include "content_mapnode.h"
37 SQLite format specification:
38 - Initially only replaces sectors/ and sectors2/
45 Map::Map(std::ostream &dout):
49 /*m_sector_mutex.Init();
50 assert(m_sector_mutex.IsInitialized());*/
58 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
59 for(; i.atEnd() == false; i++)
61 MapSector *sector = i.getNode()->getValue();
66 void Map::addEventReceiver(MapEventReceiver *event_receiver)
68 m_event_receivers.insert(event_receiver, false);
71 void Map::removeEventReceiver(MapEventReceiver *event_receiver)
73 if(m_event_receivers.find(event_receiver) == NULL)
75 m_event_receivers.remove(event_receiver);
78 void Map::dispatchEvent(MapEditEvent *event)
80 for(core::map<MapEventReceiver*, bool>::Iterator
81 i = m_event_receivers.getIterator();
82 i.atEnd()==false; i++)
84 MapEventReceiver* event_receiver = i.getNode()->getKey();
85 event_receiver->onMapEditEvent(event);
89 MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
91 if(m_sector_cache != NULL && p == m_sector_cache_p){
92 MapSector * sector = m_sector_cache;
96 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
101 MapSector *sector = n->getValue();
103 // Cache the last result
104 m_sector_cache_p = p;
105 m_sector_cache = sector;
110 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
112 return getSectorNoGenerateNoExNoLock(p);
115 MapSector * Map::getSectorNoGenerate(v2s16 p)
117 MapSector *sector = getSectorNoGenerateNoEx(p);
119 throw InvalidPositionException();
124 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
126 v2s16 p2d(p3d.X, p3d.Z);
127 MapSector * sector = getSectorNoGenerate(p2d);
129 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
134 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
138 v2s16 p2d(p3d.X, p3d.Z);
139 MapSector * sector = getSectorNoGenerate(p2d);
140 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
143 catch(InvalidPositionException &e)
149 /*MapBlock * Map::getBlockCreate(v3s16 p3d)
151 v2s16 p2d(p3d.X, p3d.Z);
152 MapSector * sector = getSectorCreate(p2d);
154 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
157 block = sector->createBlankBlock(p3d.Y);
161 bool Map::isNodeUnderground(v3s16 p)
163 v3s16 blockpos = getNodeBlockPos(p);
165 MapBlock * block = getBlockNoCreate(blockpos);
166 return block->getIsUnderground();
168 catch(InvalidPositionException &e)
175 Goes recursively through the neighbours of the node.
177 Alters only transparent nodes.
179 If the lighting of the neighbour is lower than the lighting of
180 the node was (before changing it to 0 at the step before), the
181 lighting of the neighbour is set to 0 and then the same stuff
182 repeats for the neighbour.
184 The ending nodes of the routine are stored in light_sources.
185 This is useful when a light is removed. In such case, this
186 routine can be called for the light node and then again for
187 light_sources to re-light the area without the removed light.
189 values of from_nodes are lighting values.
191 void Map::unspreadLight(enum LightBank bank,
192 core::map<v3s16, u8> & from_nodes,
193 core::map<v3s16, bool> & light_sources,
194 core::map<v3s16, MapBlock*> & modified_blocks)
197 v3s16(0,0,1), // back
199 v3s16(1,0,0), // right
200 v3s16(0,0,-1), // front
201 v3s16(0,-1,0), // bottom
202 v3s16(-1,0,0), // left
205 if(from_nodes.size() == 0)
208 u32 blockchangecount = 0;
210 core::map<v3s16, u8> unlighted_nodes;
211 core::map<v3s16, u8>::Iterator j;
212 j = from_nodes.getIterator();
215 Initialize block cache
218 MapBlock *block = NULL;
219 // Cache this a bit, too
220 bool block_checked_in_modified = false;
222 for(; j.atEnd() == false; j++)
224 v3s16 pos = j.getNode()->getKey();
225 v3s16 blockpos = getNodeBlockPos(pos);
227 // Only fetch a new block if the block position has changed
229 if(block == NULL || blockpos != blockpos_last){
230 block = getBlockNoCreate(blockpos);
231 blockpos_last = blockpos;
233 block_checked_in_modified = false;
237 catch(InvalidPositionException &e)
245 // Calculate relative position in block
246 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
248 // Get node straight from the block
249 MapNode n = block->getNode(relpos);
251 u8 oldlight = j.getNode()->getValue();
253 // Loop through 6 neighbors
254 for(u16 i=0; i<6; i++)
256 // Get the position of the neighbor node
257 v3s16 n2pos = pos + dirs[i];
259 // Get the block where the node is located
260 v3s16 blockpos = getNodeBlockPos(n2pos);
264 // Only fetch a new block if the block position has changed
266 if(block == NULL || blockpos != blockpos_last){
267 block = getBlockNoCreate(blockpos);
268 blockpos_last = blockpos;
270 block_checked_in_modified = false;
274 catch(InvalidPositionException &e)
279 // Calculate relative position in block
280 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
281 // Get node straight from the block
282 MapNode n2 = block->getNode(relpos);
284 bool changed = false;
286 //TODO: Optimize output by optimizing light_sources?
289 If the neighbor is dimmer than what was specified
290 as oldlight (the light of the previous node)
292 if(n2.getLight(bank) < oldlight)
295 And the neighbor is transparent and it has some light
297 if(n2.light_propagates() && n2.getLight(bank) != 0)
300 Set light to 0 and add to queue
303 u8 current_light = n2.getLight(bank);
304 n2.setLight(bank, 0);
305 block->setNode(relpos, n2);
307 unlighted_nodes.insert(n2pos, current_light);
311 Remove from light_sources if it is there
312 NOTE: This doesn't happen nearly at all
314 /*if(light_sources.find(n2pos))
316 std::cout<<"Removed from light_sources"<<std::endl;
317 light_sources.remove(n2pos);
322 if(light_sources.find(n2pos) != NULL)
323 light_sources.remove(n2pos);*/
326 light_sources.insert(n2pos, true);
329 // Add to modified_blocks
330 if(changed == true && block_checked_in_modified == false)
332 // If the block is not found in modified_blocks, add.
333 if(modified_blocks.find(blockpos) == NULL)
335 modified_blocks.insert(blockpos, block);
337 block_checked_in_modified = true;
340 catch(InvalidPositionException &e)
347 /*dstream<<"unspreadLight(): Changed block "
348 <<blockchangecount<<" times"
349 <<" for "<<from_nodes.size()<<" nodes"
352 if(unlighted_nodes.size() > 0)
353 unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
357 A single-node wrapper of the above
359 void Map::unLightNeighbors(enum LightBank bank,
360 v3s16 pos, u8 lightwas,
361 core::map<v3s16, bool> & light_sources,
362 core::map<v3s16, MapBlock*> & modified_blocks)
364 core::map<v3s16, u8> from_nodes;
365 from_nodes.insert(pos, lightwas);
367 unspreadLight(bank, from_nodes, light_sources, modified_blocks);
371 Lights neighbors of from_nodes, collects all them and then
374 void Map::spreadLight(enum LightBank bank,
375 core::map<v3s16, bool> & from_nodes,
376 core::map<v3s16, MapBlock*> & modified_blocks)
378 const v3s16 dirs[6] = {
379 v3s16(0,0,1), // back
381 v3s16(1,0,0), // right
382 v3s16(0,0,-1), // front
383 v3s16(0,-1,0), // bottom
384 v3s16(-1,0,0), // left
387 if(from_nodes.size() == 0)
390 u32 blockchangecount = 0;
392 core::map<v3s16, bool> lighted_nodes;
393 core::map<v3s16, bool>::Iterator j;
394 j = from_nodes.getIterator();
397 Initialize block cache
400 MapBlock *block = NULL;
401 // Cache this a bit, too
402 bool block_checked_in_modified = false;
404 for(; j.atEnd() == false; j++)
405 //for(; j != from_nodes.end(); j++)
407 v3s16 pos = j.getNode()->getKey();
409 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
410 v3s16 blockpos = getNodeBlockPos(pos);
412 // Only fetch a new block if the block position has changed
414 if(block == NULL || blockpos != blockpos_last){
415 block = getBlockNoCreate(blockpos);
416 blockpos_last = blockpos;
418 block_checked_in_modified = false;
422 catch(InvalidPositionException &e)
430 // Calculate relative position in block
431 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
433 // Get node straight from the block
434 MapNode n = block->getNode(relpos);
436 u8 oldlight = n.getLight(bank);
437 u8 newlight = diminish_light(oldlight);
439 // Loop through 6 neighbors
440 for(u16 i=0; i<6; i++){
441 // Get the position of the neighbor node
442 v3s16 n2pos = pos + dirs[i];
444 // Get the block where the node is located
445 v3s16 blockpos = getNodeBlockPos(n2pos);
449 // Only fetch a new block if the block position has changed
451 if(block == NULL || blockpos != blockpos_last){
452 block = getBlockNoCreate(blockpos);
453 blockpos_last = blockpos;
455 block_checked_in_modified = false;
459 catch(InvalidPositionException &e)
464 // Calculate relative position in block
465 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
466 // Get node straight from the block
467 MapNode n2 = block->getNode(relpos);
469 bool changed = false;
471 If the neighbor is brighter than the current node,
472 add to list (it will light up this node on its turn)
474 if(n2.getLight(bank) > undiminish_light(oldlight))
476 lighted_nodes.insert(n2pos, true);
477 //lighted_nodes.push_back(n2pos);
481 If the neighbor is dimmer than how much light this node
482 would spread on it, add to list
484 if(n2.getLight(bank) < newlight)
486 if(n2.light_propagates())
488 n2.setLight(bank, newlight);
489 block->setNode(relpos, n2);
490 lighted_nodes.insert(n2pos, true);
491 //lighted_nodes.push_back(n2pos);
496 // Add to modified_blocks
497 if(changed == true && block_checked_in_modified == false)
499 // If the block is not found in modified_blocks, add.
500 if(modified_blocks.find(blockpos) == NULL)
502 modified_blocks.insert(blockpos, block);
504 block_checked_in_modified = true;
507 catch(InvalidPositionException &e)
514 /*dstream<<"spreadLight(): Changed block "
515 <<blockchangecount<<" times"
516 <<" for "<<from_nodes.size()<<" nodes"
519 if(lighted_nodes.size() > 0)
520 spreadLight(bank, lighted_nodes, modified_blocks);
524 A single-node source variation of the above.
526 void Map::lightNeighbors(enum LightBank bank,
528 core::map<v3s16, MapBlock*> & modified_blocks)
530 core::map<v3s16, bool> from_nodes;
531 from_nodes.insert(pos, true);
532 spreadLight(bank, from_nodes, modified_blocks);
535 v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
538 v3s16(0,0,1), // back
540 v3s16(1,0,0), // right
541 v3s16(0,0,-1), // front
542 v3s16(0,-1,0), // bottom
543 v3s16(-1,0,0), // left
546 u8 brightest_light = 0;
547 v3s16 brightest_pos(0,0,0);
548 bool found_something = false;
550 // Loop through 6 neighbors
551 for(u16 i=0; i<6; i++){
552 // Get the position of the neighbor node
553 v3s16 n2pos = p + dirs[i];
558 catch(InvalidPositionException &e)
562 if(n2.getLight(bank) > brightest_light || found_something == false){
563 brightest_light = n2.getLight(bank);
564 brightest_pos = n2pos;
565 found_something = true;
569 if(found_something == false)
570 throw InvalidPositionException();
572 return brightest_pos;
576 Propagates sunlight down from a node.
577 Starting point gets sunlight.
579 Returns the lowest y value of where the sunlight went.
581 Mud is turned into grass in where the sunlight stops.
583 s16 Map::propagateSunlight(v3s16 start,
584 core::map<v3s16, MapBlock*> & modified_blocks)
589 v3s16 pos(start.X, y, start.Z);
591 v3s16 blockpos = getNodeBlockPos(pos);
594 block = getBlockNoCreate(blockpos);
596 catch(InvalidPositionException &e)
601 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
602 MapNode n = block->getNode(relpos);
604 if(n.sunlight_propagates())
606 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
607 block->setNode(relpos, n);
609 modified_blocks.insert(blockpos, block);
613 /*// Turn mud into grass
614 if(n.d == CONTENT_MUD)
617 block->setNode(relpos, n);
618 modified_blocks.insert(blockpos, block);
621 // Sunlight goes no further
628 void Map::updateLighting(enum LightBank bank,
629 core::map<v3s16, MapBlock*> & a_blocks,
630 core::map<v3s16, MapBlock*> & modified_blocks)
632 /*m_dout<<DTIME<<"Map::updateLighting(): "
633 <<a_blocks.size()<<" blocks."<<std::endl;*/
635 //TimeTaker timer("updateLighting");
639 //u32 count_was = modified_blocks.size();
641 core::map<v3s16, MapBlock*> blocks_to_update;
643 core::map<v3s16, bool> light_sources;
645 core::map<v3s16, u8> unlight_from;
647 core::map<v3s16, MapBlock*>::Iterator i;
648 i = a_blocks.getIterator();
649 for(; i.atEnd() == false; i++)
651 MapBlock *block = i.getNode()->getValue();
655 // Don't bother with dummy blocks.
659 v3s16 pos = block->getPos();
660 modified_blocks.insert(pos, block);
662 blocks_to_update.insert(pos, block);
665 Clear all light from block
667 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
668 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
669 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
674 MapNode n = block->getNode(v3s16(x,y,z));
675 u8 oldlight = n.getLight(bank);
677 block->setNode(v3s16(x,y,z), n);
679 // Collect borders for unlighting
680 if(x==0 || x == MAP_BLOCKSIZE-1
681 || y==0 || y == MAP_BLOCKSIZE-1
682 || z==0 || z == MAP_BLOCKSIZE-1)
684 v3s16 p_map = p + v3s16(
687 MAP_BLOCKSIZE*pos.Z);
688 unlight_from.insert(p_map, oldlight);
691 catch(InvalidPositionException &e)
694 This would happen when dealing with a
698 dstream<<"updateLighting(): InvalidPositionException"
703 if(bank == LIGHTBANK_DAY)
705 bool bottom_valid = block->propagateSunlight(light_sources);
707 // If bottom is valid, we're done.
711 else if(bank == LIGHTBANK_NIGHT)
713 // For night lighting, sunlight is not propagated
718 // Invalid lighting bank
722 /*dstream<<"Bottom for sunlight-propagated block ("
723 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
726 // Bottom sunlight is not valid; get the block and loop to it
730 block = getBlockNoCreate(pos);
732 catch(InvalidPositionException &e)
742 TimeTaker timer("unspreadLight");
743 unspreadLight(bank, unlight_from, light_sources, modified_blocks);
748 u32 diff = modified_blocks.size() - count_was;
749 count_was = modified_blocks.size();
750 dstream<<"unspreadLight modified "<<diff<<std::endl;
754 TimeTaker timer("spreadLight");
755 spreadLight(bank, light_sources, modified_blocks);
760 u32 diff = modified_blocks.size() - count_was;
761 count_was = modified_blocks.size();
762 dstream<<"spreadLight modified "<<diff<<std::endl;
767 //MapVoxelManipulator vmanip(this);
769 // Make a manual voxel manipulator and load all the blocks
770 // that touch the requested blocks
771 ManualMapVoxelManipulator vmanip(this);
772 core::map<v3s16, MapBlock*>::Iterator i;
773 i = blocks_to_update.getIterator();
774 for(; i.atEnd() == false; i++)
776 MapBlock *block = i.getNode()->getValue();
777 v3s16 p = block->getPos();
779 // Add all surrounding blocks
780 vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
783 Add all surrounding blocks that have up-to-date lighting
784 NOTE: This doesn't quite do the job (not everything
785 appropriate is lighted)
787 /*for(s16 z=-1; z<=1; z++)
788 for(s16 y=-1; y<=1; y++)
789 for(s16 x=-1; x<=1; x++)
792 MapBlock *block = getBlockNoCreateNoEx(p);
797 if(block->getLightingExpired())
799 vmanip.initialEmerge(p, p);
802 // Lighting of block will be updated completely
803 block->setLightingExpired(false);
807 //TimeTaker timer("unSpreadLight");
808 vmanip.unspreadLight(bank, unlight_from, light_sources);
811 //TimeTaker timer("spreadLight");
812 vmanip.spreadLight(bank, light_sources);
815 //TimeTaker timer("blitBack");
816 vmanip.blitBack(modified_blocks);
818 /*dstream<<"emerge_time="<<emerge_time<<std::endl;
822 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
825 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
826 core::map<v3s16, MapBlock*> & modified_blocks)
828 updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
829 updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
832 Update information about whether day and night light differ
834 for(core::map<v3s16, MapBlock*>::Iterator
835 i = modified_blocks.getIterator();
836 i.atEnd() == false; i++)
838 MapBlock *block = i.getNode()->getValue();
839 block->updateDayNightDiff();
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)
883 If the new node is solid and there is grass below, change it to mud
885 if(content_features(n.d).walkable == true)
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)
905 If the new node is mud and it is under sunlight, change it
908 if(n.d == CONTENT_MUD && node_under_sunlight)
915 Remove all light that has come out of this node
918 enum LightBank banks[] =
923 for(s32 i=0; i<2; i++)
925 enum LightBank bank = banks[i];
927 u8 lightwas = getNode(p).getLight(bank);
929 // Add the block of the added node to modified_blocks
930 v3s16 blockpos = getNodeBlockPos(p);
931 MapBlock * block = getBlockNoCreate(blockpos);
932 assert(block != NULL);
933 modified_blocks.insert(blockpos, block);
935 assert(isValidPosition(p));
937 // Unlight neighbours of node.
938 // This means setting light of all consequent dimmer nodes
940 // This also collects the nodes at the border which will spread
941 // light again into this.
942 unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
948 If node lets sunlight through and is under sunlight, it has
951 if(node_under_sunlight && content_features(n.d).sunlight_propagates)
953 n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
957 Set the node on the map
966 NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
969 NodeMetadata *meta = meta_proto->clone();
970 setNodeMetadata(p, meta);
974 If node is under sunlight and doesn't let sunlight through,
975 take all sunlighted nodes under it and clear light from them
976 and from where the light has been spread.
977 TODO: This could be optimized by mass-unlighting instead
980 if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
984 //m_dout<<DTIME<<"y="<<y<<std::endl;
985 v3s16 n2pos(p.X, y, p.Z);
991 catch(InvalidPositionException &e)
996 if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
998 unLightNeighbors(LIGHTBANK_DAY,
999 n2pos, n2.getLight(LIGHTBANK_DAY),
1000 light_sources, modified_blocks);
1001 n2.setLight(LIGHTBANK_DAY, 0);
1009 for(s32 i=0; i<2; i++)
1011 enum LightBank bank = banks[i];
1014 Spread light from all nodes that might be capable of doing so
1016 spreadLight(bank, light_sources, modified_blocks);
1020 Update information about whether day and night light differ
1022 for(core::map<v3s16, MapBlock*>::Iterator
1023 i = modified_blocks.getIterator();
1024 i.atEnd() == false; i++)
1026 MapBlock *block = i.getNode()->getValue();
1027 block->updateDayNightDiff();
1031 Add neighboring liquid nodes and the node itself if it is
1032 liquid (=water node was added) to transform queue.
1035 v3s16(0,0,0), // self
1036 v3s16(0,0,1), // back
1037 v3s16(0,1,0), // top
1038 v3s16(1,0,0), // right
1039 v3s16(0,0,-1), // front
1040 v3s16(0,-1,0), // bottom
1041 v3s16(-1,0,0), // left
1043 for(u16 i=0; i<7; i++)
1048 v3s16 p2 = p + dirs[i];
1050 MapNode n2 = getNode(p2);
1051 if(content_liquid(n2.d))
1053 m_transforming_liquid.push_back(p2);
1056 }catch(InvalidPositionException &e)
1064 void Map::removeNodeAndUpdate(v3s16 p,
1065 core::map<v3s16, MapBlock*> &modified_blocks)
1067 /*PrintInfo(m_dout);
1068 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1069 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1071 bool node_under_sunlight = true;
1073 v3s16 toppos = p + v3s16(0,1,0);
1075 // Node will be replaced with this
1076 u8 replace_material = CONTENT_AIR;
1079 If there is a node at top and it doesn't have sunlight,
1080 there will be no sunlight going down.
1083 MapNode topnode = getNode(toppos);
1085 if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1086 node_under_sunlight = false;
1088 catch(InvalidPositionException &e)
1092 core::map<v3s16, bool> light_sources;
1094 enum LightBank banks[] =
1099 for(s32 i=0; i<2; i++)
1101 enum LightBank bank = banks[i];
1104 Unlight neighbors (in case the node is a light source)
1106 unLightNeighbors(bank, p,
1107 getNode(p).getLight(bank),
1108 light_sources, modified_blocks);
1112 Remove node metadata
1115 removeNodeMetadata(p);
1119 This also clears the lighting.
1123 n.d = replace_material;
1126 for(s32 i=0; i<2; i++)
1128 enum LightBank bank = banks[i];
1131 Recalculate lighting
1133 spreadLight(bank, light_sources, modified_blocks);
1136 // Add the block of the removed node to modified_blocks
1137 v3s16 blockpos = getNodeBlockPos(p);
1138 MapBlock * block = getBlockNoCreate(blockpos);
1139 assert(block != NULL);
1140 modified_blocks.insert(blockpos, block);
1143 If the removed node was under sunlight, propagate the
1144 sunlight down from it and then light all neighbors
1145 of the propagated blocks.
1147 if(node_under_sunlight)
1149 s16 ybottom = propagateSunlight(p, modified_blocks);
1150 /*m_dout<<DTIME<<"Node was under sunlight. "
1151 "Propagating sunlight";
1152 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1154 for(; y >= ybottom; y--)
1156 v3s16 p2(p.X, y, p.Z);
1157 /*m_dout<<DTIME<<"lighting neighbors of node ("
1158 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1160 lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
1165 // Set the lighting of this node to 0
1166 // TODO: Is this needed? Lighting is cleared up there already.
1168 MapNode n = getNode(p);
1169 n.setLight(LIGHTBANK_DAY, 0);
1172 catch(InvalidPositionException &e)
1178 for(s32 i=0; i<2; i++)
1180 enum LightBank bank = banks[i];
1182 // Get the brightest neighbour node and propagate light from it
1183 v3s16 n2p = getBrightestNeighbour(bank, p);
1185 MapNode n2 = getNode(n2p);
1186 lightNeighbors(bank, n2p, modified_blocks);
1188 catch(InvalidPositionException &e)
1194 Update information about whether day and night light differ
1196 for(core::map<v3s16, MapBlock*>::Iterator
1197 i = modified_blocks.getIterator();
1198 i.atEnd() == false; i++)
1200 MapBlock *block = i.getNode()->getValue();
1201 block->updateDayNightDiff();
1205 Add neighboring liquid nodes to transform queue.
1208 v3s16(0,0,1), // back
1209 v3s16(0,1,0), // top
1210 v3s16(1,0,0), // right
1211 v3s16(0,0,-1), // front
1212 v3s16(0,-1,0), // bottom
1213 v3s16(-1,0,0), // left
1215 for(u16 i=0; i<6; i++)
1220 v3s16 p2 = p + dirs[i];
1222 MapNode n2 = getNode(p2);
1223 if(content_liquid(n2.d))
1225 m_transforming_liquid.push_back(p2);
1228 }catch(InvalidPositionException &e)
1234 bool Map::addNodeWithEvent(v3s16 p, MapNode n)
1237 event.type = MEET_ADDNODE;
1241 bool succeeded = true;
1243 core::map<v3s16, MapBlock*> modified_blocks;
1244 addNodeAndUpdate(p, n, modified_blocks);
1246 // Copy modified_blocks to event
1247 for(core::map<v3s16, MapBlock*>::Iterator
1248 i = modified_blocks.getIterator();
1249 i.atEnd()==false; i++)
1251 event.modified_blocks.insert(i.getNode()->getKey(), false);
1254 catch(InvalidPositionException &e){
1258 dispatchEvent(&event);
1263 bool Map::removeNodeWithEvent(v3s16 p)
1266 event.type = MEET_REMOVENODE;
1269 bool succeeded = true;
1271 core::map<v3s16, MapBlock*> modified_blocks;
1272 removeNodeAndUpdate(p, modified_blocks);
1274 // Copy modified_blocks to event
1275 for(core::map<v3s16, MapBlock*>::Iterator
1276 i = modified_blocks.getIterator();
1277 i.atEnd()==false; i++)
1279 event.modified_blocks.insert(i.getNode()->getKey(), false);
1282 catch(InvalidPositionException &e){
1286 dispatchEvent(&event);
1291 bool Map::dayNightDiffed(v3s16 blockpos)
1294 v3s16 p = blockpos + v3s16(0,0,0);
1295 MapBlock *b = getBlockNoCreate(p);
1296 if(b->dayNightDiffed())
1299 catch(InvalidPositionException &e){}
1302 v3s16 p = blockpos + v3s16(-1,0,0);
1303 MapBlock *b = getBlockNoCreate(p);
1304 if(b->dayNightDiffed())
1307 catch(InvalidPositionException &e){}
1309 v3s16 p = blockpos + v3s16(0,-1,0);
1310 MapBlock *b = getBlockNoCreate(p);
1311 if(b->dayNightDiffed())
1314 catch(InvalidPositionException &e){}
1316 v3s16 p = blockpos + v3s16(0,0,-1);
1317 MapBlock *b = getBlockNoCreate(p);
1318 if(b->dayNightDiffed())
1321 catch(InvalidPositionException &e){}
1324 v3s16 p = blockpos + v3s16(1,0,0);
1325 MapBlock *b = getBlockNoCreate(p);
1326 if(b->dayNightDiffed())
1329 catch(InvalidPositionException &e){}
1331 v3s16 p = blockpos + v3s16(0,1,0);
1332 MapBlock *b = getBlockNoCreate(p);
1333 if(b->dayNightDiffed())
1336 catch(InvalidPositionException &e){}
1338 v3s16 p = blockpos + v3s16(0,0,1);
1339 MapBlock *b = getBlockNoCreate(p);
1340 if(b->dayNightDiffed())
1343 catch(InvalidPositionException &e){}
1349 Updates usage timers
1351 void Map::timerUpdate(float dtime)
1353 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
1355 core::map<v2s16, MapSector*>::Iterator si;
1357 si = m_sectors.getIterator();
1358 for(; si.atEnd() == false; si++)
1360 MapSector *sector = si.getNode()->getValue();
1362 core::list<MapBlock*> blocks;
1363 sector->getBlocks(blocks);
1364 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1365 i != blocks.end(); i++)
1367 (*i)->incrementUsageTimer(dtime);
1372 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1374 core::list<v2s16>::Iterator j;
1375 for(j=list.begin(); j!=list.end(); j++)
1377 MapSector *sector = m_sectors[*j];
1380 sector->deleteBlocks();
1385 If sector is in sector cache, remove it from there
1387 if(m_sector_cache == sector)
1389 m_sector_cache = NULL;
1392 Remove from map and delete
1394 m_sectors.remove(*j);
1400 u32 Map::unloadUnusedData(float timeout, bool only_blocks,
1401 core::list<v3s16> *deleted_blocks)
1403 core::list<v2s16> sector_deletion_queue;
1405 core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
1406 for(; si.atEnd() == false; si++)
1408 MapSector *sector = si.getNode()->getValue();
1410 bool all_blocks_deleted = true;
1412 core::list<MapBlock*> blocks;
1413 sector->getBlocks(blocks);
1414 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1415 i != blocks.end(); i++)
1417 MapBlock *block = (*i);
1419 if(block->getUsageTimer() > timeout)
1422 if(block->getModified() != MOD_STATE_CLEAN)
1425 sector->removeBlock(block);
1430 all_blocks_deleted = false;
1434 if(all_blocks_deleted)
1436 sector_deletion_queue.push_back(si.getNode()->getKey());
1441 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1442 for(; i.atEnd() == false; i++)
1444 MapSector *sector = i.getNode()->getValue();
1446 Delete sector from memory if it hasn't been used in a long time
1448 if(sector->usage_timer > timeout)
1450 sector_deletion_queue.push_back(i.getNode()->getKey());
1452 if(deleted_blocks != NULL)
1454 // Collect positions of blocks of sector
1455 MapSector *sector = i.getNode()->getValue();
1456 core::list<MapBlock*> blocks;
1457 sector->getBlocks(blocks);
1458 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1459 i != blocks.end(); i++)
1461 deleted_blocks->push_back((*i)->getPos());
1468 deleteSectors(sector_deletion_queue, only_blocks);
1469 return sector_deletion_queue.getSize();
1472 void Map::PrintInfo(std::ostream &out)
1477 #define WATER_DROP_BOOST 4
1479 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
1481 DSTACK(__FUNCTION_NAME);
1482 //TimeTaker timer("transformLiquids()");
1485 u32 initial_size = m_transforming_liquid.size();
1487 /*if(initial_size != 0)
1488 dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
1490 while(m_transforming_liquid.size() != 0)
1493 Get a queued transforming liquid node
1495 v3s16 p0 = m_transforming_liquid.pop_front();
1497 MapNode n0 = getNode(p0);
1499 // Don't deal with non-liquids
1500 if(content_liquid(n0.d) == false)
1503 bool is_source = !content_flowing_liquid(n0.d);
1505 u8 liquid_level = 8;
1506 if(is_source == false)
1507 liquid_level = n0.param2 & 0x0f;
1509 // Turn possible source into non-source
1510 u8 nonsource_c = make_liquid_flowing(n0.d);
1513 If not source, check that some node flows into this one
1514 and what is the level of liquid in this one
1516 if(is_source == false)
1518 s8 new_liquid_level_max = -1;
1520 v3s16 dirs_from[5] = {
1521 v3s16(0,1,0), // top
1522 v3s16(0,0,1), // back
1523 v3s16(1,0,0), // right
1524 v3s16(0,0,-1), // front
1525 v3s16(-1,0,0), // left
1527 for(u16 i=0; i<5; i++)
1532 bool from_top = (i==0);
1534 v3s16 p2 = p0 + dirs_from[i];
1535 MapNode n2 = getNode(p2);
1537 if(content_liquid(n2.d))
1539 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1540 // Check that the liquids are the same type
1541 if(n2_nonsource_c != nonsource_c)
1543 dstream<<"WARNING: Not handling: different liquids"
1544 " collide"<<std::endl;
1547 bool n2_is_source = !content_flowing_liquid(n2.d);
1548 s8 n2_liquid_level = 8;
1549 if(n2_is_source == false)
1550 n2_liquid_level = n2.param2 & 0x07;
1552 s8 new_liquid_level = -1;
1555 //new_liquid_level = 7;
1556 if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
1557 new_liquid_level = 7;
1559 new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
1561 else if(n2_liquid_level > 0)
1563 new_liquid_level = n2_liquid_level - 1;
1566 if(new_liquid_level > new_liquid_level_max)
1567 new_liquid_level_max = new_liquid_level;
1570 }catch(InvalidPositionException &e)
1576 If liquid level should be something else, update it and
1577 add all the neighboring water nodes to the transform queue.
1579 if(new_liquid_level_max != liquid_level)
1581 if(new_liquid_level_max == -1)
1583 // Remove water alltoghether
1590 n0.param2 = new_liquid_level_max;
1594 // Block has been modified
1596 v3s16 blockpos = getNodeBlockPos(p0);
1597 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1599 modified_blocks.insert(blockpos, block);
1603 Add neighboring non-source liquid nodes to transform queue.
1606 v3s16(0,0,1), // back
1607 v3s16(0,1,0), // top
1608 v3s16(1,0,0), // right
1609 v3s16(0,0,-1), // front
1610 v3s16(0,-1,0), // bottom
1611 v3s16(-1,0,0), // left
1613 for(u16 i=0; i<6; i++)
1618 v3s16 p2 = p0 + dirs[i];
1620 MapNode n2 = getNode(p2);
1621 if(content_flowing_liquid(n2.d))
1623 m_transforming_liquid.push_back(p2);
1626 }catch(InvalidPositionException &e)
1633 // Get a new one from queue if the node has turned into non-water
1634 if(content_liquid(n0.d) == false)
1638 Flow water from this node
1640 v3s16 dirs_to[5] = {
1641 v3s16(0,-1,0), // bottom
1642 v3s16(0,0,1), // back
1643 v3s16(1,0,0), // right
1644 v3s16(0,0,-1), // front
1645 v3s16(-1,0,0), // left
1647 for(u16 i=0; i<5; i++)
1652 bool to_bottom = (i == 0);
1654 // If liquid is at lowest possible height, it's not going
1655 // anywhere except down
1656 if(liquid_level == 0 && to_bottom == false)
1659 u8 liquid_next_level = 0;
1660 // If going to bottom
1663 //liquid_next_level = 7;
1664 if(liquid_level >= 7 - WATER_DROP_BOOST)
1665 liquid_next_level = 7;
1667 liquid_next_level = liquid_level + WATER_DROP_BOOST;
1670 liquid_next_level = liquid_level - 1;
1672 bool n2_changed = false;
1673 bool flowed = false;
1675 v3s16 p2 = p0 + dirs_to[i];
1677 MapNode n2 = getNode(p2);
1678 //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
1680 if(content_liquid(n2.d))
1682 u8 n2_nonsource_c = make_liquid_flowing(n2.d);
1683 // Check that the liquids are the same type
1684 if(n2_nonsource_c != nonsource_c)
1686 dstream<<"WARNING: Not handling: different liquids"
1687 " collide"<<std::endl;
1690 bool n2_is_source = !content_flowing_liquid(n2.d);
1691 u8 n2_liquid_level = 8;
1692 if(n2_is_source == false)
1693 n2_liquid_level = n2.param2 & 0x07;
1702 // Just flow into the source, nothing changes.
1703 // n2_changed is not set because destination didn't change
1708 if(liquid_next_level > liquid_level)
1710 n2.param2 = liquid_next_level;
1718 else if(n2.d == CONTENT_AIR)
1721 n2.param2 = liquid_next_level;
1728 //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
1732 m_transforming_liquid.push_back(p2);
1734 v3s16 blockpos = getNodeBlockPos(p2);
1735 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1737 modified_blocks.insert(blockpos, block);
1740 // If n2_changed to bottom, don't flow anywhere else
1741 if(to_bottom && flowed && !is_source)
1744 }catch(InvalidPositionException &e)
1750 //if(loopcount >= 100000)
1751 if(loopcount >= initial_size * 1)
1754 //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
1757 NodeMetadata* Map::getNodeMetadata(v3s16 p)
1759 v3s16 blockpos = getNodeBlockPos(p);
1760 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1761 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1764 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1768 NodeMetadata *meta = block->m_node_metadata.get(p_rel);
1772 void Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
1774 v3s16 blockpos = getNodeBlockPos(p);
1775 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1776 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1779 dstream<<"WARNING: Map::setNodeMetadata(): Block not found"
1783 block->m_node_metadata.set(p_rel, meta);
1786 void Map::removeNodeMetadata(v3s16 p)
1788 v3s16 blockpos = getNodeBlockPos(p);
1789 v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
1790 MapBlock *block = getBlockNoCreateNoEx(blockpos);
1793 dstream<<"WARNING: Map::removeNodeMetadata(): Block not found"
1797 block->m_node_metadata.remove(p_rel);
1800 void Map::nodeMetadataStep(float dtime,
1801 core::map<v3s16, MapBlock*> &changed_blocks)
1805 Currently there is no way to ensure that all the necessary
1806 blocks are loaded when this is run. (They might get unloaded)
1807 NOTE: ^- Actually, that might not be so. In a quick test it
1808 reloaded a block with a furnace when I walked back to it from
1811 core::map<v2s16, MapSector*>::Iterator si;
1812 si = m_sectors.getIterator();
1813 for(; si.atEnd() == false; si++)
1815 MapSector *sector = si.getNode()->getValue();
1816 core::list< MapBlock * > sectorblocks;
1817 sector->getBlocks(sectorblocks);
1818 core::list< MapBlock * >::Iterator i;
1819 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
1821 MapBlock *block = *i;
1822 bool changed = block->m_node_metadata.step(dtime);
1824 changed_blocks[block->getPos()] = block;
1833 ServerMap::ServerMap(std::string savedir):
1836 m_map_metadata_changed(true)
1838 dstream<<__FUNCTION_NAME<<std::endl;
1840 //m_chunksize = 8; // Takes a few seconds
1842 m_seed = (((u64)(myrand()%0xffff)<<0)
1843 + ((u64)(myrand()%0xffff)<<16)
1844 + ((u64)(myrand()%0xffff)<<32)
1845 + ((u64)(myrand()%0xffff)<<48));
1848 Experimental and debug stuff
1855 Try to load map; if not found, create a new one.
1858 m_savedir = savedir;
1859 m_map_saving_enabled = false;
1863 // If directory exists, check contents and load if possible
1864 if(fs::PathExists(m_savedir))
1866 // If directory is empty, it is safe to save into it.
1867 if(fs::GetDirListing(m_savedir).size() == 0)
1869 dstream<<DTIME<<"Server: Empty save directory is valid."
1871 m_map_saving_enabled = true;
1876 // Load map metadata (seed, chunksize)
1879 catch(FileNotGoodException &e){
1880 dstream<<DTIME<<"WARNING: Could not load map metadata"
1881 //<<" Disabling chunk-based generator."
1887 // Load chunk metadata
1890 catch(FileNotGoodException &e){
1891 dstream<<DTIME<<"WARNING: Could not load chunk metadata."
1892 <<" Disabling chunk-based generator."
1897 /*dstream<<DTIME<<"Server: Successfully loaded chunk "
1898 "metadata and sector (0,0) from "<<savedir<<
1899 ", assuming valid save directory."
1902 dstream<<DTIME<<"INFO: Server: Successfully loaded map "
1903 <<"and chunk metadata from "<<savedir
1904 <<", assuming valid save directory."
1907 m_map_saving_enabled = true;
1908 // Map loaded, not creating new one
1912 // If directory doesn't exist, it is safe to save to it
1914 m_map_saving_enabled = true;
1917 catch(std::exception &e)
1919 dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
1920 <<", exception: "<<e.what()<<std::endl;
1921 dstream<<"Please remove the map or fix it."<<std::endl;
1922 dstream<<"WARNING: Map saving will be disabled."<<std::endl;
1925 dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
1927 // Create zero sector
1928 emergeSector(v2s16(0,0));
1930 // Initially write whole map
1934 ServerMap::~ServerMap()
1936 dstream<<__FUNCTION_NAME<<std::endl;
1940 if(m_map_saving_enabled)
1943 // Save only changed parts
1945 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1949 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1952 catch(std::exception &e)
1954 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1955 <<", exception: "<<e.what()<<std::endl;
1962 core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
1963 for(; i.atEnd() == false; i++)
1965 MapChunk *chunk = i.getNode()->getValue();
1972 Some helper functions for the map generator
1975 s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
1977 v3s16 em = vmanip.m_area.getExtent();
1978 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
1979 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
1980 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
1982 for(y=y_nodes_max; y>=y_nodes_min; y--)
1984 MapNode &n = vmanip.m_data[i];
1985 if(content_walkable(n.d))
1988 vmanip.m_area.add_y(em, i, -1);
1990 if(y >= y_nodes_min)
1996 s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
1998 v3s16 em = vmanip.m_area.getExtent();
1999 s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
2000 s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
2001 u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
2003 for(y=y_nodes_max; y>=y_nodes_min; y--)
2005 MapNode &n = vmanip.m_data[i];
2006 if(content_walkable(n.d)
2007 && n.d != CONTENT_TREE
2008 && n.d != CONTENT_LEAVES)
2011 vmanip.m_area.add_y(em, i, -1);
2013 if(y >= y_nodes_min)
2019 void make_tree(VoxelManipulator &vmanip, v3s16 p0)
2021 MapNode treenode(CONTENT_TREE);
2022 MapNode leavesnode(CONTENT_LEAVES);
2024 s16 trunk_h = myrand_range(3, 6);
2026 for(s16 ii=0; ii<trunk_h; ii++)
2028 if(vmanip.m_area.contains(p1))
2029 vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
2033 // p1 is now the last piece of the trunk
2036 VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
2037 //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
2038 Buffer<u8> leaves_d(leaves_a.getVolume());
2039 for(s32 i=0; i<leaves_a.getVolume(); i++)
2042 // Force leaves at near the end of the trunk
2045 for(s16 z=-d; z<=d; z++)
2046 for(s16 y=-d; y<=d; y++)
2047 for(s16 x=-d; x<=d; x++)
2049 leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
2053 // Add leaves randomly
2054 for(u32 iii=0; iii<7; iii++)
2059 myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
2060 myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
2061 myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
2064 for(s16 z=0; z<=d; z++)
2065 for(s16 y=0; y<=d; y++)
2066 for(s16 x=0; x<=d; x++)
2068 leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
2072 // Blit leaves to vmanip
2073 for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
2074 for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
2075 for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
2079 if(vmanip.m_area.contains(p) == false)
2081 u32 vi = vmanip.m_area.index(p);
2082 if(vmanip.m_data[vi].d != CONTENT_AIR
2083 && vmanip.m_data[vi].d != CONTENT_IGNORE)
2085 u32 i = leaves_a.index(x,y,z);
2086 if(leaves_d[i] == 1)
2087 vmanip.m_data[vi] = leavesnode;
2092 void make_randomstone(VoxelManipulator &vmanip, v3s16 p0)
2094 MapNode stonenode(CONTENT_STONE);
2096 s16 size = myrand_range(3, 6);
2098 VoxelArea stone_a(v3s16(-2,0,-2), v3s16(2,size,2));
2099 Buffer<u8> stone_d(stone_a.getVolume());
2100 for(s32 i=0; i<stone_a.getVolume(); i++)
2103 // Force stone at bottom to make it usually touch the ground
2105 for(s16 z=0; z<=0; z++)
2106 for(s16 y=0; y<=0; y++)
2107 for(s16 x=0; x<=0; x++)
2109 stone_d[stone_a.index(v3s16(x,y,z))] = 1;
2113 // Generate from perlin noise
2114 for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
2115 for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
2116 for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
2118 double d = noise3d_perlin((float)x/3.,(float)z/3.,(float)y/3.,
2119 p0.Z*4243+p0.Y*34+p0.X, 2, 0.5);
2120 if(z == stone_a.MinEdge.Z || z == stone_a.MaxEdge.Z)
2122 if(/*y == stone_a.MinEdge.Y ||*/ y == stone_a.MaxEdge.Y)
2124 if(x == stone_a.MinEdge.X || x == stone_a.MaxEdge.X)
2128 u32 vi = stone_a.index(v3s16(x,y,z));
2133 /*// Add stone randomly
2134 for(u32 iii=0; iii<7; iii++)
2139 myrand_range(stone_a.MinEdge.X, stone_a.MaxEdge.X-d),
2140 myrand_range(stone_a.MinEdge.Y, stone_a.MaxEdge.Y-d),
2141 myrand_range(stone_a.MinEdge.Z, stone_a.MaxEdge.Z-d)
2144 for(s16 z=0; z<=d; z++)
2145 for(s16 y=0; y<=d; y++)
2146 for(s16 x=0; x<=d; x++)
2148 stone_d[stone_a.index(p+v3s16(x,y,z))] = 1;
2152 // Blit stone to vmanip
2153 for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
2154 for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
2155 for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
2159 if(vmanip.m_area.contains(p) == false)
2161 u32 vi = vmanip.m_area.index(p);
2162 if(vmanip.m_data[vi].d != CONTENT_AIR
2163 && vmanip.m_data[vi].d != CONTENT_IGNORE)
2165 u32 i = stone_a.index(x,y,z);
2167 vmanip.m_data[vi] = stonenode;
2173 void make_largestone(VoxelManipulator &vmanip, v3s16 p0)
2175 MapNode stonenode(CONTENT_STONE);
2177 s16 size = myrand_range(8, 16);
2179 VoxelArea stone_a(v3s16(-size/2,0,-size/2), v3s16(size/2,size,size/2));
2180 Buffer<u8> stone_d(stone_a.getVolume());
2181 for(s32 i=0; i<stone_a.getVolume(); i++)
2184 // Force stone at bottom to make it usually touch the ground
2186 for(s16 z=0; z<=0; z++)
2187 for(s16 y=0; y<=0; y++)
2188 for(s16 x=0; x<=0; x++)
2190 stone_d[stone_a.index(v3s16(x,y,z))] = 1;
2194 // Generate from perlin noise
2195 for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
2196 for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
2197 for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
2200 d += noise3d_perlin((float)x/10.,(float)z/10.,(float)y/10.,
2201 p0.Z*5123+p0.Y*2439+p0.X, 2, 0.5);
2202 double mid_z = (stone_a.MaxEdge.Z+stone_a.MinEdge.Z)/2;
2203 double mid_x = (stone_a.MaxEdge.X+stone_a.MinEdge.X)/2;
2204 double mid_y = (stone_a.MaxEdge.Y+stone_a.MinEdge.Y)/2;
2205 double dz = (double)z-mid_z;
2206 double dx = (double)x-mid_x;
2207 double dy = MYMAX(0, (double)y-mid_y);
2208 double r = sqrt(dz*dz+dx*dx+dy*dy);
2209 d /= (2*r/size)*2 + 0.01;
2212 u32 vi = stone_a.index(v3s16(x,y,z));
2217 /*// Add stone randomly
2218 for(u32 iii=0; iii<7; iii++)
2223 myrand_range(stone_a.MinEdge.X, stone_a.MaxEdge.X-d),
2224 myrand_range(stone_a.MinEdge.Y, stone_a.MaxEdge.Y-d),
2225 myrand_range(stone_a.MinEdge.Z, stone_a.MaxEdge.Z-d)
2228 for(s16 z=0; z<=d; z++)
2229 for(s16 y=0; y<=d; y++)
2230 for(s16 x=0; x<=d; x++)
2232 stone_d[stone_a.index(p+v3s16(x,y,z))] = 1;
2236 // Blit stone to vmanip
2237 for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
2238 for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
2239 for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
2243 if(vmanip.m_area.contains(p) == false)
2245 u32 vi = vmanip.m_area.index(p);
2246 /*if(vmanip.m_data[vi].d != CONTENT_AIR
2247 && vmanip.m_data[vi].d != CONTENT_IGNORE)
2249 u32 i = stone_a.index(x,y,z);
2251 vmanip.m_data[vi] = stonenode;
2257 Dungeon making routines
2260 #define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1
2261 #define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2
2262 #define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\
2263 VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE)
2265 void make_room1(VoxelManipulator &vmanip, v3s16 roomsize, v3s16 roomplace)
2268 for(s16 z=0; z<roomsize.Z; z++)
2269 for(s16 y=0; y<roomsize.Y; y++)
2272 v3s16 p = roomplace + v3s16(0,y,z);
2273 if(vmanip.m_area.contains(p) == false)
2275 u32 vi = vmanip.m_area.index(p);
2276 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
2278 vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
2281 v3s16 p = roomplace + v3s16(roomsize.X-1,y,z);
2282 if(vmanip.m_area.contains(p) == false)
2284 u32 vi = vmanip.m_area.index(p);
2285 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
2287 vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
2292 for(s16 x=0; x<roomsize.X; x++)
2293 for(s16 y=0; y<roomsize.Y; y++)
2296 v3s16 p = roomplace + v3s16(x,y,0);
2297 if(vmanip.m_area.contains(p) == false)
2299 u32 vi = vmanip.m_area.index(p);
2300 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
2302 vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
2305 v3s16 p = roomplace + v3s16(x,y,roomsize.Z-1);
2306 if(vmanip.m_area.contains(p) == false)
2308 u32 vi = vmanip.m_area.index(p);
2309 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
2311 vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
2315 // Make +-Y walls (floor and ceiling)
2316 for(s16 z=0; z<roomsize.Z; z++)
2317 for(s16 x=0; x<roomsize.X; x++)
2320 v3s16 p = roomplace + v3s16(x,0,z);
2321 if(vmanip.m_area.contains(p) == false)
2323 u32 vi = vmanip.m_area.index(p);
2324 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
2326 vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
2329 v3s16 p = roomplace + v3s16(x,roomsize.Y-1,z);
2330 if(vmanip.m_area.contains(p) == false)
2332 u32 vi = vmanip.m_area.index(p);
2333 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
2335 vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
2340 for(s16 z=1; z<roomsize.Z-1; z++)
2341 for(s16 y=1; y<roomsize.Y-1; y++)
2342 for(s16 x=1; x<roomsize.X-1; x++)
2344 v3s16 p = roomplace + v3s16(x,y,z);
2345 if(vmanip.m_area.contains(p) == false)
2347 u32 vi = vmanip.m_area.index(p);
2348 vmanip.m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
2349 vmanip.m_data[vi] = MapNode(CONTENT_AIR);
2353 void make_fill(VoxelManipulator &vmanip, v3s16 place, v3s16 size,
2354 u8 avoid_flags, MapNode n, u8 or_flags)
2356 for(s16 z=0; z<size.Z; z++)
2357 for(s16 y=0; y<size.Y; y++)
2358 for(s16 x=0; x<size.X; x++)
2360 v3s16 p = place + v3s16(x,y,z);
2361 if(vmanip.m_area.contains(p) == false)
2363 u32 vi = vmanip.m_area.index(p);
2364 if(vmanip.m_flags[vi] & avoid_flags)
2366 vmanip.m_flags[vi] |= or_flags;
2367 vmanip.m_data[vi] = n;
2371 void make_hole1(VoxelManipulator &vmanip, v3s16 place)
2373 make_fill(vmanip, place, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
2374 VMANIP_FLAG_DUNGEON_INSIDE);
2377 void make_door1(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir)
2379 make_hole1(vmanip, doorplace);
2380 // Place torch (for testing)
2381 //vmanip.m_data[vmanip.m_area.index(doorplace)] = MapNode(CONTENT_TORCH);
2384 v3s16 rand_ortho_dir(PseudoRandom &random)
2386 if(random.next()%2==0)
2387 return random.next()%2 ? v3s16(-1,0,0) : v3s16(1,0,0);
2389 return random.next()%2 ? v3s16(0,0,-1) : v3s16(0,0,1);
2392 v3s16 turn_xz(v3s16 olddir, int t)
2412 v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
2414 int turn = random.range(0,2);
2423 dir = turn_xz(olddir, 0);
2426 dir = turn_xz(olddir, 1);
2430 void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir,
2431 v3s16 &result_place, v3s16 &result_dir, PseudoRandom &random)
2433 make_hole1(vmanip, doorplace);
2434 v3s16 p0 = doorplace;
2435 v3s16 dir = doordir;
2438 length = random.range(1,13);
2440 length = random.range(1,6);
2441 length = random.range(1,13);
2442 u32 partlength = random.range(1,length);
2444 s16 make_stairs = 0;
2445 if(random.next()%2 == 0 && partlength >= 3)
2446 make_stairs = random.next()%2 ? 1 : -1;
2447 for(u32 i=0; i<length; i++)
2453 /*// If already empty
2454 if(vmanip.getNodeNoExNoEmerge(p).d
2456 && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
2461 if(vmanip.m_area.contains(p) == true
2462 && vmanip.m_area.contains(p+v3s16(0,1,0)) == true)
2466 make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,5,3),
2467 VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(CONTENT_COBBLE), 0);
2468 make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
2469 VMANIP_FLAG_DUNGEON_INSIDE);
2470 make_fill(vmanip, p-dir, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
2471 VMANIP_FLAG_DUNGEON_INSIDE);
2475 make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,4,3),
2476 VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(CONTENT_COBBLE), 0);
2477 make_hole1(vmanip, p);
2478 /*make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
2479 VMANIP_FLAG_DUNGEON_INSIDE);*/
2486 // Can't go here, turn away
2487 dir = turn_xz(dir, random.range(0,1));
2488 make_stairs = -make_stairs;
2490 partlength = random.range(1,length);
2495 if(partcount >= partlength)
2499 dir = random_turn(random, dir);
2501 partlength = random.range(1,length);
2504 if(random.next()%2 == 0 && partlength >= 3)
2505 make_stairs = random.next()%2 ? 1 : -1;
2516 RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random):
2526 m_dir = rand_ortho_dir(m_random);
2529 void setPos(v3s16 pos)
2534 void setDir(v3s16 dir)
2539 bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
2541 for(u32 i=0; i<100; i++)
2543 v3s16 p = m_pos + m_dir;
2544 v3s16 p1 = p + v3s16(0,1,0);
2545 if(vmanip.m_area.contains(p) == false
2546 || vmanip.m_area.contains(p1) == false
2552 if(vmanip.getNodeNoExNoEmerge(p).d
2554 && vmanip.getNodeNoExNoEmerge(p1).d
2557 // Found wall, this is a good place!
2560 // Randomize next direction
2565 Determine where to move next
2567 // Jump one up if the actual space is there
2568 if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).d
2570 && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
2572 && vmanip.getNodeNoExNoEmerge(p+v3s16(0,2,0)).d
2575 // Jump one down if the actual space is there
2576 if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
2578 && vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).d
2580 && vmanip.getNodeNoExNoEmerge(p+v3s16(0,-1,0)).d
2583 // Check if walking is now possible
2584 if(vmanip.getNodeNoExNoEmerge(p).d
2586 || vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
2589 // Cannot continue walking here
2599 bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
2600 v3s16 &result_doordir, v3s16 &result_roomplace)
2602 for(s16 trycount=0; trycount<30; trycount++)
2606 bool r = findPlaceForDoor(doorplace, doordir);
2610 // X east, Z north, Y up
2611 if(doordir == v3s16(1,0,0)) // X+
2612 roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2+m_random.range(-roomsize.Z/2+1,roomsize.Z/2-1));
2613 if(doordir == v3s16(-1,0,0)) // X-
2614 roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2+m_random.range(-roomsize.Z/2+1,roomsize.Z/2-1));
2615 if(doordir == v3s16(0,0,1)) // Z+
2616 roomplace = doorplace + v3s16(-roomsize.X/2+m_random.range(-roomsize.X/2+1,roomsize.X/2-1),-1,0);
2617 if(doordir == v3s16(0,0,-1)) // Z-
2618 roomplace = doorplace + v3s16(-roomsize.X/2+m_random.range(-roomsize.X/2+1,roomsize.X/2-1),-1,-roomsize.Z+1);
2622 for(s16 z=1; z<roomsize.Z-1; z++)
2623 for(s16 y=1; y<roomsize.Y-1; y++)
2624 for(s16 x=1; x<roomsize.X-1; x++)
2626 v3s16 p = roomplace + v3s16(x,y,z);
2627 if(vmanip.m_area.contains(p) == false)
2632 if(vmanip.m_flags[vmanip.m_area.index(p)]
2633 & VMANIP_FLAG_DUNGEON_INSIDE)
2644 result_doorplace = doorplace;
2645 result_doordir = doordir;
2646 result_roomplace = roomplace;
2653 VoxelManipulator &vmanip;
2656 PseudoRandom &m_random;
2659 void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random)
2661 v3s16 areasize = vmanip.m_area.getExtent();
2666 Find place for first room
2669 for(u32 i=0; i<100; i++)
2671 roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
2672 roomplace = vmanip.m_area.MinEdge + v3s16(
2673 random.range(0,areasize.X-roomsize.X-1),
2674 random.range(0,areasize.Y-roomsize.Y-1),
2675 random.range(0,areasize.Z-roomsize.Z-1));
2677 Check that we're not putting the room to an unknown place,
2678 otherwise it might end up floating in the air
2681 for(s16 z=1; z<roomsize.Z-1; z++)
2682 for(s16 y=1; y<roomsize.Y-1; y++)
2683 for(s16 x=1; x<roomsize.X-1; x++)
2685 v3s16 p = roomplace + v3s16(x,y,z);
2686 u32 vi = vmanip.m_area.index(p);
2687 if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE)
2692 if(vmanip.m_data[vi].d == CONTENT_IGNORE)
2706 Stores the center position of the last room made, so that
2707 a new corridor can be started from the last room instead of
2708 the new room, if chosen so.
2710 v3s16 last_room_center = roomplace+v3s16(roomsize.X/2,1,roomsize.Z/2);
2712 u32 room_count = random.range(2,7);
2713 for(u32 i=0; i<room_count; i++)
2715 // Make a room to the determined place
2716 make_room1(vmanip, roomsize, roomplace);
2718 v3s16 room_center = roomplace + v3s16(roomsize.X/2,1,roomsize.Z/2);
2720 // Place torch at room center (for testing)
2721 //vmanip.m_data[vmanip.m_area.index(room_center)] = MapNode(CONTENT_TORCH);
2723 // Quit if last room
2724 if(i == room_count-1)
2727 // Determine walker start position
2729 bool start_in_last_room = (random.range(0,1)==0);
2730 //bool start_in_last_room = true;
2732 v3s16 walker_start_place;
2734 if(start_in_last_room)
2736 walker_start_place = last_room_center;
2740 walker_start_place = room_center;
2741 // Store center of current room as the last one
2742 last_room_center = room_center;
2745 // Create walker and find a place for a door
2746 RoomWalker walker(vmanip, walker_start_place, random);
2749 bool r = walker.findPlaceForDoor(doorplace, doordir);
2753 if(random.range(0,1)==0)
2755 make_door1(vmanip, doorplace, doordir);
2757 // Don't actually make a door
2758 doorplace -= doordir;
2760 // Make a random corridor starting from the door
2762 v3s16 corridor_end_dir;
2763 make_corridor(vmanip, doorplace, doordir, corridor_end,
2764 corridor_end_dir, random);
2766 // Find a place for a random sized room
2767 roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
2768 walker.setPos(corridor_end);
2769 walker.setDir(corridor_end_dir);
2770 r = walker.findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace);
2774 if(random.range(0,1)==0)
2776 make_door1(vmanip, doorplace, doordir);
2778 // Don't actually make a door
2779 roomplace -= doordir;
2785 Noise functions. Make sure seed is mangled differently in each one.
2788 // This affects the shape of the contour
2789 //#define CAVE_NOISE_SCALE 10.0
2790 //#define CAVE_NOISE_SCALE 7.5
2791 #define CAVE_NOISE_SCALE 5.0
2793 NoiseParams get_cave_noise1_params(u64 seed)
2795 /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.7,
2796 200, CAVE_NOISE_SCALE);*/
2797 return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.7,
2798 100, CAVE_NOISE_SCALE);
2801 NoiseParams get_cave_noise2_params(u64 seed)
2803 /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.7,
2804 200, CAVE_NOISE_SCALE);*/
2805 return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.7,
2806 100, CAVE_NOISE_SCALE);
2809 //#define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE)
2810 #define CAVE_NOISE_THRESHOLD (2.0/CAVE_NOISE_SCALE)
2812 NoiseParams get_ground_noise1_params(u64 seed)
2814 return NoiseParams(NOISE_PERLIN, seed+983240, 5,
2818 NoiseParams get_ground_crumbleness_params(u64 seed)
2820 return NoiseParams(NOISE_PERLIN, seed+34413, 3,
2824 NoiseParams get_ground_wetness_params(u64 seed)
2826 return NoiseParams(NOISE_PERLIN, seed+32474, 4,
2830 bool is_cave(u64 seed, v3s16 p)
2832 double d1 = noise3d_param(get_cave_noise1_params(seed), p.X,p.Y,p.Z);
2833 double d2 = noise3d_param(get_cave_noise2_params(seed), p.X,p.Y,p.Z);
2834 return d1*d2 > CAVE_NOISE_THRESHOLD;
2838 Ground density noise shall be interpreted by using this.
2840 TODO: No perlin noises here, they should be outsourced
2843 bool val_is_ground(double ground_noise1_val, v3s16 p, u64 seed)
2845 //return ((double)p.Y < ground_noise1_val);
2847 double f = 0.8 + noise2d_perlin(
2848 0.5+(float)p.X/250, 0.5+(float)p.Z/250,
2849 seed+920381, 3, 0.5);
2854 double h = WATER_LEVEL + 10 * noise2d_perlin(
2855 0.5+(float)p.X/250, 0.5+(float)p.Z/250,
2856 seed+84174, 4, 0.5);
2857 return ((double)p.Y - h < ground_noise1_val * f);
2861 Queries whether a position is ground or not.
2863 bool is_ground(u64 seed, v3s16 p)
2865 double val1 = noise3d_param(get_ground_noise1_params(seed), p.X,p.Y,p.Z);
2866 return val_is_ground(val1, p, seed);
2869 // Amount of trees per area in nodes
2870 double tree_amount_2d(u64 seed, v2s16 p)
2872 /*double noise = noise2d_perlin(
2873 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2875 double noise = noise2d_perlin(
2876 0.5+(float)p.X/125, 0.5+(float)p.Y/125,
2878 double zeroval = -0.35;
2882 return 0.04 * (noise-zeroval) / (1.0-zeroval);
2886 double randomstone_amount_2d(u64 seed, v2s16 p)
2888 double noise = noise2d_perlin(
2889 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2890 seed+3829434, 5, 0.66);
2891 double zeroval = 0.1;
2895 return 0.01 * (noise-zeroval) / (1.0-zeroval);
2899 double largestone_amount_2d(u64 seed, v2s16 p)
2901 double noise = noise2d_perlin(
2902 0.5+(float)p.X/250, 0.5+(float)p.Y/250,
2903 seed+14143242, 5, 0.66);
2904 double zeroval = 0.3;
2908 return 0.005 * (noise-zeroval) / (1.0-zeroval);
2912 Incrementally find ground level from 3d noise
2914 s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
2916 // Start a bit fuzzy to make averaging lower precision values
2918 s16 level = myrand_range(-precision/2, precision/2);
2919 s16 dec[] = {31000, 100, 20, 4, 1, 0};
2921 for(i = 1; dec[i] != 0 && precision <= dec[i]; i++)
2923 // First find non-ground by going upwards
2924 // Don't stop in caves.
2926 s16 max = level+dec[i-1]*2;
2927 v3s16 p(p2d.X, level, p2d.Y);
2928 for(; p.Y < max; p.Y += dec[i])
2930 if(!is_ground(seed, p))
2937 // Then find ground by going downwards from there.
2938 // Go in caves, too, when precision is 1.
2940 s16 min = level-dec[i-1]*2;
2941 v3s16 p(p2d.X, level, p2d.Y);
2942 for(; p.Y>min; p.Y-=dec[i])
2944 bool ground = is_ground(seed, p);
2945 /*if(dec[i] == 1 && is_cave(seed, p))
2956 // This is more like the actual ground level
2957 level += dec[i-1]/2;
2962 double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p=4);
2964 double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p)
2966 v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
2967 v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
2969 a += find_ground_level_from_noise(seed,
2970 v2s16(node_min.X, node_min.Y), p);
2971 a += find_ground_level_from_noise(seed,
2972 v2s16(node_min.X, node_max.Y), p);
2973 a += find_ground_level_from_noise(seed,
2974 v2s16(node_max.X, node_max.Y), p);
2975 a += find_ground_level_from_noise(seed,
2976 v2s16(node_max.X, node_min.Y), p);
2977 a += find_ground_level_from_noise(seed,
2978 v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p);
2983 double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p=4);
2985 double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p)
2987 v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
2988 v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
2990 a = MYMAX(a, find_ground_level_from_noise(seed,
2991 v2s16(node_min.X, node_min.Y), p));
2992 a = MYMAX(a, find_ground_level_from_noise(seed,
2993 v2s16(node_min.X, node_max.Y), p));
2994 a = MYMAX(a, find_ground_level_from_noise(seed,
2995 v2s16(node_max.X, node_max.Y), p));
2996 a = MYMAX(a, find_ground_level_from_noise(seed,
2997 v2s16(node_min.X, node_min.Y), p));
2998 a = MYMAX(a, find_ground_level_from_noise(seed,
2999 v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
3003 double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p=4);
3005 double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p)
3007 v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
3008 v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
3010 a = MYMIN(a, find_ground_level_from_noise(seed,
3011 v2s16(node_min.X, node_min.Y), p));
3012 a = MYMIN(a, find_ground_level_from_noise(seed,
3013 v2s16(node_min.X, node_max.Y), p));
3014 a = MYMIN(a, find_ground_level_from_noise(seed,
3015 v2s16(node_max.X, node_max.Y), p));
3016 a = MYMIN(a, find_ground_level_from_noise(seed,
3017 v2s16(node_min.X, node_min.Y), p));
3018 a = MYMIN(a, find_ground_level_from_noise(seed,
3019 v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
3024 #define AVERAGE_MUD_AMOUNT 4
3026 double base_rock_level_2d(u64 seed, v2s16 p)
3028 // The base ground level
3029 double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
3030 + 20. * noise2d_perlin(
3031 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
3032 (seed>>32)+654879876, 6, 0.6);
3034 /*// A bit hillier one
3035 double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
3036 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
3037 (seed>>27)+90340, 6, 0.69);
3041 // Higher ground level
3042 double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin(
3043 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
3044 seed+85039, 5, 0.69);
3045 //higher = 30; // For debugging
3047 // Limit higher to at least base
3051 // Steepness factor of cliffs
3052 double b = 1.0 + 1.0 * noise2d_perlin(
3053 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
3055 b = rangelim(b, 0.0, 1000.0);
3058 b = rangelim(b, 3.0, 1000.0);
3059 //dstream<<"b="<<b<<std::endl;
3062 // Offset to more low
3063 double a_off = -0.2;
3064 // High/low selector
3065 /*double a = 0.5 + b * (a_off + noise2d_perlin(
3066 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
3067 seed-359, 6, 0.7));*/
3068 double a = (double)0.5 + b * (a_off + noise2d_perlin(
3069 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
3070 seed-359, 5, 0.60));
3072 a = rangelim(a, 0.0, 1.0);
3074 //dstream<<"a="<<a<<std::endl;
3076 double h = base*(1.0-a) + higher*a;
3083 double get_mud_add_amount(u64 seed, v2s16 p)
3085 return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
3086 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
3087 seed+91013, 3, 0.55));
3091 bool get_have_sand(u64 seed, v2s16 p2d)
3093 // Determine whether to have sand here
3094 double sandnoise = noise2d_perlin(
3095 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
3096 seed+59420, 3, 0.50);
3098 return (sandnoise > -0.15);
3102 Adds random objects to block, depending on the content of the block
3104 void addRandomObjects(MapBlock *block)
3106 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3107 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3109 bool last_node_walkable = false;
3110 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3113 MapNode n = block->getNodeNoEx(p);
3114 if(n.d == CONTENT_IGNORE)
3116 if(content_features(n.d).liquid_type != LIQUID_NONE)
3118 if(content_features(n.d).walkable)
3120 last_node_walkable = true;
3123 if(last_node_walkable)
3125 // If block contains light information
3126 if(content_features(n.d).param_type == CPT_LIGHT)
3128 if(n.getLight(LIGHTBANK_DAY) <= 3)
3130 if(myrand() % 300 == 0)
3132 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
3134 ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
3135 std::string data = obj->getStaticData();
3136 StaticObject s_obj(obj->getType(),
3137 obj->getBasePosition(), data);
3139 block->m_static_objects.insert(0, s_obj);
3140 block->m_static_objects.insert(0, s_obj);
3141 block->m_static_objects.insert(0, s_obj);
3142 block->m_static_objects.insert(0, s_obj);
3143 block->m_static_objects.insert(0, s_obj);
3144 block->m_static_objects.insert(0, s_obj);
3147 if(myrand() % 1000 == 0)
3149 v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
3151 ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
3152 std::string data = obj->getStaticData();
3153 StaticObject s_obj(obj->getType(),
3154 obj->getBasePosition(), data);
3156 block->m_static_objects.insert(0, s_obj);
3162 last_node_walkable = false;
3165 block->setChangedFlag();
3168 void makeBlock(BlockMakeData *data)
3172 dstream<<"makeBlock: no-op"<<std::endl;
3176 v3s16 blockpos = data->blockpos;
3178 /*dstream<<"makeBlock(): ("<<blockpos.X<<","<<blockpos.Y<<","
3179 <<blockpos.Z<<")"<<std::endl;*/
3181 ManualMapVoxelManipulator &vmanip = data->vmanip;
3182 v3s16 blockpos_min = blockpos - v3s16(1,1,1);
3183 v3s16 blockpos_max = blockpos + v3s16(1,1,1);
3184 // Area of center block
3185 v3s16 node_min = blockpos*MAP_BLOCKSIZE;
3186 v3s16 node_max = (blockpos+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1);
3187 // Full allocated area
3188 v3s16 full_node_min = (blockpos-1)*MAP_BLOCKSIZE;
3189 v3s16 full_node_max = (blockpos+2)*MAP_BLOCKSIZE-v3s16(1,1,1);
3191 double block_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE;
3193 v2s16 p2d_center(node_min.X+MAP_BLOCKSIZE/2, node_min.Z+MAP_BLOCKSIZE/2);
3196 Get average ground level from noise
3199 s16 approx_groundlevel = (s16)get_sector_average_ground_level(
3200 data->seed, v2s16(blockpos.X, blockpos.Z));
3201 //dstream<<"approx_groundlevel="<<approx_groundlevel<<std::endl;
3203 s16 approx_ground_depth = approx_groundlevel - (node_min.Y+MAP_BLOCKSIZE/2);
3205 s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
3206 data->seed, v2s16(blockpos.X, blockpos.Z));
3207 // Minimum amount of ground above the top of the central block
3208 s16 minimum_ground_depth = minimum_groundlevel - node_max.Y;
3210 s16 maximum_groundlevel = (s16)get_sector_maximum_ground_level(
3211 data->seed, v2s16(blockpos.X, blockpos.Z), 1);
3212 // Maximum amount of ground above the bottom of the central block
3213 s16 maximum_ground_depth = maximum_groundlevel - node_min.Y;
3216 Special case for high air or water: Just fill with air and water.
3218 if(maximum_ground_depth < -20)
3220 for(s16 x=node_min.X; x<=node_max.X; x++)
3221 for(s16 z=node_min.Z; z<=node_max.Z; z++)
3226 // Use fast index incrementing
3227 v3s16 em = vmanip.m_area.getExtent();
3228 u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
3229 for(s16 y=node_min.Y; y<=node_max.Y; y++)
3231 // Only modify places that have no content
3232 if(vmanip.m_data[i].d == CONTENT_IGNORE)
3234 if(y <= WATER_LEVEL)
3235 vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE);
3237 vmanip.m_data[i] = MapNode(CONTENT_AIR);
3240 data->vmanip.m_area.add_y(em, i, 1);
3250 If block is deep underground, this is set to true and ground
3251 density noise is not generated, for speed optimization.
3253 bool all_is_ground_except_caves = (minimum_ground_depth > 16);
3256 Create a block-specific seed
3258 u32 blockseed = (data->seed%0x100000000) + full_node_min.Z*38134234
3259 + full_node_min.Y*42123 + full_node_min.X*23;
3265 //NoiseBuffer noisebuf1;
3266 //NoiseBuffer noisebuf2;
3267 NoiseBuffer noisebuf_cave;
3268 NoiseBuffer noisebuf_ground;
3269 NoiseBuffer noisebuf_ground_crumbleness;
3270 NoiseBuffer noisebuf_ground_wetness;
3272 v3f minpos_f(node_min.X, node_min.Y, node_min.Z);
3273 v3f maxpos_f(node_max.X, node_max.Y, node_max.Z);
3275 //TimeTaker timer("noisebuf.create");
3281 noisebuf_cave.create(get_cave_noise1_params(data->seed),
3282 minpos_f.X, minpos_f.Y, minpos_f.Z,
3283 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
3286 noisebuf_cave.multiply(get_cave_noise2_params(data->seed));
3293 v3f sl = v3f(4.0, 4.0, 4.0);
3298 if(all_is_ground_except_caves == false)
3299 //noisebuf_ground.create(data->seed+983240, 6, 0.60, false,
3300 noisebuf_ground.create(get_ground_noise1_params(data->seed),
3301 minpos_f.X, minpos_f.Y, minpos_f.Z,
3302 maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
3306 Ground property noise
3308 sl = v3f(2.5, 2.5, 2.5);
3309 noisebuf_ground_crumbleness.create(
3310 get_ground_crumbleness_params(data->seed),
3311 minpos_f.X, minpos_f.Y, minpos_f.Z,
3312 maxpos_f.X, maxpos_f.Y+5, maxpos_f.Z,
3314 noisebuf_ground_wetness.create(
3315 get_ground_wetness_params(data->seed),
3316 minpos_f.X, minpos_f.Y, minpos_f.Z,
3317 maxpos_f.X, maxpos_f.Y+5, maxpos_f.Z,
3322 Make base ground level
3325 for(s16 x=node_min.X; x<=node_max.X; x++)
3326 for(s16 z=node_min.Z; z<=node_max.Z; z++)
3331 // Use fast index incrementing
3332 v3s16 em = vmanip.m_area.getExtent();
3333 u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
3334 for(s16 y=node_min.Y; y<=node_max.Y; y++)
3336 // Only modify places that have no content
3337 if(vmanip.m_data[i].d == CONTENT_IGNORE)
3339 // First priority: make air and water.
3340 // This avoids caves inside water.
3341 if(all_is_ground_except_caves == false
3342 && val_is_ground(noisebuf_ground.get(x,y,z),
3343 v3s16(x,y,z), data->seed) == false)
3345 if(y <= WATER_LEVEL)
3346 vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE);
3348 vmanip.m_data[i] = MapNode(CONTENT_AIR);
3350 else if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD)
3351 vmanip.m_data[i] = MapNode(CONTENT_AIR);
3353 vmanip.m_data[i] = MapNode(CONTENT_STONE);
3356 data->vmanip.m_area.add_y(em, i, 1);
3366 PseudoRandom mineralrandom(blockseed);
3371 for(s16 i=0; i<approx_ground_depth/4; i++)
3373 if(mineralrandom.next()%50 == 0)
3375 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
3376 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
3377 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
3378 for(u16 i=0; i<27; i++)
3380 v3s16 p = v3s16(x,y,z) + g_27dirs[i];
3381 u32 vi = vmanip.m_area.index(p);
3382 if(vmanip.m_data[vi].d == CONTENT_STONE)
3383 if(mineralrandom.next()%8 == 0)
3384 vmanip.m_data[vi] = MapNode(CONTENT_MESE);
3393 u16 a = mineralrandom.range(0,15);
3395 u16 amount = 20 * a/1000;
3396 for(s16 i=0; i<amount; i++)
3398 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
3399 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
3400 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
3402 u8 base_content = CONTENT_STONE;
3403 MapNode new_content(CONTENT_IGNORE);
3406 if(noisebuf_ground_crumbleness.get(x,y+5,z) < -0.1)
3408 new_content = MapNode(CONTENT_STONE, MINERAL_COAL);
3412 if(noisebuf_ground_wetness.get(x,y+5,z) > 0.0)
3413 new_content = MapNode(CONTENT_STONE, MINERAL_IRON);
3414 /*if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
3415 vmanip.m_data[i] = MapNode(CONTENT_MUD);
3417 vmanip.m_data[i] = MapNode(CONTENT_SAND);*/
3419 /*else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.1)
3423 if(new_content.d != CONTENT_IGNORE)
3425 for(u16 i=0; i<27; i++)
3427 v3s16 p = v3s16(x,y,z) + g_27dirs[i];
3428 u32 vi = vmanip.m_area.index(p);
3429 if(vmanip.m_data[vi].d == base_content)
3431 if(mineralrandom.next()%sparseness == 0)
3432 vmanip.m_data[vi] = new_content;
3441 //for(s16 i=0; i < MYMAX(0, 50 - abs(node_min.Y+8 - (-30))); i++)
3442 //for(s16 i=0; i<50; i++)
3443 u16 coal_amount = 30;
3444 u16 coal_rareness = 60 / coal_amount;
3445 if(coal_rareness == 0)
3447 if(mineralrandom.next()%coal_rareness == 0)
3449 u16 a = mineralrandom.next() % 16;
3450 u16 amount = coal_amount * a*a*a / 1000;
3451 for(s16 i=0; i<amount; i++)
3453 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
3454 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
3455 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
3456 for(u16 i=0; i<27; i++)
3458 v3s16 p = v3s16(x,y,z) + g_27dirs[i];
3459 u32 vi = vmanip.m_area.index(p);
3460 if(vmanip.m_data[vi].d == CONTENT_STONE)
3461 if(mineralrandom.next()%8 == 0)
3462 vmanip.m_data[vi] = MapNode(CONTENT_STONE, MINERAL_COAL);
3469 u16 iron_amount = 8;
3470 u16 iron_rareness = 60 / iron_amount;
3471 if(iron_rareness == 0)
3473 if(mineralrandom.next()%iron_rareness == 0)
3475 u16 a = mineralrandom.next() % 16;
3476 u16 amount = iron_amount * a*a*a / 1000;
3477 for(s16 i=0; i<amount; i++)
3479 s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
3480 s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
3481 s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
3482 for(u16 i=0; i<27; i++)
3484 v3s16 p = v3s16(x,y,z) + g_27dirs[i];
3485 u32 vi = vmanip.m_area.index(p);
3486 if(vmanip.m_data[vi].d == CONTENT_STONE)
3487 if(mineralrandom.next()%8 == 0)
3488 vmanip.m_data[vi] = MapNode(CONTENT_STONE, MINERAL_IRON);
3495 Add mud and sand and others underground (in place of stone)
3498 for(s16 x=node_min.X; x<=node_max.X; x++)
3499 for(s16 z=node_min.Z; z<=node_max.Z; z++)
3504 // Use fast index incrementing
3505 v3s16 em = vmanip.m_area.getExtent();
3506 u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
3507 for(s16 y=node_max.Y; y>=node_min.Y; y--)
3509 if(vmanip.m_data[i].d == CONTENT_STONE)
3511 if(noisebuf_ground_crumbleness.get(x,y,z) > 1.3)
3513 if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
3514 vmanip.m_data[i] = MapNode(CONTENT_MUD);
3516 vmanip.m_data[i] = MapNode(CONTENT_SAND);
3518 else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.7)
3520 if(noisebuf_ground_wetness.get(x,y,z) < -0.6)
3521 vmanip.m_data[i] = MapNode(CONTENT_GRAVEL);
3525 data->vmanip.m_area.add_y(em, i, -1);
3534 //if(node_min.Y < approx_groundlevel)
3535 //if(myrand() % 3 == 0)
3536 //if(myrand() % 3 == 0 && node_min.Y < approx_groundlevel)
3537 //if(myrand() % 100 == 0 && node_min.Y < approx_groundlevel)
3538 //float dungeon_rarity = g_settings.getFloat("dungeon_rarity");
3539 float dungeon_rarity = 0.02;
3540 if(((noise3d(blockpos.X,blockpos.Y,blockpos.Z,data->seed)+1.0)/2.0)
3542 && node_min.Y < approx_groundlevel)
3544 // Dungeon generator doesn't modify places which have this set
3545 data->vmanip.clearFlag(VMANIP_FLAG_DUNGEON_INSIDE
3546 | VMANIP_FLAG_DUNGEON_PRESERVE);
3548 // Set all air and water to be untouchable to make dungeons open
3549 // to caves and open air
3550 for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
3551 for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
3556 // Use fast index incrementing
3557 v3s16 em = vmanip.m_area.getExtent();
3558 u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
3559 for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
3561 if(vmanip.m_data[i].d == CONTENT_AIR)
3562 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
3563 else if(vmanip.m_data[i].d == CONTENT_WATERSOURCE)
3564 vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
3565 data->vmanip.m_area.add_y(em, i, -1);
3570 PseudoRandom random(blockseed+2);
3573 make_dungeon1(data->vmanip, random);
3575 // Convert some cobble to mossy cobble
3576 for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
3577 for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
3582 // Use fast index incrementing
3583 v3s16 em = vmanip.m_area.getExtent();
3584 u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
3585 for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
3587 // (noisebuf not used because it doesn't contain the
3589 double wetness = noise3d_param(
3590 get_ground_wetness_params(data->seed), x,y,z);
3591 double d = noise3d_perlin((float)x/2.5,
3592 (float)y/2.5,(float)z/2.5,
3594 if(vmanip.m_data[i].d == CONTENT_COBBLE)
3598 vmanip.m_data[i].d = CONTENT_MOSSYCOBBLE;
3601 /*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE)
3604 vmanip.m_data[i].d = CONTENT_MUD;
3606 data->vmanip.m_area.add_y(em, i, -1);
3613 Add top and bottom side of water to transforming_liquid queue
3616 for(s16 x=node_min.X; x<=node_max.X; x++)
3617 for(s16 z=node_min.Z; z<=node_max.Z; z++)
3622 bool water_found = false;
3623 // Use fast index incrementing
3624 v3s16 em = vmanip.m_area.getExtent();
3625 u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
3626 for(s16 y=node_max.Y; y>=node_min.Y; y--)
3628 if(water_found == false)
3630 if(vmanip.m_data[i].d == CONTENT_WATERSOURCE)
3632 v3s16 p = v3s16(p2d.X, y, p2d.Y);
3633 data->transforming_liquid.push_back(p);
3639 // This can be done because water_found can only
3640 // turn to true and end up here after going through
3642 if(vmanip.m_data[i+1].d != CONTENT_WATERSOURCE)
3644 v3s16 p = v3s16(p2d.X, y+1, p2d.Y);
3645 data->transforming_liquid.push_back(p);
3646 water_found = false;
3650 data->vmanip.m_area.add_y(em, i, -1);
3656 If close to ground level
3659 //if(abs(approx_ground_depth) < 30)
3660 if(minimum_ground_depth < 5 && maximum_ground_depth > -5)
3666 for(s16 x=node_min.X; x<=node_max.X; x++)
3667 for(s16 z=node_min.Z; z<=node_max.Z; z++)
3672 bool possibly_have_sand = get_have_sand(data->seed, p2d);
3673 bool have_sand = false;
3674 u32 current_depth = 0;
3675 bool air_detected = false;
3676 bool water_detected = false;
3677 // Use fast index incrementing
3678 s16 start_y = node_max.Y+2;
3679 v3s16 em = vmanip.m_area.getExtent();
3680 u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y));
3681 for(s16 y=start_y; y>=node_min.Y-3; y--)
3683 if(vmanip.m_data[i].d == CONTENT_WATERSOURCE)
3684 water_detected = true;
3685 if(vmanip.m_data[i].d == CONTENT_AIR)
3686 air_detected = true;
3688 if((vmanip.m_data[i].d == CONTENT_STONE
3689 || vmanip.m_data[i].d == CONTENT_GRASS
3690 || vmanip.m_data[i].d == CONTENT_MUD
3691 || vmanip.m_data[i].d == CONTENT_SAND
3692 || vmanip.m_data[i].d == CONTENT_GRAVEL
3693 ) && (air_detected || water_detected))
3695 if(current_depth == 0 && y <= WATER_LEVEL+2
3696 && possibly_have_sand)
3699 if(current_depth < 4)
3703 vmanip.m_data[i] = MapNode(CONTENT_SAND);
3706 else if(current_depth==0 && !water_detected
3707 && y >= WATER_LEVEL && air_detected)
3708 vmanip.m_data[i] = MapNode(CONTENT_GRASS);
3711 vmanip.m_data[i] = MapNode(CONTENT_MUD);
3715 if(vmanip.m_data[i].d == CONTENT_MUD
3716 || vmanip.m_data[i].d == CONTENT_GRASS)
3717 vmanip.m_data[i] = MapNode(CONTENT_STONE);
3722 if(current_depth >= 8)
3725 else if(current_depth != 0)
3728 data->vmanip.m_area.add_y(em, i, -1);
3738 u32 tree_count = block_area_nodes * tree_amount_2d(data->seed, p2d_center);
3739 PseudoRandom treerandom(blockseed);
3740 // Put trees in random places on part of division
3741 for(u32 i=0; i<tree_count; i++)
3743 s16 x = treerandom.range(node_min.X, node_max.X);
3744 s16 z = treerandom.range(node_min.Z, node_max.Z);
3745 //s16 y = find_ground_level(data->vmanip, v2s16(x,z));
3746 s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4);
3747 // Don't make a tree under water level
3750 // Make sure tree fits (only trees whose starting point is
3751 // at this block are added)
3752 if(y < node_min.Y || y > node_max.Y)
3755 Find exact ground level
3759 for(; p.Y >= y-6; p.Y--)
3761 u32 i = data->vmanip.m_area.index(p);
3762 MapNode *n = &data->vmanip.m_data[i];
3763 if(n->d != CONTENT_AIR && n->d != CONTENT_IGNORE)
3769 // If not found, handle next one
3773 Trees grow only on mud and grass
3776 u32 i = data->vmanip.m_area.index(p);
3777 MapNode *n = &data->vmanip.m_data[i];
3778 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3781 // Tree will be placed one higher
3784 make_tree(data->vmanip, p);
3789 Add some kind of random stones
3792 u32 random_stone_count = block_area_nodes *
3793 randomstone_amount_2d(data->seed, p2d_center);
3794 // Put in random places on part of division
3795 for(u32 i=0; i<random_stone_count; i++)
3797 s16 x = myrand_range(node_min.X, node_max.X);
3798 s16 z = myrand_range(node_min.Z, node_max.Z);
3799 s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
3800 // Don't add under water level
3801 /*if(y < WATER_LEVEL)
3803 // Don't add if doesn't belong to this block
3804 if(y < node_min.Y || y > node_max.Y)
3809 u32 i = data->vmanip.m_area.index(v3s16(p));
3810 MapNode *n = &data->vmanip.m_data[i];
3811 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3814 // Will be placed one higher
3817 make_randomstone(data->vmanip, p);
3826 u32 large_stone_count = block_area_nodes *
3827 largestone_amount_2d(data->seed, p2d_center);
3828 //u32 large_stone_count = 1;
3829 // Put in random places on part of division
3830 for(u32 i=0; i<large_stone_count; i++)
3832 s16 x = myrand_range(node_min.X, node_max.X);
3833 s16 z = myrand_range(node_min.Z, node_max.Z);
3834 s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
3835 // Don't add under water level
3836 /*if(y < WATER_LEVEL)
3838 // Don't add if doesn't belong to this block
3839 if(y < node_min.Y || y > node_max.Y)
3844 u32 i = data->vmanip.m_area.index(v3s16(p));
3845 MapNode *n = &data->vmanip.m_data[i];
3846 if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
3849 // Will be placed one lower
3852 make_largestone(data->vmanip, p);
3859 void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
3861 /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
3862 <<blockpos.Z<<")"<<std::endl;*/
3864 data->no_op = false;
3865 data->seed = m_seed;
3866 data->blockpos = blockpos;
3869 Create the whole area of this and the neighboring blocks
3872 //TimeTaker timer("initBlockMake() create area");
3874 for(s16 x=-1; x<=1; x++)
3875 for(s16 z=-1; z<=1; z++)
3877 v2s16 sectorpos(blockpos.X+x, blockpos.Z+z);
3878 // Sector metadata is loaded from disk if not already loaded.
3879 ServerMapSector *sector = createSector(sectorpos);
3882 for(s16 y=-1; y<=1; y++)
3884 MapBlock *block = createBlock(blockpos);
3886 // Lighting won't be calculated
3887 block->setLightingExpired(true);
3888 // Lighting will be calculated
3889 //block->setLightingExpired(false);
3892 Block gets sunlight if this is true.
3894 This should be set to true when the top side of a block
3895 is completely exposed to the sky.
3897 block->setIsUnderground(false);
3903 Now we have a big empty area.
3905 Make a ManualMapVoxelManipulator that contains this and the
3909 v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
3910 v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
3912 data->vmanip.setMap(this);
3916 //TimeTaker timer("initBlockMake() initialEmerge");
3917 data->vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
3920 // Data is ready now.
3923 MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
3924 core::map<v3s16, MapBlock*> &changed_blocks)
3926 v3s16 blockpos = data->blockpos;
3927 /*dstream<<"finishBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
3928 <<blockpos.Z<<")"<<std::endl;*/
3932 dstream<<"finishBlockMake(): no-op"<<std::endl;
3936 /*dstream<<"Resulting vmanip:"<<std::endl;
3937 data->vmanip.print(dstream);*/
3940 Blit generated stuff to map
3941 NOTE: blitBackAll adds nearly everything to changed_blocks
3945 //TimeTaker timer("finishBlockMake() blitBackAll");
3946 data->vmanip.blitBackAll(&changed_blocks);
3949 dstream<<"finishBlockMake: changed_blocks.size()="
3950 <<changed_blocks.size()<<std::endl;
3953 Copy transforming liquid information
3955 while(data->transforming_liquid.size() > 0)
3957 v3s16 p = data->transforming_liquid.pop_front();
3958 m_transforming_liquid.push_back(p);
3964 MapBlock *block = getBlockNoCreateNoEx(data->blockpos);
3968 Set is_underground flag for lighting with sunlight
3971 s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
3972 data->seed, v2s16(blockpos.X, blockpos.Z));
3974 if(blockpos.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
3975 block->setIsUnderground(true);
3977 block->setIsUnderground(false);
3980 Add sunlight to central block.
3981 This makes in-dark-spawning monsters to not flood the whole thing.
3982 Do not spread the light, though.
3984 /*core::map<v3s16, bool> light_sources;
3985 bool black_air_left = false;
3986 block->propagateSunlight(light_sources, true, &black_air_left);*/
3989 NOTE: Lighting and object adding shouldn't really be here, but
3990 lighting is a bit tricky to move properly to makeBlock.
3991 TODO: Do this the right way anyway.
3998 core::map<v3s16, MapBlock*> lighting_update_blocks;
4000 lighting_update_blocks.insert(block->getPos(), block);
4002 // All modified blocks
4003 for(core::map<v3s16, MapBlock*>::Iterator
4004 i = changed_blocks.getIterator();
4005 i.atEnd() == false; i++)
4007 lighting_update_blocks.insert(i.getNode()->getKey(),
4008 i.getNode()->getValue());
4011 updateLighting(lighting_update_blocks, changed_blocks);
4014 Add random objects to block
4016 addRandomObjects(block);
4019 Go through changed blocks
4021 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
4022 i.atEnd() == false; i++)
4024 MapBlock *block = i.getNode()->getValue();
4027 Update day/night difference cache of the MapBlocks
4029 block->updateDayNightDiff();
4031 Set block as modified
4033 block->raiseModified(MOD_STATE_WRITE_NEEDED);
4037 Set central block as generated
4039 block->setGenerated(true);
4042 Save changed parts of map
4043 NOTE: Will be saved later.
4047 /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
4048 <<blockpos.Z<<")"<<std::endl;*/
4053 ServerMapSector * ServerMap::createSector(v2s16 p2d)
4055 DSTACKF("%s: p2d=(%d,%d)",
4060 Check if it exists already in memory
4062 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
4067 Try to load it from disk (with blocks)
4069 //if(loadSectorFull(p2d) == true)
4072 Try to load metadata from disk
4074 if(loadSectorMeta(p2d) == true)
4076 ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
4079 dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl;
4080 throw InvalidPositionException("");
4086 Do not create over-limit
4088 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4089 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4090 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4091 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4092 throw InvalidPositionException("createSector(): pos. over limit");
4095 Generate blank sector
4098 sector = new ServerMapSector(this, p2d);
4100 // Sector position on map in nodes
4101 v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
4106 m_sectors.insert(p2d, sector);
4112 This is a quick-hand function for calling makeBlock().
4114 MapBlock * ServerMap::generateBlock(
4116 core::map<v3s16, MapBlock*> &modified_blocks
4119 DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z);
4121 /*dstream<<"generateBlock(): "
4122 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4125 TimeTaker timer("generateBlock");
4127 //MapBlock *block = original_dummy;
4129 v2s16 p2d(p.X, p.Z);
4130 v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
4133 Do not generate over-limit
4135 if(blockpos_over_limit(p))
4137 dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
4138 throw InvalidPositionException("generateBlock(): pos. over limit");
4142 Create block make data
4145 initBlockMake(&data, p);
4151 TimeTaker t("makeBlock()");
4156 Blit data back on map, update lighting, add mobs and whatever this does
4158 finishBlockMake(&data, modified_blocks);
4163 MapBlock *block = getBlockNoCreateNoEx(p);
4170 bool erroneus_content = false;
4171 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4172 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4173 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4176 MapNode n = block->getNode(p);
4177 if(n.d == CONTENT_IGNORE)
4179 dstream<<"CONTENT_IGNORE at "
4180 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4182 erroneus_content = true;
4186 if(erroneus_content)
4194 Generate a completely empty block
4196 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4197 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4199 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4205 n.d = CONTENT_STONE;
4206 block->setNode(v3s16(x0,y0,z0), n);
4214 MapBlock * ServerMap::createBlock(v3s16 p)
4216 DSTACKF("%s: p=(%d,%d,%d)",
4217 __FUNCTION_NAME, p.X, p.Y, p.Z);
4220 Do not create over-limit
4222 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4223 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4224 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4225 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4226 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4227 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4228 throw InvalidPositionException("createBlock(): pos. over limit");
4230 v2s16 p2d(p.X, p.Z);
4233 This will create or load a sector if not found in memory.
4234 If block exists on disk, it will be loaded.
4236 NOTE: On old save formats, this will be slow, as it generates
4237 lighting on blocks for them.
4239 ServerMapSector *sector;
4241 sector = (ServerMapSector*)createSector(p2d);
4242 assert(sector->getId() == MAPSECTOR_SERVER);
4244 catch(InvalidPositionException &e)
4246 dstream<<"createBlock: createSector() failed"<<std::endl;
4250 NOTE: This should not be done, or at least the exception
4251 should not be passed on as std::exception, because it
4252 won't be catched at all.
4254 /*catch(std::exception &e)
4256 dstream<<"createBlock: createSector() failed: "
4257 <<e.what()<<std::endl;
4262 Try to get a block from the sector
4265 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4268 if(block->isDummy())
4273 block = sector->createBlankBlock(block_y);
4278 MapBlock * ServerMap::emergeBlock(
4280 bool only_from_disk,
4281 core::map<v3s16, MapBlock*> &changed_blocks,
4282 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
4285 DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
4287 p.X, p.Y, p.Z, only_from_disk);
4289 // This has to be redone or removed
4297 Do not generate over-limit
4299 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4300 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4301 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4302 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4303 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
4304 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
4305 throw InvalidPositionException("emergeBlock(): pos. over limit");
4307 v2s16 p2d(p.X, p.Z);
4310 This will create or load a sector if not found in memory.
4311 If block exists on disk, it will be loaded.
4313 ServerMapSector *sector;
4315 sector = createSector(p2d);
4316 //sector = emergeSector(p2d, changed_blocks);
4318 catch(InvalidPositionException &e)
4320 dstream<<"emergeBlock: createSector() failed: "
4321 <<e.what()<<std::endl;
4322 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4324 <<"You could try to delete it."<<std::endl;
4327 catch(VersionMismatchException &e)
4329 dstream<<"emergeBlock: createSector() failed: "
4330 <<e.what()<<std::endl;
4331 dstream<<"Path to failed sector: "<<getSectorDir(p2d)
4333 <<"You could try to delete it."<<std::endl;
4338 Try to get a block from the sector
4341 bool does_not_exist = false;
4342 bool lighting_expired = false;
4343 MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
4345 // If not found, try loading from disk
4348 block = loadBlock(p);
4354 does_not_exist = true;
4356 else if(block->isDummy() == true)
4358 does_not_exist = true;
4360 else if(block->getLightingExpired())
4362 lighting_expired = true;
4367 //dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
4372 If block was not found on disk and not going to generate a
4373 new one, make sure there is a dummy block in place.
4375 if(only_from_disk && (does_not_exist || lighting_expired))
4377 //dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
4381 // Create dummy block
4382 block = new MapBlock(this, p, true);
4384 // Add block to sector
4385 sector->insertBlock(block);
4391 //dstream<<"Not found on disk, generating."<<std::endl;
4393 //TimeTaker("emergeBlock() generate");
4395 //dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
4398 If the block doesn't exist, generate the block.
4402 block = generateBlock(p, block, sector, changed_blocks,
4403 lighting_invalidated_blocks);
4406 if(lighting_expired)
4408 lighting_invalidated_blocks.insert(p, block);
4413 Initially update sunlight
4416 core::map<v3s16, bool> light_sources;
4417 bool black_air_left = false;
4418 bool bottom_invalid =
4419 block->propagateSunlight(light_sources, true,
4422 // If sunlight didn't reach everywhere and part of block is
4423 // above ground, lighting has to be properly updated
4424 //if(black_air_left && some_part_underground)
4427 lighting_invalidated_blocks[block->getPos()] = block;
4432 lighting_invalidated_blocks[block->getPos()] = block;
4441 s16 ServerMap::findGroundLevel(v2s16 p2d)
4445 Uh, just do something random...
4447 // Find existing map from top to down
4450 v3s16 p(p2d.X, max, p2d.Y);
4451 for(; p.Y>min; p.Y--)
4453 MapNode n = getNodeNoEx(p);
4454 if(n.d != CONTENT_IGNORE)
4459 // If this node is not air, go to plan b
4460 if(getNodeNoEx(p).d != CONTENT_AIR)
4462 // Search existing walkable and return it
4463 for(; p.Y>min; p.Y--)
4465 MapNode n = getNodeNoEx(p);
4466 if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
4475 Determine from map generator noise functions
4478 s16 level = find_ground_level_from_noise(m_seed, p2d, 1);
4481 //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
4482 //return (s16)level;
4485 void ServerMap::createDirs(std::string path)
4487 if(fs::CreateAllDirs(path) == false)
4489 m_dout<<DTIME<<"ServerMap: Failed to create directory "
4490 <<"\""<<path<<"\""<<std::endl;
4491 throw BaseException("ServerMap failed to create directory");
4495 std::string ServerMap::getSectorDir(v2s16 pos, int layout)
4501 snprintf(cc, 9, "%.4x%.4x",
4502 (unsigned int)pos.X&0xffff,
4503 (unsigned int)pos.Y&0xffff);
4505 return m_savedir + "/sectors/" + cc;
4507 snprintf(cc, 9, "%.3x/%.3x",
4508 (unsigned int)pos.X&0xfff,
4509 (unsigned int)pos.Y&0xfff);
4511 return m_savedir + "/sectors2/" + cc;
4517 v2s16 ServerMap::getSectorPos(std::string dirname)
4521 size_t spos = dirname.rfind('/') + 1;
4522 assert(spos != std::string::npos);
4523 if(dirname.size() - spos == 8)
4526 r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
4528 else if(dirname.size() - spos == 3)
4531 r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
4532 // Sign-extend the 12 bit values up to 16 bits...
4533 if(x&0x800) x|=0xF000;
4534 if(y&0x800) y|=0xF000;
4541 v2s16 pos((s16)x, (s16)y);
4545 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
4547 v2s16 p2d = getSectorPos(sectordir);
4549 if(blockfile.size() != 4){
4550 throw InvalidFilenameException("Invalid block filename");
4553 int r = sscanf(blockfile.c_str(), "%4x", &y);
4555 throw InvalidFilenameException("Invalid block filename");
4556 return v3s16(p2d.X, y, p2d.Y);
4559 std::string ServerMap::getBlockFilename(v3s16 p)
4562 snprintf(cc, 5, "%.4x", (unsigned int)p.Y&0xffff);
4566 void ServerMap::save(bool only_changed)
4568 DSTACK(__FUNCTION_NAME);
4569 if(m_map_saving_enabled == false)
4571 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
4575 if(only_changed == false)
4576 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
4579 if(only_changed == false || m_map_metadata_changed)
4584 u32 sector_meta_count = 0;
4585 u32 block_count = 0;
4586 u32 block_count_all = 0; // Number of blocks in memory
4588 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
4589 for(; i.atEnd() == false; i++)
4591 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
4592 assert(sector->getId() == MAPSECTOR_SERVER);
4594 if(sector->differs_from_disk || only_changed == false)
4596 saveSectorMeta(sector);
4597 sector_meta_count++;
4599 core::list<MapBlock*> blocks;
4600 sector->getBlocks(blocks);
4601 core::list<MapBlock*>::Iterator j;
4602 for(j=blocks.begin(); j!=blocks.end(); j++)
4604 MapBlock *block = *j;
4608 if(block->getModified() >= MOD_STATE_WRITE_NEEDED
4609 || only_changed == false)
4614 /*dstream<<"ServerMap: Written block ("
4615 <<block->getPos().X<<","
4616 <<block->getPos().Y<<","
4617 <<block->getPos().Z<<")"
4624 Only print if something happened or saved whole map
4626 if(only_changed == false || sector_meta_count != 0
4627 || block_count != 0)
4629 dstream<<DTIME<<"ServerMap: Written: "
4630 <<sector_meta_count<<" sector metadata files, "
4631 <<block_count<<" block files"
4632 <<", "<<block_count_all<<" blocks in memory."
4637 void ServerMap::saveMapMeta()
4639 DSTACK(__FUNCTION_NAME);
4641 dstream<<"INFO: ServerMap::saveMapMeta(): "
4645 createDirs(m_savedir);
4647 std::string fullpath = m_savedir + "/map_meta.txt";
4648 std::ofstream os(fullpath.c_str(), std::ios_base::binary);
4649 if(os.good() == false)
4651 dstream<<"ERROR: ServerMap::saveMapMeta(): "
4652 <<"could not open"<<fullpath<<std::endl;
4653 throw FileNotGoodException("Cannot open chunk metadata");
4657 params.setU64("seed", m_seed);
4659 params.writeLines(os);
4661 os<<"[end_of_params]\n";
4663 m_map_metadata_changed = false;
4666 void ServerMap::loadMapMeta()
4668 DSTACK(__FUNCTION_NAME);
4670 dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
4673 std::string fullpath = m_savedir + "/map_meta.txt";
4674 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4675 if(is.good() == false)
4677 dstream<<"ERROR: ServerMap::loadMapMeta(): "
4678 <<"could not open"<<fullpath<<std::endl;
4679 throw FileNotGoodException("Cannot open map metadata");
4687 throw SerializationError
4688 ("ServerMap::loadMapMeta(): [end_of_params] not found");
4690 std::getline(is, line);
4691 std::string trimmedline = trim(line);
4692 if(trimmedline == "[end_of_params]")
4694 params.parseConfigLine(line);
4697 m_seed = params.getU64("seed");
4699 dstream<<"INFO: ServerMap::loadMapMeta(): "
4704 void ServerMap::saveSectorMeta(ServerMapSector *sector)
4706 DSTACK(__FUNCTION_NAME);
4707 // Format used for writing
4708 u8 version = SER_FMT_VER_HIGHEST;
4710 v2s16 pos = sector->getPos();
4711 std::string dir = getSectorDir(pos);
4714 std::string fullpath = dir + "/meta";
4715 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4716 if(o.good() == false)
4717 throw FileNotGoodException("Cannot open sector metafile");
4719 sector->serialize(o, version);
4721 sector->differs_from_disk = false;
4724 MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
4726 DSTACK(__FUNCTION_NAME);
4728 v2s16 p2d = getSectorPos(sectordir);
4730 ServerMapSector *sector = NULL;
4732 std::string fullpath = sectordir + "/meta";
4733 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4734 if(is.good() == false)
4736 // If the directory exists anyway, it probably is in some old
4737 // format. Just go ahead and create the sector.
4738 if(fs::PathExists(sectordir))
4740 dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
4741 <<fullpath<<" doesn't exist but directory does."
4742 <<" Continuing with a sector with no metadata."
4744 sector = new ServerMapSector(this, p2d);
4745 m_sectors.insert(p2d, sector);
4749 throw FileNotGoodException("Cannot open sector metafile");
4754 sector = ServerMapSector::deSerialize
4755 (is, this, p2d, m_sectors);
4757 saveSectorMeta(sector);
4760 sector->differs_from_disk = false;
4765 bool ServerMap::loadSectorMeta(v2s16 p2d)
4767 DSTACK(__FUNCTION_NAME);
4769 MapSector *sector = NULL;
4771 // The directory layout we're going to load from.
4772 // 1 - original sectors/xxxxzzzz/
4773 // 2 - new sectors2/xxx/zzz/
4774 // If we load from anything but the latest structure, we will
4775 // immediately save to the new one, and remove the old.
4777 std::string sectordir1 = getSectorDir(p2d, 1);
4778 std::string sectordir;
4779 if(fs::PathExists(sectordir1))
4781 sectordir = sectordir1;
4786 sectordir = getSectorDir(p2d, 2);
4790 sector = loadSectorMeta(sectordir, loadlayout != 2);
4792 catch(InvalidFilenameException &e)
4796 catch(FileNotGoodException &e)
4800 catch(std::exception &e)
4809 bool ServerMap::loadSectorFull(v2s16 p2d)
4811 DSTACK(__FUNCTION_NAME);
4813 MapSector *sector = NULL;
4815 // The directory layout we're going to load from.
4816 // 1 - original sectors/xxxxzzzz/
4817 // 2 - new sectors2/xxx/zzz/
4818 // If we load from anything but the latest structure, we will
4819 // immediately save to the new one, and remove the old.
4821 std::string sectordir1 = getSectorDir(p2d, 1);
4822 std::string sectordir;
4823 if(fs::PathExists(sectordir1))
4825 sectordir = sectordir1;
4830 sectordir = getSectorDir(p2d, 2);
4834 sector = loadSectorMeta(sectordir, loadlayout != 2);
4836 catch(InvalidFilenameException &e)
4840 catch(FileNotGoodException &e)
4844 catch(std::exception &e)
4852 std::vector<fs::DirListNode> list2 = fs::GetDirListing
4854 std::vector<fs::DirListNode>::iterator i2;
4855 for(i2=list2.begin(); i2!=list2.end(); i2++)
4861 loadBlock(sectordir, i2->name, sector, loadlayout != 2);
4863 catch(InvalidFilenameException &e)
4865 // This catches unknown crap in directory
4871 dstream<<"Sector converted to new layout - deleting "<<
4872 sectordir1<<std::endl;
4873 fs::RecursiveDelete(sectordir1);
4880 void ServerMap::saveBlock(MapBlock *block)
4882 DSTACK(__FUNCTION_NAME);
4884 Dummy blocks are not written
4886 if(block->isDummy())
4888 /*v3s16 p = block->getPos();
4889 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
4890 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
4894 // Format used for writing
4895 u8 version = SER_FMT_VER_HIGHEST;
4897 v3s16 p3d = block->getPos();
4899 v2s16 p2d(p3d.X, p3d.Z);
4900 std::string sectordir = getSectorDir(p2d);
4902 createDirs(sectordir);
4904 std::string fullpath = sectordir+"/"+getBlockFilename(p3d);
4905 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
4906 if(o.good() == false)
4907 throw FileNotGoodException("Cannot open block data");
4910 [0] u8 serialization version
4913 o.write((char*)&version, 1);
4916 block->serialize(o, version);
4918 // Write extra data stored on disk
4919 block->serializeDiskExtra(o, version);
4921 // We just wrote it to the disk so clear modified flag
4922 block->resetModified();
4925 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
4927 DSTACK(__FUNCTION_NAME);
4929 std::string fullpath = sectordir+"/"+blockfile;
4932 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
4933 if(is.good() == false)
4934 throw FileNotGoodException("Cannot open block file");
4936 v3s16 p3d = getBlockPos(sectordir, blockfile);
4937 v2s16 p2d(p3d.X, p3d.Z);
4939 assert(sector->getPos() == p2d);
4941 u8 version = SER_FMT_VER_INVALID;
4942 is.read((char*)&version, 1);
4945 throw SerializationError("ServerMap::loadBlock(): Failed"
4946 " to read MapBlock version");
4948 /*u32 block_size = MapBlock::serializedLength(version);
4949 SharedBuffer<u8> data(block_size);
4950 is.read((char*)*data, block_size);*/
4952 // This will always return a sector because we're the server
4953 //MapSector *sector = emergeSector(p2d);
4955 MapBlock *block = NULL;
4956 bool created_new = false;
4958 block = sector->getBlockNoCreate(p3d.Y);
4960 catch(InvalidPositionException &e)
4962 block = sector->createBlankBlockNoInsert(p3d.Y);
4967 block->deSerialize(is, version);
4969 // Read extra data stored on disk
4970 block->deSerializeDiskExtra(is, version);
4972 // If it's a new block, insert it to the map
4974 sector->insertBlock(block);
4977 Save blocks loaded in old format in new format
4980 if(version < SER_FMT_VER_HIGHEST || save_after_load)
4985 // We just loaded it from the disk, so it's up-to-date.
4986 block->resetModified();
4989 catch(SerializationError &e)
4991 dstream<<"WARNING: Invalid block data on disk "
4992 <<"fullpath="<<fullpath
4993 <<" (SerializationError). "
4994 <<"what()="<<e.what()
4996 //" Ignoring. A new one will be generated.
4999 // TODO: Backup file; name is in fullpath.
5003 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
5005 DSTACK(__FUNCTION_NAME);
5007 v2s16 p2d(blockpos.X, blockpos.Z);
5009 // The directory layout we're going to load from.
5010 // 1 - original sectors/xxxxzzzz/
5011 // 2 - new sectors2/xxx/zzz/
5012 // If we load from anything but the latest structure, we will
5013 // immediately save to the new one, and remove the old.
5015 std::string sectordir1 = getSectorDir(p2d, 1);
5016 std::string sectordir;
5017 if(fs::PathExists(sectordir1))
5019 sectordir = sectordir1;
5024 sectordir = getSectorDir(p2d, 2);
5028 Make sure sector is loaded
5030 MapSector *sector = getSectorNoGenerateNoEx(p2d);
5034 sector = loadSectorMeta(sectordir, loadlayout != 2);
5036 catch(InvalidFilenameException &e)
5040 catch(FileNotGoodException &e)
5044 catch(std::exception &e)
5051 Make sure file exists
5054 std::string blockfilename = getBlockFilename(blockpos);
5055 if(fs::PathExists(sectordir+"/"+blockfilename) == false)
5061 loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
5062 return getBlockNoCreateNoEx(blockpos);
5065 void ServerMap::PrintInfo(std::ostream &out)
5076 ClientMap::ClientMap(
5078 MapDrawControl &control,
5079 scene::ISceneNode* parent,
5080 scene::ISceneManager* mgr,
5084 scene::ISceneNode(parent, mgr, id),
5087 m_camera_position(0,0,0),
5088 m_camera_direction(0,0,1)
5090 m_camera_mutex.Init();
5091 assert(m_camera_mutex.IsInitialized());
5093 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
5094 BS*1000000,BS*1000000,BS*1000000);
5097 ClientMap::~ClientMap()
5099 /*JMutexAutoLock lock(mesh_mutex);
5108 MapSector * ClientMap::emergeSector(v2s16 p2d)
5110 DSTACK(__FUNCTION_NAME);
5111 // Check that it doesn't exist already
5113 return getSectorNoGenerate(p2d);
5115 catch(InvalidPositionException &e)
5120 ClientMapSector *sector = new ClientMapSector(this, p2d);
5123 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5124 m_sectors.insert(p2d, sector);
5130 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
5132 DSTACK(__FUNCTION_NAME);
5133 ClientMapSector *sector = NULL;
5135 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5137 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
5141 sector = (ClientMapSector*)n->getValue();
5142 assert(sector->getId() == MAPSECTOR_CLIENT);
5146 sector = new ClientMapSector(this, p2d);
5148 //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
5149 m_sectors.insert(p2d, sector);
5153 sector->deSerialize(is);
5156 void ClientMap::OnRegisterSceneNode()
5160 SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
5161 SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
5164 ISceneNode::OnRegisterSceneNode();
5167 void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
5169 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
5170 DSTACK(__FUNCTION_NAME);
5172 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
5175 This is called two times per frame, reset on the non-transparent one
5177 if(pass == scene::ESNRP_SOLID)
5179 m_last_drawn_sectors.clear();
5183 Get time for measuring timeout.
5185 Measuring time is very useful for long delays when the
5186 machine is swapping a lot.
5188 int time1 = time(0);
5190 //u32 daynight_ratio = m_client->getDayNightRatio();
5192 m_camera_mutex.Lock();
5193 v3f camera_position = m_camera_position;
5194 v3f camera_direction = m_camera_direction;
5195 m_camera_mutex.Unlock();
5198 Get all blocks and draw all visible ones
5201 v3s16 cam_pos_nodes(
5202 camera_position.X / BS,
5203 camera_position.Y / BS,
5204 camera_position.Z / BS);
5206 v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
5208 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
5209 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
5211 // Take a fair amount as we will be dropping more out later
5213 p_nodes_min.X / MAP_BLOCKSIZE - 1,
5214 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
5215 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
5217 p_nodes_max.X / MAP_BLOCKSIZE,
5218 p_nodes_max.Y / MAP_BLOCKSIZE,
5219 p_nodes_max.Z / MAP_BLOCKSIZE);
5221 u32 vertex_count = 0;
5223 // For limiting number of mesh updates per frame
5224 u32 mesh_update_count = 0;
5226 u32 blocks_would_have_drawn = 0;
5227 u32 blocks_drawn = 0;
5229 int timecheck_counter = 0;
5230 core::map<v2s16, MapSector*>::Iterator si;
5231 si = m_sectors.getIterator();
5232 for(; si.atEnd() == false; si++)
5235 timecheck_counter++;
5236 if(timecheck_counter > 50)
5238 timecheck_counter = 0;
5239 int time2 = time(0);
5240 if(time2 > time1 + 4)
5242 dstream<<"ClientMap::renderMap(): "
5243 "Rendering takes ages, returning."
5250 MapSector *sector = si.getNode()->getValue();
5251 v2s16 sp = sector->getPos();
5253 if(m_control.range_all == false)
5255 if(sp.X < p_blocks_min.X
5256 || sp.X > p_blocks_max.X
5257 || sp.Y < p_blocks_min.Z
5258 || sp.Y > p_blocks_max.Z)
5262 core::list< MapBlock * > sectorblocks;
5263 sector->getBlocks(sectorblocks);
5269 u32 sector_blocks_drawn = 0;
5271 core::list< MapBlock * >::Iterator i;
5272 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5274 MapBlock *block = *i;
5277 Compare block position to camera position, skip
5278 if not seen on display
5281 float range = 100000 * BS;
5282 if(m_control.range_all == false)
5283 range = m_control.wanted_range * BS;
5286 if(isBlockInSight(block->getPos(), camera_position,
5287 camera_direction, range, &d) == false)
5292 // This is ugly (spherical distance limit?)
5293 /*if(m_control.range_all == false &&
5294 d - 0.5*BS*MAP_BLOCKSIZE > range)
5299 Update expired mesh (used for day/night change)
5301 It doesn't work exactly like it should now with the
5302 tasked mesh update but whatever.
5305 bool mesh_expired = false;
5308 JMutexAutoLock lock(block->mesh_mutex);
5310 mesh_expired = block->getMeshExpired();
5312 // Mesh has not been expired and there is no mesh:
5313 // block has no content
5314 if(block->mesh == NULL && mesh_expired == false)
5318 f32 faraway = BS*50;
5319 //f32 faraway = m_control.wanted_range * BS;
5322 This has to be done with the mesh_mutex unlocked
5324 // Pretty random but this should work somewhat nicely
5325 if(mesh_expired && (
5326 (mesh_update_count < 3
5327 && (d < faraway || mesh_update_count < 2)
5330 (m_control.range_all && mesh_update_count < 20)
5333 /*if(mesh_expired && mesh_update_count < 6
5334 && (d < faraway || mesh_update_count < 3))*/
5336 mesh_update_count++;
5338 // Mesh has been expired: generate new mesh
5339 //block->updateMesh(daynight_ratio);
5340 m_client->addUpdateMeshTask(block->getPos());
5342 mesh_expired = false;
5347 Draw the faces of the block
5350 JMutexAutoLock lock(block->mesh_mutex);
5352 scene::SMesh *mesh = block->mesh;
5357 blocks_would_have_drawn++;
5358 if(blocks_drawn >= m_control.wanted_max_blocks
5359 && m_control.range_all == false
5360 && d > m_control.wanted_min_range * BS)
5364 sector_blocks_drawn++;
5366 u32 c = mesh->getMeshBufferCount();
5368 for(u32 i=0; i<c; i++)
5370 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
5371 const video::SMaterial& material = buf->getMaterial();
5372 video::IMaterialRenderer* rnd =
5373 driver->getMaterialRenderer(material.MaterialType);
5374 bool transparent = (rnd && rnd->isTransparent());
5375 // Render transparent on transparent pass and likewise.
5376 if(transparent == is_transparent_pass)
5379 This *shouldn't* hurt too much because Irrlicht
5380 doesn't change opengl textures if the old
5381 material is set again.
5383 driver->setMaterial(buf->getMaterial());
5384 driver->drawMeshBuffer(buf);
5385 vertex_count += buf->getVertexCount();
5389 } // foreach sectorblocks
5391 if(sector_blocks_drawn != 0)
5393 m_last_drawn_sectors[sp] = true;
5397 m_control.blocks_drawn = blocks_drawn;
5398 m_control.blocks_would_have_drawn = blocks_would_have_drawn;
5400 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
5401 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
5404 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
5405 core::map<v3s16, MapBlock*> *affected_blocks)
5407 bool changed = false;
5409 Add it to all blocks touching it
5412 v3s16(0,0,0), // this
5413 v3s16(0,0,1), // back
5414 v3s16(0,1,0), // top
5415 v3s16(1,0,0), // right
5416 v3s16(0,0,-1), // front
5417 v3s16(0,-1,0), // bottom
5418 v3s16(-1,0,0), // left
5420 for(u16 i=0; i<7; i++)
5422 v3s16 p2 = p + dirs[i];
5423 // Block position of neighbor (or requested) node
5424 v3s16 blockpos = getNodeBlockPos(p2);
5425 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5426 if(blockref == NULL)
5428 // Relative position of requested node
5429 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5430 if(blockref->setTempMod(relpos, mod))
5435 if(changed && affected_blocks!=NULL)
5437 for(u16 i=0; i<7; i++)
5439 v3s16 p2 = p + dirs[i];
5440 // Block position of neighbor (or requested) node
5441 v3s16 blockpos = getNodeBlockPos(p2);
5442 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5443 if(blockref == NULL)
5445 affected_blocks->insert(blockpos, blockref);
5451 bool ClientMap::clearTempMod(v3s16 p,
5452 core::map<v3s16, MapBlock*> *affected_blocks)
5454 bool changed = false;
5456 v3s16(0,0,0), // this
5457 v3s16(0,0,1), // back
5458 v3s16(0,1,0), // top
5459 v3s16(1,0,0), // right
5460 v3s16(0,0,-1), // front
5461 v3s16(0,-1,0), // bottom
5462 v3s16(-1,0,0), // left
5464 for(u16 i=0; i<7; i++)
5466 v3s16 p2 = p + dirs[i];
5467 // Block position of neighbor (or requested) node
5468 v3s16 blockpos = getNodeBlockPos(p2);
5469 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5470 if(blockref == NULL)
5472 // Relative position of requested node
5473 v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
5474 if(blockref->clearTempMod(relpos))
5479 if(changed && affected_blocks!=NULL)
5481 for(u16 i=0; i<7; i++)
5483 v3s16 p2 = p + dirs[i];
5484 // Block position of neighbor (or requested) node
5485 v3s16 blockpos = getNodeBlockPos(p2);
5486 MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
5487 if(blockref == NULL)
5489 affected_blocks->insert(blockpos, blockref);
5495 void ClientMap::expireMeshes(bool only_daynight_diffed)
5497 TimeTaker timer("expireMeshes()");
5499 core::map<v2s16, MapSector*>::Iterator si;
5500 si = m_sectors.getIterator();
5501 for(; si.atEnd() == false; si++)
5503 MapSector *sector = si.getNode()->getValue();
5505 core::list< MapBlock * > sectorblocks;
5506 sector->getBlocks(sectorblocks);
5508 core::list< MapBlock * >::Iterator i;
5509 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
5511 MapBlock *block = *i;
5513 if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
5519 JMutexAutoLock lock(block->mesh_mutex);
5520 if(block->mesh != NULL)
5522 /*block->mesh->drop();
5523 block->mesh = NULL;*/
5524 block->setMeshExpired(true);
5531 void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
5533 assert(mapType() == MAPTYPE_CLIENT);
5536 v3s16 p = blockpos + v3s16(0,0,0);
5537 MapBlock *b = getBlockNoCreate(p);
5538 b->updateMesh(daynight_ratio);
5539 //b->setMeshExpired(true);
5541 catch(InvalidPositionException &e){}
5544 v3s16 p = blockpos + v3s16(-1,0,0);
5545 MapBlock *b = getBlockNoCreate(p);
5546 b->updateMesh(daynight_ratio);
5547 //b->setMeshExpired(true);
5549 catch(InvalidPositionException &e){}
5551 v3s16 p = blockpos + v3s16(0,-1,0);
5552 MapBlock *b = getBlockNoCreate(p);
5553 b->updateMesh(daynight_ratio);
5554 //b->setMeshExpired(true);
5556 catch(InvalidPositionException &e){}
5558 v3s16 p = blockpos + v3s16(0,0,-1);
5559 MapBlock *b = getBlockNoCreate(p);
5560 b->updateMesh(daynight_ratio);
5561 //b->setMeshExpired(true);
5563 catch(InvalidPositionException &e){}
5568 Update mesh of block in which the node is, and if the node is at the
5569 leading edge, update the appropriate leading blocks too.
5571 void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
5579 v3s16 blockposes[4];
5580 for(u32 i=0; i<4; i++)
5582 v3s16 np = nodepos + dirs[i];
5583 blockposes[i] = getNodeBlockPos(np);
5584 // Don't update mesh of block if it has been done already
5585 bool already_updated = false;
5586 for(u32 j=0; j<i; j++)
5588 if(blockposes[j] == blockposes[i])
5590 already_updated = true;
5597 MapBlock *b = getBlockNoCreate(blockposes[i]);
5598 b->updateMesh(daynight_ratio);
5603 void ClientMap::PrintInfo(std::ostream &out)
5614 MapVoxelManipulator::MapVoxelManipulator(Map *map)
5619 MapVoxelManipulator::~MapVoxelManipulator()
5621 /*dstream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
5625 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5627 TimeTaker timer1("emerge", &emerge_time);
5629 // Units of these are MapBlocks
5630 v3s16 p_min = getNodeBlockPos(a.MinEdge);
5631 v3s16 p_max = getNodeBlockPos(a.MaxEdge);
5633 VoxelArea block_area_nodes
5634 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5636 addArea(block_area_nodes);
5638 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5639 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5640 for(s32 x=p_min.X; x<=p_max.X; x++)
5643 core::map<v3s16, bool>::Node *n;
5644 n = m_loaded_blocks.find(p);
5648 bool block_data_inexistent = false;
5651 TimeTaker timer1("emerge load", &emerge_load_time);
5653 /*dstream<<"Loading block (caller_id="<<caller_id<<")"
5654 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5657 dstream<<std::endl;*/
5659 MapBlock *block = m_map->getBlockNoCreate(p);
5660 if(block->isDummy())
5661 block_data_inexistent = true;
5663 block->copyTo(*this);
5665 catch(InvalidPositionException &e)
5667 block_data_inexistent = true;
5670 if(block_data_inexistent)
5672 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5673 // Fill with VOXELFLAG_INEXISTENT
5674 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5675 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5677 s32 i = m_area.index(a.MinEdge.X,y,z);
5678 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5682 m_loaded_blocks.insert(p, !block_data_inexistent);
5685 //dstream<<"emerge done"<<std::endl;
5689 SUGG: Add an option to only update eg. water and air nodes.
5690 This will make it interfere less with important stuff if
5693 void MapVoxelManipulator::blitBack
5694 (core::map<v3s16, MapBlock*> & modified_blocks)
5696 if(m_area.getExtent() == v3s16(0,0,0))
5699 //TimeTaker timer1("blitBack");
5701 /*dstream<<"blitBack(): m_loaded_blocks.size()="
5702 <<m_loaded_blocks.size()<<std::endl;*/
5705 Initialize block cache
5707 v3s16 blockpos_last;
5708 MapBlock *block = NULL;
5709 bool block_checked_in_modified = false;
5711 for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++)
5712 for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++)
5713 for(s32 x=m_area.MinEdge.X; x<=m_area.MaxEdge.X; x++)
5717 u8 f = m_flags[m_area.index(p)];
5718 if(f & (VOXELFLAG_NOT_LOADED|VOXELFLAG_INEXISTENT))
5721 MapNode &n = m_data[m_area.index(p)];
5723 v3s16 blockpos = getNodeBlockPos(p);
5728 if(block == NULL || blockpos != blockpos_last){
5729 block = m_map->getBlockNoCreate(blockpos);
5730 blockpos_last = blockpos;
5731 block_checked_in_modified = false;
5734 // Calculate relative position in block
5735 v3s16 relpos = p - blockpos * MAP_BLOCKSIZE;
5737 // Don't continue if nothing has changed here
5738 if(block->getNode(relpos) == n)
5741 //m_map->setNode(m_area.MinEdge + p, n);
5742 block->setNode(relpos, n);
5745 Make sure block is in modified_blocks
5747 if(block_checked_in_modified == false)
5749 modified_blocks[blockpos] = block;
5750 block_checked_in_modified = true;
5753 catch(InvalidPositionException &e)
5759 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
5760 MapVoxelManipulator(map),
5761 m_create_area(false)
5765 ManualMapVoxelManipulator::~ManualMapVoxelManipulator()
5769 void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
5771 // Just create the area so that it can be pointed to
5772 VoxelManipulator::emerge(a, caller_id);
5775 void ManualMapVoxelManipulator::initialEmerge(
5776 v3s16 blockpos_min, v3s16 blockpos_max)
5778 TimeTaker timer1("initialEmerge", &emerge_time);
5780 // Units of these are MapBlocks
5781 v3s16 p_min = blockpos_min;
5782 v3s16 p_max = blockpos_max;
5784 VoxelArea block_area_nodes
5785 (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5787 u32 size_MB = block_area_nodes.getVolume()*4/1000000;
5790 dstream<<"initialEmerge: area: ";
5791 block_area_nodes.print(dstream);
5792 dstream<<" ("<<size_MB<<"MB)";
5796 addArea(block_area_nodes);
5798 for(s32 z=p_min.Z; z<=p_max.Z; z++)
5799 for(s32 y=p_min.Y; y<=p_max.Y; y++)
5800 for(s32 x=p_min.X; x<=p_max.X; x++)
5803 core::map<v3s16, bool>::Node *n;
5804 n = m_loaded_blocks.find(p);
5808 bool block_data_inexistent = false;
5811 TimeTaker timer1("emerge load", &emerge_load_time);
5813 MapBlock *block = m_map->getBlockNoCreate(p);
5814 if(block->isDummy())
5815 block_data_inexistent = true;
5817 block->copyTo(*this);
5819 catch(InvalidPositionException &e)
5821 block_data_inexistent = true;
5824 if(block_data_inexistent)
5827 Mark area inexistent
5829 VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
5830 // Fill with VOXELFLAG_INEXISTENT
5831 for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
5832 for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
5834 s32 i = m_area.index(a.MinEdge.X,y,z);
5835 memset(&m_flags[i], VOXELFLAG_INEXISTENT, MAP_BLOCKSIZE);
5839 m_loaded_blocks.insert(p, !block_data_inexistent);
5843 void ManualMapVoxelManipulator::blitBackAll(
5844 core::map<v3s16, MapBlock*> * modified_blocks)
5846 if(m_area.getExtent() == v3s16(0,0,0))
5850 Copy data of all blocks
5852 for(core::map<v3s16, bool>::Iterator
5853 i = m_loaded_blocks.getIterator();
5854 i.atEnd() == false; i++)
5856 bool existed = i.getNode()->getValue();
5857 if(existed == false)
5859 v3s16 p = i.getNode()->getKey();
5860 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
5863 dstream<<"WARNING: "<<__FUNCTION_NAME
5864 <<": got NULL block "
5865 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
5870 block->copyFrom(*this);
5873 modified_blocks->insert(p, block);