X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmap.cpp;h=a13c028fa22f895d396eb536869db6ea261399c3;hb=4846846a2d28e06f347cb46ffe6be2a42337eb32;hp=cb0967c35143059294ca7b42c2ccdfd7f406f07d;hpb=aed9e809a19bffdf911f75dd9d718efb8decd2c1;p=minetest.git diff --git a/src/map.cpp b/src/map.cpp index cb0967c35..a13c028fa 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -18,24 +18,37 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "map.h" +#include "mapsector.h" +#include "mapblock.h" #include "main.h" -#include "jmutexautolock.h" #include "client.h" #include "filesys.h" #include "utility.h" #include "voxel.h" #include "porting.h" -#include "mineral.h" -#include "noise.h" -#include "serverobject.h" +#include "mapgen.h" +#include "nodemetadata.h" #include "content_mapnode.h" +#ifndef SERVER +#include +#endif +#include "settings.h" -extern "C" { - #include "sqlite3.h" -} /* SQLite format specification: - Initially only replaces sectors/ and sectors2/ + + If map.sqlite does not exist in the save dir + or the block was not found in the database + the map will try to load from sectors folder. + In either case, map.sqlite will be created + and all future saves will save there. + + Structure of map.sqlite: + Tables: + blocks + (PK) INT pos + BLOB data */ /* @@ -121,42 +134,23 @@ MapSector * Map::getSectorNoGenerate(v2s16 p) return sector; } -MapBlock * Map::getBlockNoCreate(v3s16 p3d) -{ - v2s16 p2d(p3d.X, p3d.Z); - MapSector * sector = getSectorNoGenerate(p2d); - - MapBlock *block = sector->getBlockNoCreate(p3d.Y); - - return block; -} - MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d) { - try - { - v2s16 p2d(p3d.X, p3d.Z); - MapSector * sector = getSectorNoGenerate(p2d); - MapBlock *block = sector->getBlockNoCreate(p3d.Y); - return block; - } - catch(InvalidPositionException &e) - { + v2s16 p2d(p3d.X, p3d.Z); + MapSector * sector = getSectorNoGenerateNoEx(p2d); + if(sector == NULL) return NULL; - } + MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y); + return block; } -/*MapBlock * Map::getBlockCreate(v3s16 p3d) -{ - v2s16 p2d(p3d.X, p3d.Z); - MapSector * sector = getSectorCreate(p2d); - assert(sector); - MapBlock *block = sector->getBlockNoCreate(p3d.Y); - if(block) - return block; - block = sector->createBlankBlock(p3d.Y); +MapBlock * Map::getBlockNoCreate(v3s16 p3d) +{ + MapBlock *block = getBlockNoCreateNoEx(p3d); + if(block == NULL) + throw InvalidPositionException(); return block; -}*/ +} bool Map::isNodeUnderground(v3s16 p) { @@ -171,6 +165,45 @@ bool Map::isNodeUnderground(v3s16 p) } } +bool Map::isValidPosition(v3s16 p) +{ + v3s16 blockpos = getNodeBlockPos(p); + MapBlock *block = getBlockNoCreate(blockpos); + return (block != NULL); +} + +// Returns a CONTENT_IGNORE node if not found +MapNode Map::getNodeNoEx(v3s16 p) +{ + v3s16 blockpos = getNodeBlockPos(p); + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block == NULL) + return MapNode(CONTENT_IGNORE); + v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; + return block->getNodeNoCheck(relpos); +} + +// throws InvalidPositionException if not found +MapNode Map::getNode(v3s16 p) +{ + v3s16 blockpos = getNodeBlockPos(p); + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block == NULL) + throw InvalidPositionException(); + v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; + return block->getNodeNoCheck(relpos); +} + +// throws InvalidPositionException if not found +void Map::setNode(v3s16 p, MapNode & n) +{ + v3s16 blockpos = getNodeBlockPos(p); + MapBlock *block = getBlockNoCreate(blockpos); + v3s16 relpos = p - blockpos*MAP_BLOCKSIZE; + block->setNodeNoCheck(relpos, n); +} + + /* Goes recursively through the neighbours of the node. @@ -611,9 +644,9 @@ s16 Map::propagateSunlight(v3s16 start, else { /*// Turn mud into grass - if(n.d == CONTENT_MUD) + if(n.getContent() == CONTENT_MUD) { - n.d = CONTENT_GRASS; + n.setContent(CONTENT_GRASS); block->setNode(relpos, n); modified_blocks.insert(blockpos, block); }*/ @@ -736,6 +769,25 @@ void Map::updateLighting(enum LightBank bank, } } + + /* + Enable this to disable proper lighting for speeding up map + generation for testing or whatever + */ +#if 0 + //if(g_settings->get("")) + { + core::map::Iterator i; + i = blocks_to_update.getIterator(); + for(; i.atEnd() == false; i++) + { + MapBlock *block = i.getNode()->getValue(); + v3s16 p = block->getPos(); + block->setLightingExpired(false); + } + return; + } +#endif #if 0 { @@ -843,7 +895,7 @@ void Map::updateLighting(core::map & a_blocks, /* */ void Map::addNodeAndUpdate(v3s16 p, MapNode n, - core::map &modified_blocks) + core::map &modified_blocks, std::string &player_name) { /*PrintInfo(m_dout); m_dout<clone(); + meta->setOwner(player_name); setNodeMetadata(p, meta); } @@ -977,7 +1030,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, TODO: This could be optimized by mass-unlighting instead of looping */ - if(node_under_sunlight && !content_features(n.d).sunlight_propagates) + if(node_under_sunlight && !content_features(n).sunlight_propagates) { s16 y = p.Y - 1; for(;; y--){ @@ -1048,7 +1101,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, v3s16 p2 = p + dirs[i]; MapNode n2 = getNode(p2); - if(content_liquid(n2.d)) + if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR) { m_transforming_liquid.push_back(p2); } @@ -1073,7 +1126,7 @@ void Map::removeNodeAndUpdate(v3s16 p, v3s16 toppos = p + v3s16(0,1,0); // Node will be replaced with this - u8 replace_material = CONTENT_AIR; + content_t replace_material = CONTENT_AIR; /* If there is a node at top and it doesn't have sunlight, @@ -1120,7 +1173,7 @@ void Map::removeNodeAndUpdate(v3s16 p, */ MapNode n; - n.d = replace_material; + n.setContent(replace_material); setNode(p, n); for(s32 i=0; i<2; i++) @@ -1202,17 +1255,19 @@ void Map::removeNodeAndUpdate(v3s16 p, } /* - Add neighboring liquid nodes to transform queue. + Add neighboring liquid nodes and this node to transform queue. + (it's vital for the node itself to get updated last.) */ - v3s16 dirs[6] = { + v3s16 dirs[7] = { v3s16(0,0,1), // back v3s16(0,1,0), // top v3s16(1,0,0), // right v3s16(0,0,-1), // front v3s16(0,-1,0), // bottom v3s16(-1,0,0), // left + v3s16(0,0,0), // self }; - for(u16 i=0; i<6; i++) + for(u16 i=0; i<7; i++) { try { @@ -1220,7 +1275,7 @@ void Map::removeNodeAndUpdate(v3s16 p, v3s16 p2 = p + dirs[i]; MapNode n2 = getNode(p2); - if(content_liquid(n2.d)) + if(content_liquid(n2.getContent()) || n2.getContent() == CONTENT_AIR) { m_transforming_liquid.push_back(p2); } @@ -1241,7 +1296,8 @@ bool Map::addNodeWithEvent(v3s16 p, MapNode n) bool succeeded = true; try{ core::map modified_blocks; - addNodeAndUpdate(p, n, modified_blocks); + std::string st = std::string(""); + addNodeAndUpdate(p, n, modified_blocks, st); // Copy modified_blocks to event for(core::map::Iterator @@ -1348,59 +1404,104 @@ bool Map::dayNightDiffed(v3s16 blockpos) /* Updates usage timers */ -void Map::timerUpdate(float dtime) +void Map::timerUpdate(float dtime, float unload_timeout, + core::list *unloaded_blocks) { - //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out + bool save_before_unloading = (mapType() == MAPTYPE_SERVER); + + core::list sector_deletion_queue; + u32 deleted_blocks_count = 0; + u32 saved_blocks_count = 0; core::map::Iterator si; + beginSave(); si = m_sectors.getIterator(); for(; si.atEnd() == false; si++) { MapSector *sector = si.getNode()->getValue(); + bool all_blocks_deleted = true; + core::list blocks; sector->getBlocks(blocks); + for(core::list::Iterator i = blocks.begin(); i != blocks.end(); i++) { - (*i)->incrementUsageTimer(dtime); + MapBlock *block = (*i); + + block->incrementUsageTimer(dtime); + + if(block->getUsageTimer() > unload_timeout) + { + v3s16 p = block->getPos(); + + // Save if modified + if(block->getModified() != MOD_STATE_CLEAN + && save_before_unloading) + { + saveBlock(block); + saved_blocks_count++; + } + + // Delete from memory + sector->deleteBlock(block); + + if(unloaded_blocks) + unloaded_blocks->push_back(p); + + deleted_blocks_count++; + } + else + { + all_blocks_deleted = false; + } + } + + if(all_blocks_deleted) + { + sector_deletion_queue.push_back(si.getNode()->getKey()); } } + endSave(); + + // Finally delete the empty sectors + deleteSectors(sector_deletion_queue); + + if(deleted_blocks_count != 0) + { + PrintInfo(dstream); // ServerMap/ClientMap: + dstream<<"Unloaded "< &list, bool only_blocks) +void Map::deleteSectors(core::list &list) { core::list::Iterator j; for(j=list.begin(); j!=list.end(); j++) { MapSector *sector = m_sectors[*j]; - if(only_blocks) - { - sector->deleteBlocks(); - } - else - { - /* - If sector is in sector cache, remove it from there - */ - if(m_sector_cache == sector) - { - m_sector_cache = NULL; - } - /* - Remove from map and delete - */ - m_sectors.remove(*j); - delete sector; - } + // If sector is in sector cache, remove it from there + if(m_sector_cache == sector) + m_sector_cache = NULL; + // Remove from map and delete + m_sectors.remove(*j); + delete sector; } } -u32 Map::unloadUnusedData(float timeout, bool only_blocks, +#if 0 +void Map::unloadUnusedData(float timeout, core::list *deleted_blocks) { core::list sector_deletion_queue; + u32 deleted_blocks_count = 0; + u32 saved_blocks_count = 0; core::map::Iterator si = m_sectors.getIterator(); for(; si.atEnd() == false; si++) @@ -1415,15 +1516,18 @@ u32 Map::unloadUnusedData(float timeout, bool only_blocks, i != blocks.end(); i++) { MapBlock *block = (*i); - + if(block->getUsageTimer() > timeout) { // Save if modified if(block->getModified() != MOD_STATE_CLEAN) + { saveBlock(block); - // Unload - sector->removeBlock(block); - delete block; + saved_blocks_count++; + } + // Delete from memory + sector->deleteBlock(block); + deleted_blocks_count++; } else { @@ -1437,37 +1541,16 @@ u32 Map::unloadUnusedData(float timeout, bool only_blocks, } } -#if 0 - core::map::Iterator i = m_sectors.getIterator(); - for(; i.atEnd() == false; i++) - { - MapSector *sector = i.getNode()->getValue(); - /* - Delete sector from memory if it hasn't been used in a long time - */ - if(sector->usage_timer > timeout) - { - sector_deletion_queue.push_back(i.getNode()->getKey()); + deleteSectors(sector_deletion_queue); - if(deleted_blocks != NULL) - { - // Collect positions of blocks of sector - MapSector *sector = i.getNode()->getValue(); - core::list blocks; - sector->getBlocks(blocks); - for(core::list::Iterator i = blocks.begin(); - i != blocks.end(); i++) - { - deleted_blocks->push_back((*i)->getPos()); - } - } - } - } -#endif + dstream<<"Map: Unloaded "< & modified_blocks) { DSTACK(__FUNCTION_NAME); @@ -1487,272 +1581,237 @@ void Map::transformLiquids(core::map & modified_blocks) /*if(initial_size != 0) dstream<<"transformLiquids(): initial_size="< must_reflow; + + // List of MapBlocks that will require a lighting update (due to lava) + core::map lighting_modified_blocks; + while(m_transforming_liquid.size() != 0) { + // This should be done here so that it is done when continue is used + if(loopcount >= initial_size * 3) + break; + loopcount++; + /* Get a queued transforming liquid node */ v3s16 p0 = m_transforming_liquid.pop_front(); - MapNode n0 = getNode(p0); - - // Don't deal with non-liquids - if(content_liquid(n0.d) == false) - continue; - - bool is_source = !content_flowing_liquid(n0.d); - - u8 liquid_level = 8; - if(is_source == false) - liquid_level = n0.param2 & 0x0f; - - // Turn possible source into non-source - u8 nonsource_c = make_liquid_flowing(n0.d); + MapNode n0 = getNodeNoEx(p0); /* - If not source, check that some node flows into this one - and what is the level of liquid in this one - */ - if(is_source == false) - { - s8 new_liquid_level_max = -1; - - v3s16 dirs_from[5] = { - v3s16(0,1,0), // top - v3s16(0,0,1), // back - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(-1,0,0), // left - }; - for(u16 i=0; i<5; i++) - { - try - { - - bool from_top = (i==0); - - v3s16 p2 = p0 + dirs_from[i]; - MapNode n2 = getNode(p2); - - if(content_liquid(n2.d)) - { - u8 n2_nonsource_c = make_liquid_flowing(n2.d); - // Check that the liquids are the same type - if(n2_nonsource_c != nonsource_c) - { - dstream<<"WARNING: Not handling: different liquids" - " collide"<= 7 - WATER_DROP_BOOST) - new_liquid_level = 7; - else - new_liquid_level = n2_liquid_level + WATER_DROP_BOOST; - } - else if(n2_liquid_level > 0) - { - new_liquid_level = n2_liquid_level - 1; + /* + Collect information about the environment + */ + const v3s16 *dirs = g_6dirs; + NodeNeighbor sources[6]; // surrounding sources + int num_sources = 0; + NodeNeighbor flows[6]; // surrounding flowing liquid nodes + int num_flows = 0; + NodeNeighbor airs[6]; // surrounding air + int num_airs = 0; + NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid + int num_neutrals = 0; + bool flowing_down = false; + for (u16 i = 0; i < 6; i++) { + NeighborType nt = NEIGHBOR_SAME_LEVEL; + switch (i) { + case 1: + nt = NEIGHBOR_UPPER; + break; + case 4: + nt = NEIGHBOR_LOWER; + break; + } + v3s16 npos = p0 + dirs[i]; + NodeNeighbor nb = {getNodeNoEx(npos), nt, npos}; + switch (content_features(nb.n.getContent()).liquid_type) { + case LIQUID_NONE: + if (nb.n.getContent() == CONTENT_AIR) { + airs[num_airs++] = nb; + // if the current node is a water source the neighbor + // should be enqueded for transformation regardless of whether the + // current node changes or not. + if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE) + m_transforming_liquid.push_back(npos); + // if the current node happens to be a flowing node, it will start to flow down here. + if (nb.t == NEIGHBOR_LOWER) { + flowing_down = true; + } + } else { + neutrals[num_neutrals++] = nb; } - - if(new_liquid_level > new_liquid_level_max) - new_liquid_level_max = new_liquid_level; - } - - }catch(InvalidPositionException &e) - { - } - } //for - - /* - If liquid level should be something else, update it and - add all the neighboring water nodes to the transform queue. - */ - if(new_liquid_level_max != liquid_level) - { - if(new_liquid_level_max == -1) - { - // Remove water alltoghether - n0.d = CONTENT_AIR; - n0.param2 = 0; - setNode(p0, n0); - } - else - { - n0.param2 = new_liquid_level_max; - setNode(p0, n0); - } - - // Block has been modified - { - v3s16 blockpos = getNodeBlockPos(p0); - MapBlock *block = getBlockNoCreateNoEx(blockpos); - if(block != NULL) - modified_blocks.insert(blockpos, block); - } - - /* - Add neighboring non-source liquid nodes to transform queue. - */ - v3s16 dirs[6] = { - v3s16(0,0,1), // back - v3s16(0,1,0), // top - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(0,-1,0), // bottom - v3s16(-1,0,0), // left - }; - for(u16 i=0; i<6; i++) - { - try - { - - v3s16 p2 = p0 + dirs[i]; - - MapNode n2 = getNode(p2); - if(content_flowing_liquid(n2.d)) - { - m_transforming_liquid.push_back(p2); + break; + case LIQUID_SOURCE: + // if this node is not (yet) of a liquid type, choose the first liquid type we encounter + if (liquid_kind == CONTENT_AIR) + liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing; + if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) { + neutrals[num_neutrals++] = nb; + } else { + sources[num_sources++] = nb; } - - }catch(InvalidPositionException &e) - { + break; + case LIQUID_FLOWING: + // if this node is not (yet) of a liquid type, choose the first liquid type we encounter + if (liquid_kind == CONTENT_AIR) + liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing; + if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) { + neutrals[num_neutrals++] = nb; + } else { + flows[num_flows++] = nb; + if (nb.t == NEIGHBOR_LOWER) + flowing_down = true; } - } + break; } } - // Get a new one from queue if the node has turned into non-water - if(content_liquid(n0.d) == false) - continue; - /* - Flow water from this node - */ - v3s16 dirs_to[5] = { - v3s16(0,-1,0), // bottom - v3s16(0,0,1), // back - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(-1,0,0), // left - }; - for(u16 i=0; i<5; i++) - { - try - { - - bool to_bottom = (i == 0); - - // If liquid is at lowest possible height, it's not going - // anywhere except down - if(liquid_level == 0 && to_bottom == false) - continue; - - u8 liquid_next_level = 0; - // If going to bottom - if(to_bottom) - { - //liquid_next_level = 7; - if(liquid_level >= 7 - WATER_DROP_BOOST) - liquid_next_level = 7; - else - liquid_next_level = liquid_level + WATER_DROP_BOOST; + decide on the type (and possibly level) of the current node + */ + content_t new_node_content; + s8 new_node_level = -1; + s8 max_node_level = -1; + if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) { + // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid) + // or the flowing alternative of the first of the surrounding sources (if it's air), so + // it's perfectly safe to use liquid_kind here to determine the new node content. + new_node_content = content_features(liquid_kind).liquid_alternative_source; + } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) { + // liquid_kind is set properly, see above + new_node_content = liquid_kind; + max_node_level = new_node_level = LIQUID_LEVEL_MAX; + } else { + // no surrounding sources, so get the maximum level that can flow into this node + for (u16 i = 0; i < num_flows; i++) { + u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK); + switch (flows[i].t) { + case NEIGHBOR_UPPER: + if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) { + max_node_level = LIQUID_LEVEL_MAX; + if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX) + max_node_level = nb_liquid_level + WATER_DROP_BOOST; + } else if (nb_liquid_level > max_node_level) + max_node_level = nb_liquid_level; + break; + case NEIGHBOR_LOWER: + break; + case NEIGHBOR_SAME_LEVEL: + if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK && + nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) { + max_node_level = nb_liquid_level - 1; + } + break; + } } + + u8 viscosity = content_features(liquid_kind).liquid_viscosity; + if (viscosity > 1 && max_node_level != liquid_level) { + // amount to gain, limited by viscosity + // must be at least 1 in absolute value + s8 level_inc = max_node_level - liquid_level; + if (level_inc < -viscosity || level_inc > viscosity) + new_node_level = liquid_level + level_inc/viscosity; + else if (level_inc < 0) + new_node_level = liquid_level - 1; + else if (level_inc > 0) + new_node_level = liquid_level + 1; + if (new_node_level != max_node_level) + must_reflow.push_back(p0); + } else + new_node_level = max_node_level; + + if (new_node_level >= 0) + new_node_content = liquid_kind; else - liquid_next_level = liquid_level - 1; + new_node_content = CONTENT_AIR; - bool n2_changed = false; - bool flowed = false; + } - v3s16 p2 = p0 + dirs_to[i]; + /* + check if anything has changed. if not, just continue with the next node. + */ + if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING || + ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level && + ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK) + == flowing_down))) + continue; - MapNode n2 = getNode(p2); - //dstream<<"[1] n2.param="<<(int)n2.param<getPos()] = block; + } - if(to_bottom) - { - flowed = true; - } - - if(n2_is_source) - { - // Just flow into the source, nothing changes. - // n2_changed is not set because destination didn't change - flowed = true; - } - else - { - if(liquid_next_level > liquid_level) - { - n2.param2 = liquid_next_level; - setNode(p2, n2); - - n2_changed = true; - flowed = true; - } - } - } - else if(n2.d == CONTENT_AIR) - { - n2.d = nonsource_c; - n2.param2 = liquid_next_level; - setNode(p2, n2); - - n2_changed = true; - flowed = true; - } - - //dstream<<"[2] n2.param="<<(int)n2.param<= 100000) - if(loopcount >= initial_size * 1) - break; - } - //dstream<<"Map::transformLiquids(): loopcount="<getU64("fixed_map_seed"); + } /* Experimental and debug stuff @@ -1939,7 +2008,6 @@ ServerMap::~ServerMap() { if(m_map_saving_enabled) { - //save(false); // Save only changed parts save(true); dstream<=y_nodes_min; y--) - { - MapNode &n = vmanip.m_data[i]; - if(content_walkable(n.d)) - break; - - vmanip.m_area.add_y(em, i, -1); - } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min; -} - -s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d) -{ - v3s16 em = vmanip.m_area.getExtent(); - s16 y_nodes_max = vmanip.m_area.MaxEdge.Y; - s16 y_nodes_min = vmanip.m_area.MinEdge.Y; - u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); - s16 y; - for(y=y_nodes_max; y>=y_nodes_min; y--) - { - MapNode &n = vmanip.m_data[i]; - if(content_walkable(n.d) - && n.d != CONTENT_TREE - && n.d != CONTENT_LEAVES) - break; - - vmanip.m_area.add_y(em, i, -1); - } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min; -} - -void make_tree(VoxelManipulator &vmanip, v3s16 p0) -{ - MapNode treenode(CONTENT_TREE); - MapNode leavesnode(CONTENT_LEAVES); - - s16 trunk_h = myrand_range(3, 6); - v3s16 p1 = p0; - for(s16 ii=0; ii leaves_d(new u8[leaves_a.getVolume()]); - Buffer leaves_d(leaves_a.getVolume()); - for(s32 i=0; i stone_d(stone_a.getVolume()); - for(s32 i=0; i 0.0) - { - u32 vi = stone_a.index(v3s16(x,y,z)); - stone_d[vi] = 1; - } - } - - /*// Add stone randomly - for(u32 iii=0; iii<7; iii++) - { - s16 d = 1; - - v3s16 p( - myrand_range(stone_a.MinEdge.X, stone_a.MaxEdge.X-d), - myrand_range(stone_a.MinEdge.Y, stone_a.MaxEdge.Y-d), - myrand_range(stone_a.MinEdge.Z, stone_a.MaxEdge.Z-d) - ); - - for(s16 z=0; z<=d; z++) - for(s16 y=0; y<=d; y++) - for(s16 x=0; x<=d; x++) - { - stone_d[stone_a.index(p+v3s16(x,y,z))] = 1; - } - }*/ - - // Blit stone to vmanip - for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++) - for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++) - for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++) - { - v3s16 p(x,y,z); - p += p0; - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].d != CONTENT_AIR - && vmanip.m_data[vi].d != CONTENT_IGNORE) - continue; - u32 i = stone_a.index(x,y,z); - if(stone_d[i] == 1) - vmanip.m_data[vi] = stonenode; - } -} -#endif - -#if 0 -void make_largestone(VoxelManipulator &vmanip, v3s16 p0) -{ - MapNode stonenode(CONTENT_STONE); - - s16 size = myrand_range(8, 16); - - VoxelArea stone_a(v3s16(-size/2,0,-size/2), v3s16(size/2,size,size/2)); - Buffer stone_d(stone_a.getVolume()); - for(s32 i=0; i 1.0) - { - u32 vi = stone_a.index(v3s16(x,y,z)); - stone_d[vi] = 1; - } - } - - /*// Add stone randomly - for(u32 iii=0; iii<7; iii++) - { - s16 d = 1; - - v3s16 p( - myrand_range(stone_a.MinEdge.X, stone_a.MaxEdge.X-d), - myrand_range(stone_a.MinEdge.Y, stone_a.MaxEdge.Y-d), - myrand_range(stone_a.MinEdge.Z, stone_a.MaxEdge.Z-d) - ); - - for(s16 z=0; z<=d; z++) - for(s16 y=0; y<=d; y++) - for(s16 x=0; x<=d; x++) - { - stone_d[stone_a.index(p+v3s16(x,y,z))] = 1; - } - }*/ - - // Blit stone to vmanip - for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++) - for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++) - for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++) - { - v3s16 p(x,y,z); - p += p0; - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - /*if(vmanip.m_data[vi].d != CONTENT_AIR - && vmanip.m_data[vi].d != CONTENT_IGNORE) - continue;*/ - u32 i = stone_a.index(x,y,z); - if(stone_d[i] == 1) - vmanip.m_data[vi] = stonenode; - } -} -#endif - -/* - Dungeon making routines -*/ - -#define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1 -#define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2 -#define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\ - VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE) - -void make_room1(VoxelManipulator &vmanip, v3s16 roomsize, v3s16 roomplace) -{ - // Make +-X walls - for(s16 z=0; z= 3) - make_stairs = random.next()%2 ? 1 : -1; - for(u32 i=0; i= partlength) - { - partcount = 0; - - dir = random_turn(random, dir); - - partlength = random.range(1,length); - - make_stairs = 0; - if(random.next()%2 == 0 && partlength >= 3) - make_stairs = random.next()%2 ? 1 : -1; - } - } - result_place = p0; - result_dir = dir; -} - -class RoomWalker -{ -public: - - RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random): - vmanip(vmanip_), - m_pos(pos), - m_random(random) - { - randomizeDir(); - } - - void randomizeDir() - { - m_dir = rand_ortho_dir(m_random); - } - - void setPos(v3s16 pos) - { - m_pos = pos; - } - - void setDir(v3s16 dir) - { - m_dir = dir; - } - - bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir) - { - for(u32 i=0; i<100; i++) - { - v3s16 p = m_pos + m_dir; - v3s16 p1 = p + v3s16(0,1,0); - if(vmanip.m_area.contains(p) == false - || vmanip.m_area.contains(p1) == false - || i % 4 == 0) - { - randomizeDir(); - continue; - } - if(vmanip.getNodeNoExNoEmerge(p).d - == CONTENT_COBBLE - && vmanip.getNodeNoExNoEmerge(p1).d - == CONTENT_COBBLE) - { - // Found wall, this is a good place! - result_place = p; - result_dir = m_dir; - // Randomize next direction - randomizeDir(); - return true; - } - /* - Determine where to move next - */ - // Jump one up if the actual space is there - if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).d - == CONTENT_COBBLE - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d - == CONTENT_AIR - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,2,0)).d - == CONTENT_AIR) - p += v3s16(0,1,0); - // Jump one down if the actual space is there - if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d - == CONTENT_COBBLE - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).d - == CONTENT_AIR - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,-1,0)).d - == CONTENT_AIR) - p += v3s16(0,-1,0); - // Check if walking is now possible - if(vmanip.getNodeNoExNoEmerge(p).d - != CONTENT_AIR - || vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d - != CONTENT_AIR) - { - // Cannot continue walking here - randomizeDir(); - continue; - } - // Move there - m_pos = p; - } - return false; - } - - bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace, - v3s16 &result_doordir, v3s16 &result_roomplace) - { - for(s16 trycount=0; trycount<30; trycount++) - { - v3s16 doorplace; - v3s16 doordir; - bool r = findPlaceForDoor(doorplace, doordir); - if(r == false) - continue; - v3s16 roomplace; - // X east, Z north, Y up - if(doordir == v3s16(1,0,0)) // X+ - roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2+m_random.range(-roomsize.Z/2+1,roomsize.Z/2-1)); - if(doordir == v3s16(-1,0,0)) // X- - roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2+m_random.range(-roomsize.Z/2+1,roomsize.Z/2-1)); - if(doordir == v3s16(0,0,1)) // Z+ - roomplace = doorplace + v3s16(-roomsize.X/2+m_random.range(-roomsize.X/2+1,roomsize.X/2-1),-1,0); - if(doordir == v3s16(0,0,-1)) // Z- - roomplace = doorplace + v3s16(-roomsize.X/2+m_random.range(-roomsize.X/2+1,roomsize.X/2-1),-1,-roomsize.Z+1); - - // Check fit - bool fits = true; - for(s16 z=1; z CAVE_NOISE_THRESHOLD; -} - -/* - Ground density noise shall be interpreted by using this. - - TODO: No perlin noises here, they should be outsourced - and buffered -*/ -bool val_is_ground(double ground_noise1_val, v3s16 p, u64 seed) -{ - //return ((double)p.Y < ground_noise1_val); - - double f = 0.8 + noise2d_perlin( - 0.5+(float)p.X/250, 0.5+(float)p.Z/250, - seed+920381, 3, 0.5); - if(f < 0.01) - f = 0.01; - else if(f >= 1.0) - f *= 2.0; - double h = WATER_LEVEL + 10 * noise2d_perlin( - 0.5+(float)p.X/250, 0.5+(float)p.Z/250, - seed+84174, 4, 0.5); - return ((double)p.Y - h < ground_noise1_val * f); -} - -/* - Queries whether a position is ground or not. -*/ -bool is_ground(u64 seed, v3s16 p) -{ - double val1 = noise3d_param(get_ground_noise1_params(seed), p.X,p.Y,p.Z); - return val_is_ground(val1, p, seed); -} - -// Amount of trees per area in nodes -double tree_amount_2d(u64 seed, v2s16 p) -{ - /*double noise = noise2d_perlin( - 0.5+(float)p.X/250, 0.5+(float)p.Y/250, - seed+2, 5, 0.66);*/ - double noise = noise2d_perlin( - 0.5+(float)p.X/125, 0.5+(float)p.Y/125, - seed+2, 4, 0.66); - double zeroval = -0.35; - if(noise < zeroval) - return 0; - else - return 0.04 * (noise-zeroval) / (1.0-zeroval); -} - -#if 0 -double randomstone_amount_2d(u64 seed, v2s16 p) -{ - double noise = noise2d_perlin( - 0.5+(float)p.X/250, 0.5+(float)p.Y/250, - seed+3829434, 5, 0.66); - double zeroval = 0.1; - if(noise < zeroval) - return 0; - else - return 0.01 * (noise-zeroval) / (1.0-zeroval); -} -#endif - -double largestone_amount_2d(u64 seed, v2s16 p) -{ - double noise = noise2d_perlin( - 0.5+(float)p.X/250, 0.5+(float)p.Y/250, - seed+14143242, 5, 0.66); - double zeroval = 0.3; - if(noise < zeroval) - return 0; - else - return 0.005 * (noise-zeroval) / (1.0-zeroval); -} - -/* - Incrementally find ground level from 3d noise -*/ -s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) -{ - // Start a bit fuzzy to make averaging lower precision values - // more useful - s16 level = myrand_range(-precision/2, precision/2); - s16 dec[] = {31000, 100, 20, 4, 1, 0}; - s16 i; - for(i = 1; dec[i] != 0 && precision <= dec[i]; i++) - { - // First find non-ground by going upwards - // Don't stop in caves. - { - s16 max = level+dec[i-1]*2; - v3s16 p(p2d.X, level, p2d.Y); - for(; p.Y < max; p.Y += dec[i]) - { - if(!is_ground(seed, p)) - { - level = p.Y; - break; - } - } - } - // Then find ground by going downwards from there. - // Go in caves, too, when precision is 1. - { - s16 min = level-dec[i-1]*2; - v3s16 p(p2d.X, level, p2d.Y); - for(; p.Y>min; p.Y-=dec[i]) - { - bool ground = is_ground(seed, p); - /*if(dec[i] == 1 && is_cave(seed, p)) - ground = false;*/ - if(ground) - { - level = p.Y; - break; - } - } - } - } - - // This is more like the actual ground level - level += dec[i-1]/2; - - return level; -} - -double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p=4); - -double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p) -{ - v2s16 node_min = sectorpos*MAP_BLOCKSIZE; - v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); - double a = 0; - a += find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p); - a += find_ground_level_from_noise(seed, - v2s16(node_min.X, node_max.Y), p); - a += find_ground_level_from_noise(seed, - v2s16(node_max.X, node_max.Y), p); - a += find_ground_level_from_noise(seed, - v2s16(node_max.X, node_min.Y), p); - a += find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p); - a /= 5; - return a; -} - -double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p=4); - -double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p) -{ - v2s16 node_min = sectorpos*MAP_BLOCKSIZE; - v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); - double a = -31000; - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_max.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_max.X, node_max.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p)); - return a; -} - -double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p=4); - -double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p) -{ - v2s16 node_min = sectorpos*MAP_BLOCKSIZE; - v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); - double a = 31000; - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_max.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_max.X, node_max.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p)); - return a; -} - -#if 0 -#define AVERAGE_MUD_AMOUNT 4 - -double base_rock_level_2d(u64 seed, v2s16 p) -{ - // The base ground level - double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT - + 20. * noise2d_perlin( - 0.5+(float)p.X/500., 0.5+(float)p.Y/500., - (seed>>32)+654879876, 6, 0.6); - - /*// A bit hillier one - double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin( - 0.5+(float)p.X/250., 0.5+(float)p.Y/250., - (seed>>27)+90340, 6, 0.69); - if(base2 > base) - base = base2;*/ -#if 1 - // Higher ground level - double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin( - 0.5+(float)p.X/250., 0.5+(float)p.Y/250., - seed+85039, 5, 0.69); - //higher = 30; // For debugging - - // Limit higher to at least base - if(higher < base) - higher = base; - - // Steepness factor of cliffs - double b = 1.0 + 1.0 * noise2d_perlin( - 0.5+(float)p.X/250., 0.5+(float)p.Y/250., - seed-932, 7, 0.7); - b = rangelim(b, 0.0, 1000.0); - b = pow(b, 5); - b *= 7; - b = rangelim(b, 3.0, 1000.0); - //dstream<<"b="<blockpos; - - /*dstream<<"makeBlock(): ("<vmanip; - v3s16 blockpos_min = blockpos - v3s16(1,1,1); - v3s16 blockpos_max = blockpos + v3s16(1,1,1); - // Area of center block - v3s16 node_min = blockpos*MAP_BLOCKSIZE; - v3s16 node_max = (blockpos+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1); - // Full allocated area - v3s16 full_node_min = (blockpos-1)*MAP_BLOCKSIZE; - v3s16 full_node_max = (blockpos+2)*MAP_BLOCKSIZE-v3s16(1,1,1); - // Area of a block - double block_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE; - - v2s16 p2d_center(node_min.X+MAP_BLOCKSIZE/2, node_min.Z+MAP_BLOCKSIZE/2); - - /* - Get average ground level from noise - */ - - s16 approx_groundlevel = (s16)get_sector_average_ground_level( - data->seed, v2s16(blockpos.X, blockpos.Z)); - //dstream<<"approx_groundlevel="<seed, v2s16(blockpos.X, blockpos.Z)); - // Minimum amount of ground above the top of the central block - s16 minimum_ground_depth = minimum_groundlevel - node_max.Y; - - s16 maximum_groundlevel = (s16)get_sector_maximum_ground_level( - data->seed, v2s16(blockpos.X, blockpos.Z), 1); - // Maximum amount of ground above the bottom of the central block - s16 maximum_ground_depth = maximum_groundlevel - node_min.Y; - - /* - Special case for high air or water: Just fill with air and water. - */ - if(maximum_ground_depth < -20) - { - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y)); - for(s16 y=node_min.Y; y<=node_max.Y; y++) - { - // Only modify places that have no content - if(vmanip.m_data[i].d == CONTENT_IGNORE) - { - if(y <= WATER_LEVEL) - vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE); - else - vmanip.m_data[i] = MapNode(CONTENT_AIR); - } - - data->vmanip.m_area.add_y(em, i, 1); - } - } - } - - // We're done - return; - } - - /* - If block is deep underground, this is set to true and ground - density noise is not generated, for speed optimization. - */ - bool all_is_ground_except_caves = (minimum_ground_depth > 16); - - /* - Create a block-specific seed - */ - u32 blockseed = (data->seed%0x100000000) + full_node_min.Z*38134234 - + full_node_min.Y*42123 + full_node_min.X*23; - - /* - Make some 3D noise - */ - - //NoiseBuffer noisebuf1; - //NoiseBuffer noisebuf2; - NoiseBuffer noisebuf_cave; - NoiseBuffer noisebuf_ground; - NoiseBuffer noisebuf_ground_crumbleness; - NoiseBuffer noisebuf_ground_wetness; - { - v3f minpos_f(node_min.X, node_min.Y, node_min.Z); - v3f maxpos_f(node_max.X, node_max.Y, node_max.Z); - - //TimeTaker timer("noisebuf.create"); - - /* - Cave noise - */ - - noisebuf_cave.create(get_cave_noise1_params(data->seed), - minpos_f.X, minpos_f.Y, minpos_f.Z, - maxpos_f.X, maxpos_f.Y, maxpos_f.Z, - 4, 3, 4); - - noisebuf_cave.multiply(get_cave_noise2_params(data->seed)); - - /* - Ground noise - */ - - // Sample length - v3f sl = v3f(4.0, 4.0, 4.0); - - /* - Density noise - */ - if(all_is_ground_except_caves == false) - //noisebuf_ground.create(data->seed+983240, 6, 0.60, false, - noisebuf_ground.create(get_ground_noise1_params(data->seed), - minpos_f.X, minpos_f.Y, minpos_f.Z, - maxpos_f.X, maxpos_f.Y, maxpos_f.Z, - sl.X, sl.Y, sl.Z); - - /* - Ground property noise - */ - sl = v3f(2.5, 2.5, 2.5); - noisebuf_ground_crumbleness.create( - get_ground_crumbleness_params(data->seed), - minpos_f.X, minpos_f.Y, minpos_f.Z, - maxpos_f.X, maxpos_f.Y+5, maxpos_f.Z, - sl.X, sl.Y, sl.Z); - noisebuf_ground_wetness.create( - get_ground_wetness_params(data->seed), - minpos_f.X, minpos_f.Y, minpos_f.Z, - maxpos_f.X, maxpos_f.Y+5, maxpos_f.Z, - sl.X, sl.Y, sl.Z); - } - - /* - Make base ground level - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y)); - for(s16 y=node_min.Y; y<=node_max.Y; y++) - { - // Only modify places that have no content - if(vmanip.m_data[i].d == CONTENT_IGNORE) - { - // First priority: make air and water. - // This avoids caves inside water. - if(all_is_ground_except_caves == false - && val_is_ground(noisebuf_ground.get(x,y,z), - v3s16(x,y,z), data->seed) == false) - { - if(y <= WATER_LEVEL) - vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE); - else - vmanip.m_data[i] = MapNode(CONTENT_AIR); - } - else if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD) - vmanip.m_data[i] = MapNode(CONTENT_AIR); - else - vmanip.m_data[i] = MapNode(CONTENT_STONE); - } - - data->vmanip.m_area.add_y(em, i, 1); - } - } - } - - /* - Add minerals - */ - - { - PseudoRandom mineralrandom(blockseed); - - /* - Add meseblocks - */ - for(s16 i=0; i 0.0) - new_content = MapNode(CONTENT_STONE, MINERAL_IRON); - /*if(noisebuf_ground_wetness.get(x,y,z) > 0.0) - vmanip.m_data[i] = MapNode(CONTENT_MUD); - else - vmanip.m_data[i] = MapNode(CONTENT_SAND);*/ - } - /*else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.1) - { - }*/ - - if(new_content.d != CONTENT_IGNORE) - { - for(u16 i=0; i<27; i++) - { - v3s16 p = v3s16(x,y,z) + g_27dirs[i]; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].d == base_content) - { - if(mineralrandom.next()%sparseness == 0) - vmanip.m_data[vi] = new_content; - } - } - } - } - } - /* - Add coal - */ - //for(s16 i=0; i < MYMAX(0, 50 - abs(node_min.Y+8 - (-30))); i++) - //for(s16 i=0; i<50; i++) - u16 coal_amount = 30; - u16 coal_rareness = 60 / coal_amount; - if(coal_rareness == 0) - coal_rareness = 1; - if(mineralrandom.next()%coal_rareness == 0) - { - u16 a = mineralrandom.next() % 16; - u16 amount = coal_amount * a*a*a / 1000; - for(s16 i=0; i=node_min.Y; y--) - { - if(vmanip.m_data[i].d == CONTENT_STONE) - { - if(noisebuf_ground_crumbleness.get(x,y,z) > 1.3) - { - if(noisebuf_ground_wetness.get(x,y,z) > 0.0) - vmanip.m_data[i] = MapNode(CONTENT_MUD); - else - vmanip.m_data[i] = MapNode(CONTENT_SAND); - } - else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.7) - { - if(noisebuf_ground_wetness.get(x,y,z) < -0.6) - vmanip.m_data[i] = MapNode(CONTENT_GRAVEL); - } - } - - data->vmanip.m_area.add_y(em, i, -1); - } - } - } - - /* - Add dungeons - */ - - //if(node_min.Y < approx_groundlevel) - //if(myrand() % 3 == 0) - //if(myrand() % 3 == 0 && node_min.Y < approx_groundlevel) - //if(myrand() % 100 == 0 && node_min.Y < approx_groundlevel) - //float dungeon_rarity = g_settings.getFloat("dungeon_rarity"); - float dungeon_rarity = 0.02; - if(((noise3d(blockpos.X,blockpos.Y,blockpos.Z,data->seed)+1.0)/2.0) - < dungeon_rarity - && node_min.Y < approx_groundlevel) - { - // Dungeon generator doesn't modify places which have this set - data->vmanip.clearFlag(VMANIP_FLAG_DUNGEON_INSIDE - | VMANIP_FLAG_DUNGEON_PRESERVE); - - // Set all air and water to be untouchable to make dungeons open - // to caves and open air - for(s16 x=full_node_min.X; x<=full_node_max.X; x++) - for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y)); - for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--) - { - if(vmanip.m_data[i].d == CONTENT_AIR) - vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; - else if(vmanip.m_data[i].d == CONTENT_WATERSOURCE) - vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; - data->vmanip.m_area.add_y(em, i, -1); - } - } - } - - PseudoRandom random(blockseed+2); - - // Add it - make_dungeon1(data->vmanip, random); - - // Convert some cobble to mossy cobble - for(s16 x=full_node_min.X; x<=full_node_max.X; x++) - for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y)); - for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--) - { - // (noisebuf not used because it doesn't contain the - // full area) - double wetness = noise3d_param( - get_ground_wetness_params(data->seed), x,y,z); - double d = noise3d_perlin((float)x/2.5, - (float)y/2.5,(float)z/2.5, - blockseed, 2, 1.4); - if(vmanip.m_data[i].d == CONTENT_COBBLE) - { - if(d < wetness/3.0) - { - vmanip.m_data[i].d = CONTENT_MOSSYCOBBLE; - } - } - /*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE) - { - if(wetness > 1.2) - vmanip.m_data[i].d = CONTENT_MUD; - }*/ - data->vmanip.m_area.add_y(em, i, -1); - } - } - } - } - - /* - Add top and bottom side of water to transforming_liquid queue - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - bool water_found = false; - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y)); - for(s16 y=node_max.Y; y>=node_min.Y; y--) - { - if(water_found == false) - { - if(vmanip.m_data[i].d == CONTENT_WATERSOURCE) - { - v3s16 p = v3s16(p2d.X, y, p2d.Y); - data->transforming_liquid.push_back(p); - water_found = true; - } - } - else - { - // This can be done because water_found can only - // turn to true and end up here after going through - // a single block. - if(vmanip.m_data[i+1].d != CONTENT_WATERSOURCE) - { - v3s16 p = v3s16(p2d.X, y+1, p2d.Y); - data->transforming_liquid.push_back(p); - water_found = false; - } - } - - data->vmanip.m_area.add_y(em, i, -1); - } - } - } - - /* - If close to ground level - */ - - //if(abs(approx_ground_depth) < 30) - if(minimum_ground_depth < 5 && maximum_ground_depth > -5) - { - /* - Add grass and mud - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - bool possibly_have_sand = get_have_sand(data->seed, p2d); - bool have_sand = false; - u32 current_depth = 0; - bool air_detected = false; - bool water_detected = false; - // Use fast index incrementing - s16 start_y = node_max.Y+2; - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y)); - for(s16 y=start_y; y>=node_min.Y-3; y--) - { - if(vmanip.m_data[i].d == CONTENT_WATERSOURCE) - water_detected = true; - if(vmanip.m_data[i].d == CONTENT_AIR) - air_detected = true; - - if((vmanip.m_data[i].d == CONTENT_STONE - || vmanip.m_data[i].d == CONTENT_GRASS - || vmanip.m_data[i].d == CONTENT_MUD - || vmanip.m_data[i].d == CONTENT_SAND - || vmanip.m_data[i].d == CONTENT_GRAVEL - ) && (air_detected || water_detected)) - { - if(current_depth == 0 && y <= WATER_LEVEL+2 - && possibly_have_sand) - have_sand = true; - - if(current_depth < 4) - { - if(have_sand) - { - vmanip.m_data[i] = MapNode(CONTENT_SAND); - } - #if 1 - else if(current_depth==0 && !water_detected - && y >= WATER_LEVEL && air_detected) - vmanip.m_data[i] = MapNode(CONTENT_GRASS); - #endif - else - vmanip.m_data[i] = MapNode(CONTENT_MUD); - } - else - { - if(vmanip.m_data[i].d == CONTENT_MUD - || vmanip.m_data[i].d == CONTENT_GRASS) - vmanip.m_data[i] = MapNode(CONTENT_STONE); - } - - current_depth++; - - if(current_depth >= 8) - break; - } - else if(current_depth != 0) - break; - - data->vmanip.m_area.add_y(em, i, -1); - } - } - } - - /* - Add trees - */ - - // Amount of trees - u32 tree_count = block_area_nodes * tree_amount_2d(data->seed, p2d_center); - PseudoRandom treerandom(blockseed); - // Put trees in random places on part of division - for(u32 i=0; ivmanip, v2s16(x,z)); - s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4); - // Don't make a tree under water level - if(y < WATER_LEVEL) - continue; - // Make sure tree fits (only trees whose starting point is - // at this block are added) - if(y < node_min.Y || y > node_max.Y) - continue; - /* - Find exact ground level - */ - v3s16 p(x,y+6,z); - bool found = false; - for(; p.Y >= y-6; p.Y--) - { - u32 i = data->vmanip.m_area.index(p); - MapNode *n = &data->vmanip.m_data[i]; - if(n->d != CONTENT_AIR && n->d != CONTENT_IGNORE) - { - found = true; - break; - } - } - // If not found, handle next one - if(found == false) - continue; - /* - Trees grow only on mud and grass - */ - { - u32 i = data->vmanip.m_area.index(p); - MapNode *n = &data->vmanip.m_data[i]; - if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS) - continue; - } - // Tree will be placed one higher - p.Y++; - // Make a tree - make_tree(data->vmanip, p); - } - -#if 0 - /* - Add some kind of random stones - */ - - u32 random_stone_count = block_area_nodes * - randomstone_amount_2d(data->seed, p2d_center); - // Put in random places on part of division - for(u32 i=0; iseed, v2s16(x,z), 1); - // Don't add under water level - /*if(y < WATER_LEVEL) - continue;*/ - // Don't add if doesn't belong to this block - if(y < node_min.Y || y > node_max.Y) - continue; - v3s16 p(x,y,z); - // Filter placement - /*{ - u32 i = data->vmanip.m_area.index(v3s16(p)); - MapNode *n = &data->vmanip.m_data[i]; - if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS) - continue; - }*/ - // Will be placed one higher - p.Y++; - // Add it - make_randomstone(data->vmanip, p); - } -#endif - -#if 0 - /* - Add larger stones - */ - - u32 large_stone_count = block_area_nodes * - largestone_amount_2d(data->seed, p2d_center); - //u32 large_stone_count = 1; - // Put in random places on part of division - for(u32 i=0; iseed, v2s16(x,z), 1); - // Don't add under water level - /*if(y < WATER_LEVEL) - continue;*/ - // Don't add if doesn't belong to this block - if(y < node_min.Y || y > node_max.Y) - continue; - v3s16 p(x,y,z); - // Filter placement - /*{ - u32 i = data->vmanip.m_area.index(v3s16(p)); - MapNode *n = &data->vmanip.m_data[i]; - if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS) - continue; - }*/ - // Will be placed one lower - p.Y--; - // Add it - make_largestone(data->vmanip, p); - } -#endif - } - -} - -void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos) -{ - /*dstream<<"initBlockMake(): ("<getBool("enable_mapgen_debug_info"); + if(enable_mapgen_debug_info) + dstream<<"initBlockMake(): ("<no_op = true; + return; + } + data->no_op = false; data->seed = m_seed; data->blockpos = blockpos; @@ -3881,20 +2081,28 @@ void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos) for(s16 y=-1; y<=1; y++) { - MapBlock *block = createBlock(blockpos); + v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z); + //MapBlock *block = createBlock(p); + // 1) get from memory, 2) load from disk + MapBlock *block = emergeBlock(p, false); + // 3) create a blank one + if(block == NULL) + { + block = createBlock(p); - // Lighting won't be calculated + /* + Block gets sunlight if this is true. + + Refer to the map generator heuristics. + */ + bool ug = mapgen::block_is_underground(data->seed, p); + block->setIsUnderground(ug); + } + + // Lighting will not be valid after make_chunk is called block->setLightingExpired(true); // Lighting will be calculated //block->setLightingExpired(false); - - /* - Block gets sunlight if this is true. - - This should be set to true when the top side of a block - is completely exposed to the sky. - */ - block->setIsUnderground(false); } } } @@ -3906,21 +2114,23 @@ void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos) neighboring blocks */ + // The area that contains this block and it's neighbors v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1); v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1); - data->vmanip.setMap(this); + data->vmanip = new ManualMapVoxelManipulator(this); + //data->vmanip->setMap(this); // Add the area { //TimeTaker timer("initBlockMake() initialEmerge"); - data->vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max); + data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max); } // Data is ready now. } -MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, +MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data, core::map &changed_blocks) { v3s16 blockpos = data->blockpos; @@ -3929,10 +2139,12 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, if(data->no_op) { - dstream<<"finishBlockMake(): no-op"<getBool("enable_mapgen_debug_info"); + /*dstream<<"Resulting vmanip:"<vmanip.print(dstream);*/ @@ -3943,12 +2155,13 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, { // 70ms @cs=8 //TimeTaker timer("finishBlockMake() blitBackAll"); - data->vmanip.blitBackAll(&changed_blocks); + data->vmanip->blitBackAll(&changed_blocks); } -#if 1 - dstream<<"finishBlockMake: changed_blocks.size()=" - < lighting_update_blocks; - // Center block - lighting_update_blocks.insert(block->getPos(), block); + core::map lighting_update_blocks; +#if 1 + // Center block + lighting_update_blocks.insert(block->getPos(), block); + + /*{ + s16 x = 0; + s16 z = 0; + v3s16 p = block->getPos()+v3s16(x,1,z); + lighting_update_blocks[p] = getBlockNoCreateNoEx(p); + }*/ +#endif #if 0 - // All modified blocks - for(core::map::Iterator - i = changed_blocks.getIterator(); - i.atEnd() == false; i++) - { - lighting_update_blocks.insert(i.getNode()->getKey(), - i.getNode()->getValue()); - } + // All modified blocks + // NOTE: Should this be done? If this is not done, then the lighting + // of the others will be updated in a different place, one by one, i + // think... or they might not? Well, at least they are left marked as + // "lighting expired"; it seems that is not handled at all anywhere, + // so enabling this will slow it down A LOT because otherwise it + // would not do this at all. This causes the black trees. + for(core::map::Iterator + i = changed_blocks.getIterator(); + i.atEnd() == false; i++) + { + lighting_update_blocks.insert(i.getNode()->getKey(), + i.getNode()->getValue()); + } + /*// Also force-add all the upmost blocks for proper sunlight + for(s16 x=-1; x<=1; x++) + for(s16 z=-1; z<=1; z++) + { + v3s16 p = block->getPos()+v3s16(x,1,z); + lighting_update_blocks[p] = getBlockNoCreateNoEx(p); + }*/ #endif - updateLighting(lighting_update_blocks, changed_blocks); - + updateLighting(lighting_update_blocks, changed_blocks); + + /* + Set lighting to non-expired state in all of them. + This is cheating, but it is not fast enough if all of them + would actually be updated. + */ + for(s16 x=-1; x<=1; x++) + for(s16 y=-1; y<=1; y++) + for(s16 z=-1; z<=1; z++) + { + v3s16 p = block->getPos()+v3s16(x,y,z); + getBlockNoCreateNoEx(p)->setLightingExpired(false); + } + + if(enable_mapgen_debug_info == false) + t.stop(true); // Hide output + } + /* Add random objects to block */ - addRandomObjects(block); + mapgen::add_random_objects(block); /* Go through changed blocks @@ -4046,7 +2302,26 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, /*dstream<<"finishBlockMake() done for ("<getPos()+v3s16(x,y,z); + MapBlock *block = getBlockNoCreateNoEx(p); + char spos[20]; + snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z); + dstream<<"Generated "<getBool("enable_mapgen_debug_info"); + TimeTaker timer("generateBlock"); //MapBlock *block = original_dummy; @@ -4141,15 +2419,18 @@ MapBlock * ServerMap::generateBlock( /* Create block make data */ - BlockMakeData data; + mapgen::BlockMakeData data; initBlockMake(&data, p); /* Generate block */ { - TimeTaker t("makeBlock()"); - makeBlock(&data); + TimeTaker t("mapgen::make_block()"); + mapgen::make_block(&data); + + if(enable_mapgen_debug_info == false) + t.stop(true); // Hide output } /* @@ -4161,53 +2442,61 @@ MapBlock * ServerMap::generateBlock( Get central block */ MapBlock *block = getBlockNoCreateNoEx(p); - assert(block); #if 0 /* Check result */ - bool erroneus_content = false; - for(s16 z0=0; z0getNode(p); - if(n.d == CONTENT_IGNORE) + if(block) + { + bool erroneus_content = false; + for(s16 z0=0; z0getNode(p); + if(n.getContent() == CONTENT_IGNORE) + { + dstream<<"CONTENT_IGNORE at " + <<"("<setNode(v3s16(x0,y0,z0), n); + for(s16 y0=0; y0setNode(v3s16(x0,y0,z0), n); + } } } #endif + if(enable_mapgen_debug_info == false) + timer.stop(true); // Hide output + return block; } @@ -4274,23 +2563,51 @@ MapBlock * ServerMap::createBlock(v3s16 p) return block; } -#if 0 -MapBlock * ServerMap::emergeBlock( - v3s16 p, - bool only_from_disk, - core::map &changed_blocks, - core::map &lighting_invalidated_blocks -) +MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate) { - DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d", + DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d", __FUNCTION_NAME, - p.X, p.Y, p.Z, only_from_disk); + p.X, p.Y, p.Z, allow_generate); - // This has to be redone or removed - assert(0); + { + MapBlock *block = getBlockNoCreateNoEx(p); + if(block && block->isDummy() == false) + return block; + } + + { + MapBlock *block = loadBlock(p); + if(block) + return block; + } + + if(allow_generate) + { + core::map modified_blocks; + MapBlock *block = generateBlock(p, modified_blocks); + if(block) + { + MapEditEvent event; + event.type = MEET_OTHER; + event.p = p; + + // Copy modified_blocks to event + for(core::map::Iterator + i = modified_blocks.getIterator(); + i.atEnd()==false; i++) + { + event.modified_blocks.insert(i.getNode()->getKey(), false); + } + + // Queue event + dispatchEvent(&event); + + return block; + } + } + return NULL; } -#endif #if 0 /* @@ -4451,19 +2768,19 @@ s16 ServerMap::findGroundLevel(v2s16 p2d) for(; p.Y>min; p.Y--) { MapNode n = getNodeNoEx(p); - if(n.d != CONTENT_IGNORE) + if(n.getContent() != CONTENT_IGNORE) break; } if(p.Y == min) goto plan_b; // If this node is not air, go to plan b - if(getNodeNoEx(p).d != CONTENT_AIR) + if(getNodeNoEx(p).getContent() != CONTENT_AIR) goto plan_b; // Search existing walkable and return it for(; p.Y>min; p.Y--) { MapNode n = getNodeNoEx(p); - if(content_walkable(n.d) && n.d != CONTENT_IGNORE) + if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE) return p.Y; } @@ -4475,13 +2792,82 @@ s16 ServerMap::findGroundLevel(v2s16 p2d) Determine from map generator noise functions */ - s16 level = find_ground_level_from_noise(m_seed, p2d, 1); + s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1); return level; //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT; //return (s16)level; } +void ServerMap::createDatabase() { + int e; + assert(m_database); + e = sqlite3_exec(m_database, + "CREATE TABLE IF NOT EXISTS `blocks` (" + "`pos` INT NOT NULL PRIMARY KEY," + "`data` BLOB" + ");" + , NULL, NULL, NULL); + if(e == SQLITE_ABORT) + throw FileNotGoodException("Could not create database structure"); + else + dstream<<"Server: Database structure was created"; +} + +void ServerMap::verifyDatabase() { + if(m_database) + return; + + { + std::string dbp = m_savedir + "/map.sqlite"; + bool needs_create = false; + int d; + + /* + Open the database connection + */ + + createDirs(m_savedir); + + if(!fs::PathExists(dbp)) + needs_create = true; + + d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); + if(d != SQLITE_OK) { + dstream<<"WARNING: Database failed to open: "<::Iterator i = m_sectors.getIterator(); for(; i.atEnd() == false; i++) { @@ -4599,6 +2986,8 @@ void ServerMap::save(bool only_changed) core::list blocks; sector->getBlocks(blocks); core::list::Iterator j; + + //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL); for(j=blocks.begin(); j!=blocks.end(); j++) { MapBlock *block = *j; @@ -4617,8 +3006,10 @@ void ServerMap::save(bool only_changed) <getPos().Z<<")" <getPos(); + +#if 0 v2s16 p2d(p3d.X, p3d.Z); std::string sectordir = getSectorDir(p2d); @@ -4905,11 +3310,16 @@ void ServerMap::saveBlock(MapBlock *block) std::ofstream o(fullpath.c_str(), std::ios_base::binary); if(o.good() == false) throw FileNotGoodException("Cannot open block data"); - +#endif /* [0] u8 serialization version [1] data */ + + verifyDatabase(); + + std::ostringstream o(std::ios_base::binary); + o.write((char*)&version, 1); // Write basic data @@ -4917,7 +3327,23 @@ void ServerMap::saveBlock(MapBlock *block) // Write extra data stored on disk block->serializeDiskExtra(o, version); - + + // Write block to database + + std::string tmp = o.str(); + const char *bytes = tmp.c_str(); + + if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) + dstream<<"WARNING: Block position failed to bind: "<resetModified(); } @@ -4954,10 +3380,8 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto MapBlock *block = NULL; bool created_new = false; - try{ - block = sector->getBlockNoCreate(p3d.Y); - } - catch(InvalidPositionException &e) + block = sector->getBlockNoCreateNoEx(p3d.Y); + if(block == NULL) { block = sector->createBlankBlockNoInsert(p3d.Y); created_new = true; @@ -4980,6 +3404,9 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto if(version < SER_FMT_VER_HIGHEST || save_after_load) { saveBlock(block); + + // Should be in database now, so delete the old file + fs::RecursiveDelete(fullpath); } // We just loaded it from the disk, so it's up-to-date. @@ -5000,12 +3427,111 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto } } +void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load) +{ + DSTACK(__FUNCTION_NAME); + + try { + std::istringstream is(*blob, std::ios_base::binary); + + u8 version = SER_FMT_VER_INVALID; + is.read((char*)&version, 1); + + if(is.fail()) + throw SerializationError("ServerMap::loadBlock(): Failed" + " to read MapBlock version"); + + /*u32 block_size = MapBlock::serializedLength(version); + SharedBuffer data(block_size); + is.read((char*)*data, block_size);*/ + + // This will always return a sector because we're the server + //MapSector *sector = emergeSector(p2d); + + MapBlock *block = NULL; + bool created_new = false; + block = sector->getBlockNoCreateNoEx(p3d.Y); + if(block == NULL) + { + block = sector->createBlankBlockNoInsert(p3d.Y); + created_new = true; + } + + // Read basic data + block->deSerialize(is, version); + + // Read extra data stored on disk + block->deSerializeDiskExtra(is, version); + + // If it's a new block, insert it to the map + if(created_new) + sector->insertBlock(block); + + /* + Save blocks loaded in old format in new format + */ + + if(version < SER_FMT_VER_HIGHEST || save_after_load) + { + saveBlock(block); + } + + // We just loaded it from, so it's up-to-date. + block->resetModified(); + + } + catch(SerializationError &e) + { + dstream<<"WARNING: Invalid block data in database " + <<" (SerializationError). " + <<"what()="<deSerialize(is); } +#endif void ClientMap::OnRegisterSceneNode() { @@ -5192,6 +3721,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) m_camera_mutex.Lock(); v3f camera_position = m_camera_position; v3f camera_direction = m_camera_direction; + f32 camera_fov = m_camera_fov; m_camera_mutex.Unlock(); /* @@ -5210,13 +3740,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Take a fair amount as we will be dropping more out later v3s16 p_blocks_min( - p_nodes_min.X / MAP_BLOCKSIZE - 1, - p_nodes_min.Y / MAP_BLOCKSIZE - 1, - p_nodes_min.Z / MAP_BLOCKSIZE - 1); + p_nodes_min.X / MAP_BLOCKSIZE - 2, + p_nodes_min.Y / MAP_BLOCKSIZE - 2, + p_nodes_min.Z / MAP_BLOCKSIZE - 2); v3s16 p_blocks_max( - p_nodes_max.X / MAP_BLOCKSIZE, - p_nodes_max.Y / MAP_BLOCKSIZE, - p_nodes_max.Z / MAP_BLOCKSIZE); + p_nodes_max.X / MAP_BLOCKSIZE + 1, + p_nodes_max.Y / MAP_BLOCKSIZE + 1, + p_nodes_max.Z / MAP_BLOCKSIZE + 1); u32 vertex_count = 0; @@ -5284,10 +3814,14 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) float d = 0.0; if(isBlockInSight(block->getPos(), camera_position, - camera_direction, range, &d) == false) + camera_direction, camera_fov, + range, &d) == false) { continue; } + + // Okay, this block will be drawn. Reset usage timer. + block->resetUsageTimer(); // This is ugly (spherical distance limit?) /*if(m_control.range_all == false && @@ -5401,6 +3935,35 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) <<", rendered "<getBool("free_move") == false) + { + post_effect_color = video::SColor(255, 0, 0, 0); + } + if (post_effect_color.getAlpha() != 0) + { + // Draw a full-screen rectangle + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + v2u32 ss = driver->getScreenSize(); + core::rect rect(0,0, ss.X, ss.Y); + driver->draw2DRectangle(post_effect_color, rect); + } +} + bool ClientMap::setTempMod(v3s16 p, NodeMod mod, core::map *affected_blocks) { @@ -5853,10 +4416,16 @@ void ManualMapVoxelManipulator::blitBackAll( i = m_loaded_blocks.getIterator(); i.atEnd() == false; i++) { + v3s16 p = i.getNode()->getKey(); bool existed = i.getNode()->getValue(); if(existed == false) + { + // The Great Bug was found using this + /*dstream<<"ManualMapVoxelManipulator::blitBackAll: " + <<"Inexistent ("<getKey(); + } MapBlock *block = m_map->getBlockNoCreateNoEx(p); if(block == NULL) {