X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmap.cpp;h=ba4130ca29e860a9b6196cc66a1be5ecdb990988;hb=9b907dd65a2c045d10605894fdaea504200e2be7;hp=2a3e9f760ba32c9db0dff24008e6c190d192f4c2;hpb=fe855e004f424fec1f7f3f2e7e06be2f70f4952e;p=minetest.git diff --git a/src/map.cpp b/src/map.cpp index 2a3e9f760..ba4130ca2 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -28,13 +28,31 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "mapgen.h" #include "nodemetadata.h" +#include "content_mapnode.h" +#ifndef SERVER +#include +#endif +#include "settings.h" +#include "log.h" +#include "profiler.h" + +#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" -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 */ /* @@ -332,7 +350,7 @@ void Map::unspreadLight(enum LightBank bank, */ /*if(light_sources.find(n2pos)) { - std::cout<<"Removed from light_sources"<getKey(); //v3s16 pos = *j; - //dstream<<"pos=("<setNode(relpos, n); modified_blocks.insert(blockpos, block); }*/ @@ -714,7 +732,7 @@ void Map::updateLighting(enum LightBank bank, dummy block. */ //assert(0); - dstream<<"updateLighting(): InvalidPositionException" + infostream<<"updateLighting(): InvalidPositionException" <get("")) { core::map::Iterator i; i = blocks_to_update.getIterator(); @@ -785,7 +803,7 @@ void Map::updateLighting(enum LightBank bank, { u32 diff = modified_blocks.size() - count_was; count_was = modified_blocks.size(); - dstream<<"unspreadLight modified "<clone(); + meta->setOwner(player_name); setNodeMetadata(p, meta); } @@ -1015,7 +1034,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--){ @@ -1086,7 +1105,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); } @@ -1111,7 +1130,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, @@ -1158,7 +1177,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++) @@ -1240,17 +1259,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 { @@ -1258,7 +1279,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); } @@ -1279,7 +1300,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 @@ -1397,6 +1419,7 @@ void Map::timerUpdate(float dtime, float unload_timeout, core::map::Iterator si; + beginSave(); si = m_sectors.getIterator(); for(; si.atEnd() == false; si++) { @@ -1406,6 +1429,7 @@ void Map::timerUpdate(float dtime, float unload_timeout, core::list blocks; sector->getBlocks(blocks); + for(core::list::Iterator i = blocks.begin(); i != blocks.end(); i++) { @@ -1444,18 +1468,19 @@ void Map::timerUpdate(float dtime, float unload_timeout, 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 "< & modified_blocks) { DSTACK(__FUNCTION_NAME); @@ -1547,10 +1583,21 @@ void Map::transformLiquids(core::map & modified_blocks) u32 initial_size = m_transforming_liquid.size(); /*if(initial_size != 0) - dstream<<"transformLiquids(): initial_size="<= 7 - WATER_DROP_BOOST) - new_liquid_level = 7; - else - new_liquid_level = n2_liquid_level + WATER_DROP_BOOST; + /* + 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; } - else if(n2_liquid_level > 0) - { - new_liquid_level = n2_liquid_level - 1; + 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; } - - if(new_liquid_level > new_liquid_level_max) - new_liquid_level_max = new_liquid_level; - } - } //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++) - { - v3s16 p2 = p0 + dirs[i]; - - MapNode n2 = getNodeNoEx(p2); - if(content_flowing_liquid(n2.d)) - { - m_transforming_liquid.push_back(p2); + 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++) - { - 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; - } - else - liquid_next_level = liquid_level - 1; - - bool n2_changed = false; - bool flowed = false; - - v3s16 p2 = p0 + dirs_to[i]; - - MapNode n2 = getNodeNoEx(p2); - //dstream<<"[1] n2.param="<<(int)n2.param< liquid_level) - { - n2.param2 = liquid_next_level; - setNode(p2, n2); - - n2_changed = true; - flowed = true; - } + 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; } } - else if(n2.d == CONTENT_AIR) - { - n2.d = nonsource_c; - n2.param2 = liquid_next_level; - setNode(p2, n2); - n2_changed = true; - flowed = true; - } + 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 + new_node_content = CONTENT_AIR; - //dstream<<"[2] n2.param="<<(int)n2.param<getPos()] = block; } - loopcount++; - //if(loopcount >= 100000) - if(loopcount >= initial_size * 1) - break; + /* + enqueue neighbors for update if neccessary + */ + switch (content_features(n0.getContent()).liquid_type) { + case LIQUID_SOURCE: + case LIQUID_FLOWING: + // make sure source flows into all neighboring nodes + for (u16 i = 0; i < num_flows; i++) + if (flows[i].t != NEIGHBOR_UPPER) + m_transforming_liquid.push_back(flows[i].p); + for (u16 i = 0; i < num_airs; i++) + if (airs[i].t != NEIGHBOR_UPPER) + m_transforming_liquid.push_back(airs[i].p); + break; + case LIQUID_NONE: + // this flow has turned to air; neighboring flows might need to do the same + for (u16 i = 0; i < num_flows; i++) + m_transforming_liquid.push_back(flows[i].p); + break; + } } - //dstream<<"Map::transformLiquids(): loopcount="<get("fixed_map_seed").empty()) + { + m_seed = (((u64)(myrand()%0xffff)<<0) + + ((u64)(myrand()%0xffff)<<16) + + ((u64)(myrand()%0xffff)<<32) + + ((u64)(myrand()%0xffff)<<48)); + } + else + { + m_seed = g_settings->getU64("fixed_map_seed"); + } /* Experimental and debug stuff @@ -1907,7 +1949,7 @@ ServerMap::ServerMap(std::string savedir): // If directory is empty, it is safe to save into it. if(fs::GetDirListing(m_savedir).size() == 0) { - dstream<getBool("enable_mapgen_debug_info"); + if(enable_mapgen_debug_info) + infostream<<"initBlockMake(): ("<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); } } } @@ -2091,19 +2148,19 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data, core::map &changed_blocks) { v3s16 blockpos = data->blockpos; - /*dstream<<"finishBlockMake(): ("<no_op) { - //dstream<<"finishBlockMake(): no-op"<getBool("enable_mapgen_debug_info"); - /*dstream<<"Resulting vmanip:"<vmanip.print(dstream);*/ + /*infostream<<"Resulting vmanip:"<vmanip.print(infostream);*/ /* Blit generated stuff to map @@ -2116,7 +2173,7 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data, } if(enable_mapgen_debug_info) - dstream<<"finishBlockMake: changed_blocks.size()=" + infostream<<"finishBlockMake: changed_blocks.size()=" <setIsUnderground(mapgen::block_is_underground(data->seed, blockpos)); - block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos)); /* Add sunlight to central block. @@ -2169,6 +2230,13 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data, #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 @@ -2185,8 +2253,28 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data, 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); + + /* + 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 @@ -2226,9 +2314,28 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data, */ //save(true); - /*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); + infostream<<"Generated "<getBool("enable_mapgen_debug_info"); TimeTaker timer("generateBlock"); @@ -2318,7 +2426,7 @@ MapBlock * ServerMap::generateBlock( */ if(blockpos_over_limit(p)) { - dstream<<__FUNCTION_NAME<<": Block position over limit"<getNode(p); - if(n.d == CONTENT_IGNORE) + if(n.getContent() == CONTENT_IGNORE) { - dstream<<"CONTENT_IGNORE at " + infostream<<"CONTENT_IGNORE at " <<"("<setNode(v3s16(x0,y0,z0), n); } } @@ -2438,7 +2546,7 @@ MapBlock * ServerMap::createBlock(v3s16 p) } catch(InvalidPositionException &e) { - dstream<<"createBlock: createSector() failed"<isDummy() == false) return block; } @@ -2515,152 +2623,6 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate) return NULL; } -#if 0 - /* - Do not generate over-limit - */ - if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) - throw InvalidPositionException("emergeBlock(): pos. over limit"); - - v2s16 p2d(p.X, p.Z); - s16 block_y = p.Y; - /* - This will create or load a sector if not found in memory. - If block exists on disk, it will be loaded. - */ - ServerMapSector *sector; - try{ - sector = createSector(p2d); - //sector = emergeSector(p2d, changed_blocks); - } - catch(InvalidPositionException &e) - { - dstream<<"emergeBlock: createSector() failed: " - <getBlockNoCreateNoEx(block_y); - - // If not found, try loading from disk - if(block == NULL) - { - block = loadBlock(p); - } - - // Handle result - if(block == NULL) - { - does_not_exist = true; - } - else if(block->isDummy() == true) - { - does_not_exist = true; - } - else if(block->getLightingExpired()) - { - lighting_expired = true; - } - else - { - // Valid block - //dstream<<"emergeBlock(): Returning already valid block"<insertBlock(block); - } - // Done. - return block; - } - - //dstream<<"Not found on disk, generating."< making one"< light_sources; - bool black_air_left = false; - bool bottom_invalid = - block->propagateSunlight(light_sources, true, - &black_air_left); - - // If sunlight didn't reach everywhere and part of block is - // above ground, lighting has to be properly updated - //if(black_air_left && some_part_underground) - if(black_air_left) - { - lighting_invalidated_blocks[block->getPos()] = block; - } - - if(bottom_invalid) - { - lighting_invalidated_blocks[block->getPos()] = block; - } - } -#endif - - return block; -} -#endif - s16 ServerMap::findGroundLevel(v2s16 p2d) { #if 0 @@ -2674,19 +2636,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; } @@ -2705,6 +2667,81 @@ s16 ServerMap::findGroundLevel(v2s16 p2d) //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 + infostream<<"Server: Database structure was created"; +} + +void ServerMap::verifyDatabase() { + if(m_database) + return; + + { + std::string dbp = m_savedir + DIR_DELIM + "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) { + infostream<<"WARNING: Database failed to open: "<::Iterator i = m_sectors.getIterator(); for(; i.atEnd() == false; i++) { @@ -2822,6 +2860,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; @@ -2834,14 +2874,16 @@ void ServerMap::save(bool only_changed) saveBlock(block); block_count++; - /*dstream<<"ServerMap: Written block (" + /*infostream<<"ServerMap: Written block (" <getPos().X<<"," <getPos().Y<<"," <getPos().Z<<")" <= 0) + return i % mod; + return mod - ((-i) % mod); +} + +v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i) +{ + s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048); + i = (i - x) / 4096; + s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048); + i = (i - y) / 4096; + s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048); + return v3s16(x,y,z); +} + +void ServerMap::listAllLoadableBlocks(core::list &dst) +{ + if(loadFromFolders()){ + errorstream<<"Map::listAllLoadableBlocks(): Result will be missing " + <<"all blocks that are stored in flat files"<isDummy()) { /*v3s16 p = block->getPos(); - dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block " + infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block " <<"("<getPos(); + +#if 0 v2s16 p2d(p3d.X, p3d.Z); std::string sectordir = getSectorDir(p2d); createDirs(sectordir); - std::string fullpath = sectordir+"/"+getBlockFilename(p3d); + std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d); 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 @@ -3140,7 +3245,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) + infostream<<"WARNING: Block position failed to bind: "<resetModified(); } @@ -3149,7 +3270,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto { DSTACK(__FUNCTION_NAME); - std::string fullpath = sectordir+"/"+blockfile; + std::string fullpath = sectordir+DIR_DELIM+blockfile; try{ std::ifstream is(fullpath.c_str(), std::ios_base::binary); @@ -3201,6 +3322,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. @@ -3209,7 +3333,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto } catch(SerializationError &e) { - dstream<<"WARNING: Invalid block data on disk " + infostream<<"WARNING: Invalid block data on disk " <<"fullpath="< 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) + { + infostream<<"WARNING: Invalid block data in database " + <<" (SerializationError). " + <<"what()="<::Iterator si; @@ -3462,7 +3700,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) int time2 = time(0); if(time2 > time1 + 4) { - dstream<<"ClientMap::renderMap(): " + infostream<<"ClientMap::renderMap(): " "Rendering takes ages, returning." <getPos(), camera_position, - camera_direction, range, &d) == false) + camera_direction, camera_fov, + range, &d) == false) { continue; } @@ -3519,6 +3758,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) /*if(m_control.range_all == false && d - 0.5*BS*MAP_BLOCKSIZE > range) continue;*/ + + blocks_in_range++; #if 1 /* @@ -3537,8 +3778,10 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Mesh has not been expired and there is no mesh: // block has no content - if(block->mesh == NULL && mesh_expired == false) + if(block->mesh == NULL && mesh_expired == false){ + blocks_in_range_without_mesh++; continue; + } } f32 faraway = BS*50; @@ -3576,9 +3819,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) JMutexAutoLock lock(block->mesh_mutex); scene::SMesh *mesh = block->mesh; - - if(mesh == NULL) + + if(mesh == NULL){ + blocks_in_range_without_mesh++; continue; + } blocks_would_have_drawn++; if(blocks_drawn >= m_control.wanted_max_blocks @@ -3590,7 +3835,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) sector_blocks_drawn++; u32 c = mesh->getMeshBufferCount(); - + bool stuff_actually_drawn = false; for(u32 i=0; igetMeshBuffer(i); @@ -3601,16 +3846,25 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // Render transparent on transparent pass and likewise. if(transparent == is_transparent_pass) { + if(buf->getVertexCount() == 0) + errorstream<<"Block ["<setMaterial(buf->getMaterial()); driver->drawMeshBuffer(buf); vertex_count += buf->getVertexCount(); + meshbuffer_count++; + stuff_actually_drawn = true; } } + if(stuff_actually_drawn) + blocks_had_pass_meshbuf++; + else + blocks_without_stuff++; } } // foreach sectorblocks @@ -3620,13 +3874,66 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) } } + std::string prefix = "CM: "; + + // Log only on solid pass because values are the same + if(pass == scene::ESNRP_SOLID){ + g_profiler->avg(prefix+"blocks in range", blocks_in_range); + if(blocks_in_range != 0) + g_profiler->avg(prefix+"blocks in range without mesh (frac)", + (float)blocks_in_range_without_mesh/blocks_in_range); + g_profiler->avg(prefix+"blocks drawn", blocks_drawn); + } + + if(pass == scene::ESNRP_SOLID) + prefix = "CM: solid: "; + else + prefix = "CM: transparent: "; + + g_profiler->avg(prefix+"vertices drawn", vertex_count); + if(blocks_had_pass_meshbuf != 0) + g_profiler->avg(prefix+"meshbuffers per block", + (float)meshbuffer_count / (float)blocks_had_pass_meshbuf); + if(blocks_drawn != 0) + g_profiler->avg(prefix+"empty blocks (frac)", + (float)blocks_without_stuff / blocks_drawn); + m_control.blocks_drawn = blocks_drawn; m_control.blocks_would_have_drawn = blocks_would_have_drawn; - /*dstream<<"renderMap(): is_transparent_pass="<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) { @@ -3844,7 +4151,7 @@ MapVoxelManipulator::MapVoxelManipulator(Map *map) MapVoxelManipulator::~MapVoxelManipulator() { - /*dstream<<"MapVoxelManipulator: blocks: "<getBlockNoCreate(p); if(block->isDummy()) @@ -3908,7 +4215,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) m_loaded_blocks.insert(p, !block_data_inexistent); } - //dstream<<"emerge done"<getKey(); bool existed = i.getNode()->getValue(); if(existed == false) + { + // The Great Bug was found using this + /*infostream<<"ManualMapVoxelManipulator::blitBackAll: " + <<"Inexistent ("<getKey(); + } MapBlock *block = m_map->getBlockNoCreateNoEx(p); if(block == NULL) { - dstream<<"WARNING: "<<__FUNCTION_NAME + infostream<<"WARNING: "<<__FUNCTION_NAME <<": got NULL block " <<"("<