Add ability to delete MapBlocks from map
authorkwolekr <kwolekr@minetest.net>
Thu, 15 Jan 2015 21:20:05 +0000 (16:20 -0500)
committerkwolekr <kwolekr@minetest.net>
Thu, 15 Jan 2015 21:48:56 +0000 (16:48 -0500)
Also add a Lua API and chatcommand for this

15 files changed:
builtin/game/chatcommands.lua
doc/lua_api.txt
src/database-dummy.cpp
src/database-dummy.h
src/database-leveldb.cpp
src/database-leveldb.h
src/database-redis.cpp
src/database-redis.h
src/database-sqlite3.cpp
src/database-sqlite3.h
src/database.h
src/map.cpp
src/map.h
src/script/lua_api/l_env.cpp
src/script/lua_api/l_env.h

index 4f7b031aa1d9bca47d6176c48abe5d75f78a91a6..18b5dbe7264b5b97129724a1c47012dcc4f8eca9 100644 (file)
@@ -403,6 +403,46 @@ core.register_chatcommand("set", {
        end,
 })
 
+core.register_chatcommand("deleteblocks", {
+       params = "[here] [<pos1> <pos2>]",
+       description = "delete map blocks contained in area pos1 to pos2",
+       privs = {server=true},
+       func = function(name, param)
+               local p1 = {}
+               local p2 = {}
+               if param == "here" then
+                       local player = core.get_player_by_name(name)
+                       if player == nil then
+                               core.log("error", "player is nil")
+                               return false, "Unable to get current position; player is nil"
+                       end
+                       p1 = player:getpos()
+                       p2 = p1
+               else
+                       p1.x, p1.y, p1.z, p2.x, p2.y, p2.z = string.match(param,
+                               "^%(([%d.-]+), *([%d.-]+), *([%d.-]+)%) *%(([%d.-]+), *([%d.-]+), *([%d.-]+)%)$")
+                       p1.x = tonumber(p1.x)
+                       p1.y = tonumber(p1.y)
+                       p1.z = tonumber(p1.z)
+                       p2.x = tonumber(p2.x)
+                       p2.y = tonumber(p2.y)
+                       p2.z = tonumber(p2.z)
+
+                       if p1.x == nil or p1.y == nil or p1.z == nil or
+                               p2.x == nil or p2.y == nil or p2.z == nil then
+                               return false, "Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)"
+                       end
+               end
+
+               if core.delete_area(p1, p2) then
+                       return true, "Successfully cleared area ranging from " ..
+                               core.pos_to_string(p1) .. " to " .. core.pos_to_string(p2)
+               else
+                       return false, "Failed to clear one or more blocks in area"
+               end
+       end,
+})
+
 core.register_chatcommand("mods", {
        params = "",
        description = "List mods installed on the server",
index 6d66253480e8b34b833032be6380ff9ea8bb191d..6359ef6dee51f6d1d455dcc72f88bec128511db7 100644 (file)
@@ -1880,6 +1880,8 @@ and `minetest.auth_reload` call the authetification handler.
    * Generate all registered decorations within the VoxelManip specified by `vm`.
 * `minetest.clear_objects()`
     * clear all objects in the environments
+* `minetest.delete_area(pos1, pos2)`
+    * delete all mapblocks in the area from pos1 to pos2, inclusive
 * `minetest.line_of_sight(pos1, pos2, stepsize)`: returns `boolean, pos`
     * Check if there is a direct line of sight between `pos1` and `pos2`
     * Returns the position of the blocking node when `false`
index df1057274f4fc335209fa675b46f978055ba3d76..62483c81b630e46f52b7a49f8d5a939f4cafc054 100644 (file)
@@ -59,6 +59,11 @@ std::string Database_Dummy::loadBlock(v3s16 blockpos)
                return "";
 }
 
+bool Database_Dummy::deleteBlock(v3s16 blockpos)
+{
+       m_database.erase(getBlockAsInteger(blockpos));
+}
+
 void Database_Dummy::listAllLoadableBlocks(std::list<v3s16> &dst)
 {
        for(std::map<u64, std::string>::iterator x = m_database.begin(); x != m_database.end(); ++x)
index e1c7b5b2d1884f9a901071e225ed887021f07795..a1535937d20ba8fef5116c018a1800e7be08b5c9 100644 (file)
@@ -35,6 +35,7 @@ public:
        virtual void endSave();
        virtual bool saveBlock(v3s16 blockpos, std::string &data);
        virtual std::string loadBlock(v3s16 blockpos);
+       virtual bool deleteBlock(v3s16 blockpos);
        virtual void listAllLoadableBlocks(std::list<v3s16> &dst);
        virtual int Initialized(void);
        ~Database_Dummy();
index 1681b0195bab69e91739138108711ce1194ed53c..de510e5336382f8e2d723add6df9762de4d7167d 100644 (file)
@@ -80,6 +80,19 @@ std::string Database_LevelDB::loadBlock(v3s16 blockpos)
                return "";
 }
 
+bool Database_LevelDB::deleteBlock(v3s16 blockpos)
+{
+       leveldb::Status status = m_database->Delete(leveldb::WriteOptions(),
+                       i64tos(getBlockAsInteger(blockpos)));
+       if (!status.ok()) {
+               errorstream << "WARNING: deleteBlock: LevelDB error deleting block "
+                       << PP(blockpos) << ": " << status.ToString() << std::endl;
+               return false;
+       }
+
+       return true;
+}
+
 void Database_LevelDB::listAllLoadableBlocks(std::list<v3s16> &dst)
 {
        leveldb::Iterator* it = m_database->NewIterator(leveldb::ReadOptions());
index ad165fcf2e8018e84ad8d2136ee235430a946e8d..c195260dac52b9c774b662e85c46422a3e66648b 100644 (file)
@@ -38,6 +38,7 @@ public:
        virtual void endSave();
        virtual bool saveBlock(v3s16 blockpos, std::string &data);
        virtual std::string loadBlock(v3s16 blockpos);
+       virtual bool deleteBlock(v3s16 blockpos);
        virtual void listAllLoadableBlocks(std::list<v3s16> &dst);
        virtual int Initialized(void);
        ~Database_LevelDB();
index 028a0ab4d8de990d74c95d00928f96b7b2c99bb3..b086f899dd396369362c26044a55a989de86b8b9 100644 (file)
@@ -123,6 +123,30 @@ std::string Database_Redis::loadBlock(v3s16 blockpos)
        return str;
 }
 
+bool Database_Redis::deleteBlock(v3s16 blockpos)
+{
+       std::string tmp = i64tos(getBlockAsInteger(blockpos));
+
+       redisReply *reply = (redisReply *)redisCommand(ctx, "HDEL %s %s",
+               hash.c_str(), tmp.c_str());
+       if (!reply) {
+               errorstream << "WARNING: deleteBlock: redis command 'HDEL' failed on "
+                       "block " << PP(blockpos) << ": " << ctx->errstr << std::endl;
+               freeReplyObject(reply);
+               return false;
+       }
+
+       if (reply->type == REDIS_REPLY_ERROR) {
+               errorstream << "WARNING: deleteBlock: deleting block " << PP(blockpos)
+                       << "failed" << std::endl;
+               freeReplyObject(reply);
+               return false;
+       }
+
+       freeReplyObject(reply);
+       return true;
+}
+
 void Database_Redis::listAllLoadableBlocks(std::list<v3s16> &dst)
 {
        redisReply *reply;
index cc33db7d4835eac55d296669f6c8b1ebbe38a500..34b90fa5909f7090a5207a92b91c69005d5c5f7c 100644 (file)
@@ -38,6 +38,7 @@ public:
        virtual void endSave();
        virtual bool saveBlock(v3s16 blockpos, std::string &data);
        virtual std::string loadBlock(v3s16 blockpos);
+       virtual bool deleteBlock(v3s16 blockpos);
        virtual void listAllLoadableBlocks(std::list<v3s16> &dst);
        virtual int Initialized(void);
        ~Database_Redis();
index 8e1501786b84294da68a6f1feb79015f2c7a18dd..7faffb01caa65626d9064ee03d14a822593e711b 100644 (file)
@@ -131,13 +131,11 @@ void Database_SQLite3::verifyDatabase() {
                throw FileNotGoodException("Cannot prepare write statement");
        }
 
-#ifdef __ANDROID__
        d = sqlite3_prepare(m_database, "DELETE FROM `blocks` WHERE `pos`=?;", -1, &m_database_delete, NULL);
        if(d != SQLITE_OK) {
                infostream<<"WARNING: SQLite3 database delete statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
                throw FileNotGoodException("Cannot prepare delete statement");
        }
-#endif
 
        d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
        if(d != SQLITE_OK) {
@@ -148,22 +146,48 @@ void Database_SQLite3::verifyDatabase() {
        infostream<<"ServerMap: SQLite3 database opened"<<std::endl;
 }
 
+bool Database_SQLite3::deleteBlock(v3s16 blockpos)
+{
+       verifyDatabase();
+
+       if (sqlite3_bind_int64(m_database_delete, 1,
+                       getBlockAsInteger(blockpos)) != SQLITE_OK) {
+               errorstream << "WARNING: Could not bind block position for delete: "
+                       << sqlite3_errmsg(m_database) << std::endl;
+       }
+
+       if (sqlite3_step(m_database_delete) != SQLITE_DONE) {
+               errorstream << "WARNING: deleteBlock: Block failed to delete "
+                       << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
+               sqlite3_reset(m_database_delete);
+               return false;
+       }
+
+       sqlite3_reset(m_database_delete);
+       return true;
+}
+
 bool Database_SQLite3::saveBlock(v3s16 blockpos, std::string &data)
 {
        verifyDatabase();
 
+       s64 bkey = getBlockAsInteger(blockpos);
+
 #ifdef __ANDROID__
        /**
         * Note: For some unknown reason sqlite3 fails to REPLACE blocks on android,
         * deleting them and inserting first works.
         */
-       if (sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
+       if (sqlite3_bind_int64(m_database_read, 1, bkey) != SQLITE_OK) {
                infostream << "WARNING: Could not bind block position for load: "
                        << sqlite3_errmsg(m_database)<<std::endl;
        }
 
-       if (sqlite3_step(m_database_read) == SQLITE_ROW) {
-               if (sqlite3_bind_int64(m_database_delete, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
+       int step_result = sqlite3_step(m_database_read);
+       sqlite3_reset(m_database_read);
+
+       if (step_result == SQLITE_ROW) {
+               if (sqlite3_bind_int64(m_database_delete, 1, bkey) != SQLITE_OK) {
                        infostream << "WARNING: Could not bind block position for delete: "
                                << sqlite3_errmsg(m_database)<<std::endl;
                }
@@ -175,17 +199,17 @@ bool Database_SQLite3::saveBlock(v3s16 blockpos, std::string &data)
                }
                sqlite3_reset(m_database_delete);
        }
-       sqlite3_reset(m_database_read);
 #endif
 
-       if (sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
+       if (sqlite3_bind_int64(m_database_write, 1, bkey) != SQLITE_OK) {
                errorstream << "WARNING: saveBlock: Block position failed to bind: "
                        << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
                sqlite3_reset(m_database_write);
                return false;
        }
 
-       if (sqlite3_bind_blob(m_database_write, 2, (void *) data.c_str(), data.size(), NULL) != SQLITE_OK) {
+       if (sqlite3_bind_blob(m_database_write, 2, (void *)data.c_str(),
+                       data.size(), NULL) != SQLITE_OK) {
                errorstream << "WARNING: saveBlock: Block data failed to bind: "
                        << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
                sqlite3_reset(m_database_write);
@@ -277,6 +301,7 @@ Database_SQLite3::~Database_SQLite3()
        FINALIZE_STATEMENT(m_database_read)
        FINALIZE_STATEMENT(m_database_write)
        FINALIZE_STATEMENT(m_database_list)
+       FINALIZE_STATEMENT(m_database_delete)
 
        if(m_database)
                rc = sqlite3_close(m_database);
index 45619b8852d63cfb47c05119b4dc06ac381127cf..5035c67c8d36fb93a6b326f52bea91c6d24de34c 100644 (file)
@@ -38,6 +38,7 @@ public:
 
        virtual bool saveBlock(v3s16 blockpos, std::string &data);
        virtual std::string loadBlock(v3s16 blockpos);
+       virtual bool deleteBlock(v3s16 blockpos);
        virtual void listAllLoadableBlocks(std::list<v3s16> &dst);
        virtual int Initialized(void);
        ~Database_SQLite3();
@@ -47,9 +48,7 @@ private:
        sqlite3 *m_database;
        sqlite3_stmt *m_database_read;
        sqlite3_stmt *m_database_write;
-#ifdef __ANDROID__
        sqlite3_stmt *m_database_delete;
-#endif
        sqlite3_stmt *m_database_list;
 
        // Create the database structure
index ffec7c977b56eaeec4c0fbabeef7146f649f8099..f04c4aa502c4fc2e8a08cd99f4aa0cd57ffd693e 100644 (file)
@@ -37,6 +37,7 @@ public:
 
        virtual bool saveBlock(v3s16 blockpos, std::string &data) = 0;
        virtual std::string loadBlock(v3s16 blockpos) = 0;
+       virtual bool deleteBlock(v3s16 blockpos) = 0;
        s64 getBlockAsInteger(const v3s16 pos) const;
        v3s16 getIntegerAsBlock(s64 i) const;
        virtual void listAllLoadableBlocks(std::list<v3s16> &dst) = 0;
index 48585a17039456bae65fd2425e734903cf45b582..fdc35558d342f1707f5383c4fbc121c557172b0b 100644 (file)
@@ -3588,6 +3588,23 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
        return getBlockNoCreateNoEx(blockpos);
 }
 
+bool ServerMap::deleteBlock(v3s16 blockpos)
+{
+       if (!dbase->deleteBlock(blockpos))
+               return false;
+
+       MapBlock *block = getBlockNoCreateNoEx(blockpos);
+       if (block) {
+               v2s16 p2d(blockpos.X, blockpos.Z);
+               MapSector *sector = getSectorNoGenerateNoEx(p2d);
+               if (!sector)
+                       return false;
+               sector->deleteBlock(block);
+       }
+
+       return true;
+}
+
 void ServerMap::PrintInfo(std::ostream &out)
 {
        out<<"ServerMap: ";
index 4e3f09a21b4b3b5f28f8aa089f0f95898411208c..3335d9026382f40c639fb22ba2bc3d2c25950342 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -267,9 +267,10 @@ public:
 
        virtual void save(ModifiedState save_level){assert(0);};
 
-       // Server implements this.
-       // Client leaves it as no-op.
+       // Server implements these.
+       // Client leaves them as no-op.
        virtual bool saveBlock(MapBlock *block) { return false; };
+       virtual bool deleteBlock(v3s16 blockpos) { return false; };
 
        /*
                Updates usage timers and unloads unused blocks and sectors.
@@ -423,7 +424,7 @@ public:
                - Create blank filled with CONTENT_IGNORE
 
        */
-       MapBlock * emergeBlock(v3s16 p, bool create_blank=true);
+       MapBlock *emergeBlock(v3s16 p, bool create_blank=true);
 
        /*
                Try to get a block.
@@ -498,6 +499,8 @@ public:
        // Database version
        void loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load=false);
 
+       bool deleteBlock(v3s16 blockpos);
+
        void updateVManip(v3s16 pos);
 
        // For debug printing
index c8c1ca0e179663e2e680cdded2b90cd325729f9e..cd5d253ac50716071d856b2ff58e45716a107345 100644 (file)
@@ -657,7 +657,8 @@ int ModApiEnvMod::l_clear_objects(lua_State *L)
 }
 
 // line_of_sight(pos1, pos2, stepsize) -> true/false, pos
-int ModApiEnvMod::l_line_of_sight(lua_State *L) {
+int ModApiEnvMod::l_line_of_sight(lua_State *L)
+{
        float stepsize = 1.0;
 
        GET_ENV_PTR;
@@ -681,6 +682,37 @@ int ModApiEnvMod::l_line_of_sight(lua_State *L) {
        return 1;
 }
 
+// delete_area(p1, p2)
+// delete mapblocks in area p1..p2
+int ModApiEnvMod::l_delete_area(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       v3s16 bpmin = getNodeBlockPos(read_v3s16(L, 1));
+       v3s16 bpmax = getNodeBlockPos(read_v3s16(L, 2));
+       sortBoxVerticies(bpmin, bpmax);
+
+       ServerMap &map = env->getServerMap();
+
+       MapEditEvent event;
+       event.type = MEET_OTHER;
+
+       bool success = true;
+       for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
+       for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
+       for (s16 x = bpmin.X; x <= bpmax.X; x++) {
+               v3s16 bp(x, y, z);
+               if (map.deleteBlock(bp))
+                       event.modified_blocks.insert(bp);
+               else
+                       success = false;
+       }
+
+       map.dispatchEvent(&event);
+       lua_pushboolean(L, success);
+       return 1;
+}
+
 // find_path(pos1, pos2, searchdistance,
 //     max_jump, max_drop, algorithm) -> table containing path
 int ModApiEnvMod::l_find_path(lua_State *L)
@@ -849,6 +881,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
        API_FCT(get_gametime);
        API_FCT(find_node_near);
        API_FCT(find_nodes_in_area);
+       API_FCT(delete_area);
        API_FCT(get_perlin);
        API_FCT(get_perlin_map);
        API_FCT(get_voxel_manip);
index 76c6594abb402f3ec3ff38cc189f40aa201a0222..bfaea1c4d92feae8770b91c4092a65aae5d0690a 100644 (file)
@@ -34,7 +34,7 @@ private:
        // remove_node(pos)
        // pos = {x=num, y=num, z=num}
        static int l_remove_node(lua_State *L);
-       
+
        // swap_node(pos, node)
        // pos = {x=num, y=num, z=num}
        static int l_swap_node(lua_State *L);
@@ -119,6 +119,9 @@ private:
        // nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
        static int l_find_nodes_in_area(lua_State *L);
 
+       // delete_area(p1, p2) -> true/false
+       static int l_delete_area(lua_State *L);
+
        // get_perlin(seeddiff, octaves, persistence, scale)
        // returns world-specific PerlinNoise
        static int l_get_perlin(lua_State *L);
@@ -126,11 +129,11 @@ private:
        // get_perlin_map(noiseparams, size)
        // returns world-specific PerlinNoiseMap
        static int l_get_perlin_map(lua_State *L);
-       
+
        // get_voxel_manip()
        // returns world-specific voxel manipulator
        static int l_get_voxel_manip(lua_State *L);
-       
+
        // clear_objects()
        // clear all objects in the environment
        static int l_clear_objects(lua_State *L);
@@ -151,11 +154,11 @@ private:
        // forceload_block(blockpos)
        // forceloads a block
        static int l_forceload_block(lua_State *L);
-       
+
        // forceload_free_block(blockpos)
        // stops forceloading a position
        static int l_forceload_free_block(lua_State *L);
-       
+
        // get us precision time
        static int l_get_us_time(lua_State *L);