Fix a memleak pointed by @Zeno- in MeshUpdateQueue
[oweals/minetest.git] / src / map.cpp
index a1502befa5ff0a7aa161b062dae82c340ce01d2d..63e1e4ccd2d68bc3543ac2e46430c852888e9bcb 100644 (file)
@@ -44,6 +44,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "database.h"
 #include "database-dummy.h"
 #include "database-sqlite3.h"
+#include "script/scripting_server.h"
 #include <deque>
 #include <queue>
 #if USE_LEVELDB
@@ -637,7 +638,8 @@ s32 Map::transforming_liquid_size() {
         return m_transforming_liquid.size();
 }
 
-void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
+void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
+               ServerEnvironment *env)
 {
        DSTACK(FUNCTION_NAME);
        //TimeTaker timer("transformLiquids()");
@@ -897,8 +899,16 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
                        // set the liquid level and flow bit to 0
                        n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
                }
+
+               // change the node.
                n0.setContent(new_node_content);
 
+               // on_flood() the node
+               if (floodable_node != CONTENT_AIR) {
+                       if (env->getScriptIface()->node_on_flood(p0, n00, n0))
+                               continue;
+               }
+
                // Ignore light (because calling voxalgo::update_lighting_nodes)
                n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
                n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
@@ -971,7 +981,7 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks)
 
        time_until_purge *= 1000;       // seconds -> milliseconds
 
-       u32 curr_time = getTime(PRECISION_MILLI);
+       u32 curr_time = porting::getTime(PRECISION_MILLI);
        u32 prev_unprocessed = m_unprocessed_count;
        m_unprocessed_count = m_transforming_liquid.size();
 
@@ -1157,10 +1167,77 @@ void Map::removeNodeTimer(v3s16 p)
        block->m_node_timers.remove(p_rel);
 }
 
+bool Map::isOccluded(v3s16 p0, v3s16 p1, float step, float stepfac,
+               float start_off, float end_off, u32 needed_count)
+{
+       float d0 = (float)BS * p0.getDistanceFrom(p1);
+       v3s16 u0 = p1 - p0;
+       v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
+       uf.normalize();
+       v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
+       u32 count = 0;
+       for(float s=start_off; s<d0+end_off; s+=step){
+               v3f pf = p0f + uf * s;
+               v3s16 p = floatToInt(pf, BS);
+               MapNode n = getNodeNoEx(p);
+               const ContentFeatures &f = m_nodedef->get(n);
+               if(f.drawtype == NDT_NORMAL){
+                       // not transparent, see ContentFeature::updateTextures
+                       count++;
+                       if(count >= needed_count)
+                               return true;
+               }
+               step *= stepfac;
+       }
+       return false;
+}
+
+bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) {
+       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;
+       // The occlusion search of 'isOccluded()' must stop short of the target
+       // point by distance 'endoff' (end offset) to not enter the target mapblock.
+       // For the 8 mapblock corners 'endoff' must therefore be the maximum diagonal
+       // of a mapblock, because we must consider all view angles.
+       // sqrt(1^2 + 1^2 + 1^2) = 1.732
+       float endoff = -BS * MAP_BLOCKSIZE * 1.732050807569;
+       v3s16 spn = cam_pos_nodes;
+       s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
+       // to reduce the likelihood of falsely occluded blocks
+       // require at least two solid blocks
+       // this is a HACK, we should think of a more precise algorithm
+       u32 needed_count = 2;
+
+       return (
+               // For the central point of the mapblock 'endoff' can be halved
+               isOccluded(spn, cpn,
+                       step, stepfac, startoff, endoff / 2.0f, needed_count) &&
+               isOccluded(spn, cpn + v3s16(bs2,bs2,bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(bs2,bs2,-bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(bs2,-bs2,bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(bs2,-bs2,-bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(-bs2,bs2,bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(-bs2,bs2,-bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(-bs2,-bs2,bs2),
+                       step, stepfac, startoff, endoff, needed_count) &&
+               isOccluded(spn, cpn + v3s16(-bs2,-bs2,-bs2),
+                       step, stepfac, startoff, endoff, needed_count));
+}
+
 /*
        ServerMap
 */
-ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
+ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
+               EmergeManager *emerge):
        Map(dout_server, gamedef),
        settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
        m_emerge(emerge),
@@ -1301,6 +1378,19 @@ s16 ServerMap::getWaterLevel()
        return getMapgenParams()->water_level;
 }
 
+bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
+{
+       const s16 mapgen_limit_bp = rangelim(
+               getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
+               MAP_BLOCKSIZE;
+       return p.X < -mapgen_limit_bp ||
+               p.X >  mapgen_limit_bp ||
+               p.Y < -mapgen_limit_bp ||
+               p.Y >  mapgen_limit_bp ||
+               p.Z < -mapgen_limit_bp ||
+               p.Z >  mapgen_limit_bp;
+}
+
 bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
 {
        s16 csize = getMapgenParams()->chunksize;
@@ -1314,9 +1404,9 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
        v3s16 full_bpmin = bpmin - extra_borders;
        v3s16 full_bpmax = bpmax + extra_borders;
 
-       // Do nothing if not inside limits (+-1 because of neighbors)
-       if (blockpos_over_limit(full_bpmin) ||
-               blockpos_over_limit(full_bpmax))
+       // Do nothing if not inside mapgen limits (+-1 because of neighbors)
+       if (blockpos_over_mapgen_limit(full_bpmin) ||
+                       blockpos_over_mapgen_limit(full_bpmax))
                return false;
 
        data->seed = getSeed();
@@ -1483,25 +1573,14 @@ ServerMapSector *ServerMap::createSector(v2s16 p2d)
 #endif
 
        /*
-               Do not create over-limit.
-               We are checking for any nodes of the mapblocks of the sector being beyond the limit.
-               A sector is a vertical column of mapblocks, so sectorpos is like a 2D blockpos.
-
-               At the negative limit we are checking for
-                       block minimum nodepos < -mapgenlimit.
-               At the positive limit we are checking for
-                       block maximum nodepos > mapgenlimit.
-
-               Block minimum nodepos = blockpos * mapblocksize.
-               Block maximum nodepos = (blockpos + 1) * mapblocksize - 1.
+               Do not create over max mapgen limit
        */
-       const u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
-               g_settings->getU16("map_generation_limit"));
-       if (p2d.X * MAP_BLOCKSIZE < -map_gen_limit
-                       || (p2d.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit
-                       || p2d.Y * MAP_BLOCKSIZE < -map_gen_limit
-                       || (p2d.Y + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit)
-               throw InvalidPositionException("createSector(): pos. over limit");
+       const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
+       if (p2d.X < -max_limit_bp ||
+                       p2d.X >  max_limit_bp ||
+                       p2d.Y < -max_limit_bp ||
+                       p2d.Y >  max_limit_bp)
+               throw InvalidPositionException("createSector(): pos. over max mapgen limit");
 
        /*
                Generate blank sector
@@ -1642,10 +1721,10 @@ MapBlock * ServerMap::createBlock(v3s16 p)
                        FUNCTION_NAME, p.X, p.Y, p.Z);
 
        /*
-               Do not create over-limit
+               Do not create over max mapgen limit
        */
-       if (blockpos_over_limit(p))
-               throw InvalidPositionException("createBlock(): pos. over limit");
+       if (blockpos_over_max_limit(p))
+               throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
 
        v2s16 p2d(p.X, p.Z);
        s16 block_y = p.Y;
@@ -1868,7 +1947,7 @@ std::string ServerMap::getSectorDir(v2s16 pos, int layout)
        }
 }
 
-v2s16 ServerMap::getSectorPos(std::string dirname)
+v2s16 ServerMap::getSectorPos(const std::string &dirname)
 {
        unsigned int x = 0, y = 0;
        int r;
@@ -1898,7 +1977,7 @@ v2s16 ServerMap::getSectorPos(std::string dirname)
        return pos;
 }
 
-v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
+v3s16 ServerMap::getBlockPos(const std::string &sectordir, const std::string &blockfile)
 {
        v2s16 p2d = getSectorPos(sectordir);
 
@@ -2207,13 +2286,13 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
 }
 #endif
 
-Database *ServerMap::createDatabase(
+MapDatabase *ServerMap::createDatabase(
        const std::string &name,
        const std::string &savedir,
        Settings &conf)
 {
        if (name == "sqlite3")
-               return new Database_SQLite3(savedir);
+               return new MapDatabaseSQLite3(savedir);
        if (name == "dummy")
                return new Database_Dummy();
        #if USE_LEVELDB
@@ -2225,8 +2304,11 @@ Database *ServerMap::createDatabase(
                return new Database_Redis(conf);
        #endif
        #if USE_POSTGRESQL
-       else if (name == "postgresql")
-               return new Database_PostgreSQL(conf);
+       else if (name == "postgresql") {
+               std::string connect_string = "";
+               conf.getNoEx("pgsql_connection", connect_string);
+               return new MapDatabasePostgreSQL(connect_string);
+       }
        #endif
        else
                throw BaseException(std::string("Database backend ") + name + " not supported.");
@@ -2247,7 +2329,7 @@ bool ServerMap::saveBlock(MapBlock *block)
        return saveBlock(block, dbase);
 }
 
-bool ServerMap::saveBlock(MapBlock *block, Database *db)
+bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db)
 {
        v3s16 p3d = block->getPos();
 
@@ -2278,16 +2360,15 @@ bool ServerMap::saveBlock(MapBlock *block, Database *db)
        return ret;
 }
 
-void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
+void ServerMap::loadBlock(const std::string &sectordir, const std::string &blockfile,
                MapSector *sector, bool save_after_load)
 {
        DSTACK(FUNCTION_NAME);
 
        std::string fullpath = sectordir + DIR_DELIM + blockfile;
        try {
-
                std::ifstream is(fullpath.c_str(), std::ios_base::binary);
-               if(is.good() == false)
+               if (!is.good())
                        throw FileNotGoodException("Cannot open block file");
 
                v3s16 p3d = getBlockPos(sectordir, blockfile);
@@ -2372,13 +2453,6 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool
                        throw SerializationError("ServerMap::loadBlock(): Failed"
                                        " to read MapBlock version");
 
-               /*u32 block_size = MapBlock::serializedLength(version);
-               SharedBuffer<u8> 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);
@@ -2530,6 +2604,16 @@ void ServerMap::PrintInfo(std::ostream &out)
        out<<"ServerMap: ";
 }
 
+bool ServerMap::repairBlockLight(v3s16 blockpos,
+       std::map<v3s16, MapBlock *> *modified_blocks)
+{
+       MapBlock *block = emergeBlock(blockpos, false);
+       if (!block || !block->isGenerated())
+               return false;
+       voxalgo::repair_block_light(this, block, modified_blocks);
+       return true;
+}
+
 MMVManip::MMVManip(Map *map):
                VoxelManipulator(),
                m_is_dirty(false),
@@ -2596,7 +2680,7 @@ void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
                if(block_data_inexistent)
                {
 
-                       if (load_if_inexistent && !blockpos_over_limit(p)) {
+                       if (load_if_inexistent && !blockpos_over_max_limit(p)) {
                                ServerMap *svrmap = (ServerMap *)m_map;
                                block = svrmap->emergeBlock(p, false);
                                if (block == NULL)