X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmap.cpp;h=858c08b63705d4f3ca3414960e2649767ee85176;hb=64996422c00ddb70cfc8aee7da7b62485b8b0416;hp=4204c2a02ee88b055e018284188c5b6e96c7bb85;hpb=570a8dbf22e8e33fed22292941edfaa38d41c289;p=minetest.git diff --git a/src/map.cpp b/src/map.cpp index 4204c2a02..858c08b63 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -21,20 +21,41 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapsector.h" #include "mapblock.h" #include "main.h" +#ifndef SERVER #include "client.h" +#endif #include "filesys.h" #include "utility.h" #include "voxel.h" #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" +#include "mapnode_contentfeatures.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 +353,7 @@ void Map::unspreadLight(enum LightBank bank, */ /*if(light_sources.find(n2pos)) { - std::cout<<"Removed from light_sources"<getKey(); //v3s16 pos = *j; - //dstream<<"pos=("<get("")) { core::map::Iterator i; i = blocks_to_update.getIterator(); @@ -785,7 +806,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); } @@ -1281,7 +1303,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 @@ -1399,6 +1422,7 @@ void Map::timerUpdate(float dtime, float unload_timeout, core::map::Iterator si; + beginSave(); si = m_sectors.getIterator(); for(; si.atEnd() == false; si++) { @@ -1408,6 +1432,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++) { @@ -1446,18 +1471,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) u32 initial_size = m_transforming_liquid.size(); /*if(initial_size != 0) - dstream<<"transformLiquids(): initial_size="<get("fixed_map_seed").empty()) { m_seed = (((u64)(myrand()%0xffff)<<0) + ((u64)(myrand()%0xffff)<<16) @@ -1888,7 +1927,7 @@ ServerMap::ServerMap(std::string savedir): } else { - m_seed = g_settings.getU64("fixed_map_seed"); + m_seed = g_settings->getU64("fixed_map_seed"); } /* @@ -1913,7 +1952,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) - dstream<<"initBlockMake(): ("< &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 @@ -2127,7 +2176,7 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data, } if(enable_mapgen_debug_info) - dstream<<"finishBlockMake: changed_blocks.size()=" + infostream<<"finishBlockMake: changed_blocks.size()=" <getBool("enable_mapgen_debug_info"); TimeTaker timer("generateBlock"); @@ -2379,7 +2429,7 @@ MapBlock * ServerMap::generateBlock( */ if(blockpos_over_limit(p)) { - dstream<<__FUNCTION_NAME<<": Block position over limit"<getNode(p); if(n.getContent() == CONTENT_IGNORE) { - dstream<<"CONTENT_IGNORE at " + infostream<<"CONTENT_IGNORE at " <<"("< 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 @@ -2766,6 +2670,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++) { @@ -2883,6 +2863,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; @@ -2895,14 +2877,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 @@ -3201,7 +3248,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(); } @@ -3210,7 +3273,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); @@ -3262,6 +3325,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. @@ -3270,7 +3336,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()="<getNodeNoEx(p); + bool is_transparent = false; + ContentFeatures &f = content_features(n); + if(f.solidness == 0) + is_transparent = (f.visual_solidness != 2); + else + is_transparent = (f.solidness != 2); + if(!is_transparent){ + count++; + if(count >= needed_count) + return true; + } + step *= stepfac; + } + return false; +} + void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) { //m_dout< drawset; - int timecheck_counter = 0; - core::map::Iterator si; - si = m_sectors.getIterator(); - for(; si.atEnd() == false; si++) { - { - timecheck_counter++; - if(timecheck_counter > 50) - { - timecheck_counter = 0; - int time2 = time(0); - if(time2 > time1 + 4) - { - dstream<<"ClientMap::renderMap(): " - "Rendering takes ages, returning." - <::Iterator + si = m_sectors.getIterator(); + si.atEnd() == false; si++) + { MapSector *sector = si.getNode()->getValue(); v2s16 sp = sector->getPos(); @@ -3547,11 +3753,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) sector->getBlocks(sectorblocks); /* - Draw blocks + Loop through blocks in sector */ - - u32 sector_blocks_drawn = 0; + u32 sector_blocks_drawn = 0; + core::list< MapBlock * >::Iterator i; for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++) { @@ -3565,22 +3771,22 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) float range = 100000 * BS; if(m_control.range_all == false) range = m_control.wanted_range * BS; - + 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 && d - 0.5*BS*MAP_BLOCKSIZE > range) continue;*/ + blocks_in_range++; + #if 1 /* Update expired mesh (used for day/night change) @@ -3598,8 +3804,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; @@ -3628,66 +3836,211 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) mesh_expired = false; } - #endif + /* - Draw the faces of the block + Occlusion culling + */ + + v3s16 cpn = block->getPos() * MAP_BLOCKSIZE; + cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2); + float step = BS*1; + float stepfac = 1.1; + float startoff = BS*1; + float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42; + v3s16 spn = cam_pos_nodes + v3s16(0,0,0); + s16 bs2 = MAP_BLOCKSIZE/2 + 1; + u32 needed_count = 1; + if( + isOccluded(this, spn, cpn + v3s16(0,0,0), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2), + step, stepfac, startoff, endoff, needed_count) + ) + { + blocks_occlusion_culled++; + continue; + } + + // This block is in range. Reset usage timer. + block->resetUsageTimer(); + + /* + Ignore if mesh doesn't exist */ { JMutexAutoLock lock(block->mesh_mutex); scene::SMesh *mesh = block->mesh; - - if(mesh == NULL) - continue; - blocks_would_have_drawn++; - if(blocks_drawn >= m_control.wanted_max_blocks - && m_control.range_all == false - && d > m_control.wanted_min_range * BS) + if(mesh == NULL){ + blocks_in_range_without_mesh++; continue; + } + } + + // Limit block count in case of a sudden increase + blocks_would_have_drawn++; + if(blocks_drawn >= m_control.wanted_max_blocks + && m_control.range_all == false + && d > m_control.wanted_min_range * BS) + continue; + + // Add to set + drawset[block->getPos()] = block; + + sector_blocks_drawn++; + blocks_drawn++; - blocks_drawn++; - sector_blocks_drawn++; + } // foreach sectorblocks - u32 c = mesh->getMeshBufferCount(); + if(sector_blocks_drawn != 0) + m_last_drawn_sectors[sp] = true; + } + } // ScopeProfiler + + /* + Draw the selected MapBlocks + */ - for(u32 i=0; i::Iterator + i = drawset.getIterator(); + i.atEnd() == false; i++) + { + { + timecheck_counter++; + if(timecheck_counter > 50) + { + timecheck_counter = 0; + int time2 = time(0); + if(time2 > time1 + 4) { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); - const video::SMaterial& material = buf->getMaterial(); - video::IMaterialRenderer* rnd = - driver->getMaterialRenderer(material.MaterialType); - bool transparent = (rnd && rnd->isTransparent()); - // Render transparent on transparent pass and likewise. - if(transparent == is_transparent_pass) - { - /* - This *shouldn't* hurt too much because Irrlicht - doesn't change opengl textures if the old - material is set again. - */ - driver->setMaterial(buf->getMaterial()); - driver->drawMeshBuffer(buf); - vertex_count += buf->getVertexCount(); - } + infostream<<"ClientMap::renderMap(): " + "Rendering takes ages, returning." + <getValue(); - if(sector_blocks_drawn != 0) + /* + Draw the faces of the block + */ { - m_last_drawn_sectors[sp] = true; + JMutexAutoLock lock(block->mesh_mutex); + + scene::SMesh *mesh = block->mesh; + assert(mesh); + + u32 c = mesh->getMeshBufferCount(); + bool stuff_actually_drawn = false; + for(u32 i=0; igetMeshBuffer(i); + const video::SMaterial& material = buf->getMaterial(); + video::IMaterialRenderer* rnd = + driver->getMaterialRenderer(material.MaterialType); + bool transparent = (rnd && rnd->isTransparent()); + // 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++; } } + } // ScopeProfiler + + // Log only on solid pass because values are the same + if(pass == scene::ESNRP_SOLID){ + g_profiler->avg("CM: blocks in range", blocks_in_range); + g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled); + if(blocks_in_range != 0) + g_profiler->avg("CM: blocks in range without mesh (frac)", + (float)blocks_in_range_without_mesh/blocks_in_range); + g_profiler->avg("CM: blocks drawn", blocks_drawn); + } + 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) { @@ -3905,7 +4258,7 @@ MapVoxelManipulator::MapVoxelManipulator(Map *map) MapVoxelManipulator::~MapVoxelManipulator() { - /*dstream<<"MapVoxelManipulator: blocks: "<getBlockNoCreate(p); if(block->isDummy()) @@ -3969,7 +4322,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) m_loaded_blocks.insert(p, !block_data_inexistent); } - //dstream<<"emerge done"<getBlockNoCreateNoEx(p); if(block == NULL) { - dstream<<"WARNING: "<<__FUNCTION_NAME + infostream<<"WARNING: "<<__FUNCTION_NAME <<": got NULL block " <<"("<