From 9736548720a96c9c7f739edb0435d9ba4ad80652 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Thu, 15 Jan 2015 16:20:05 -0500 Subject: [PATCH] Add ability to delete MapBlocks from map Also add a Lua API and chatcommand for this --- builtin/game/chatcommands.lua | 40 ++++++++++++++++++++++++++++++++++ doc/lua_api.txt | 2 ++ src/database-dummy.cpp | 5 +++++ src/database-dummy.h | 1 + src/database-leveldb.cpp | 13 +++++++++++ src/database-leveldb.h | 1 + src/database-redis.cpp | 24 ++++++++++++++++++++ src/database-redis.h | 1 + src/database-sqlite3.cpp | 41 ++++++++++++++++++++++++++++------- src/database-sqlite3.h | 3 +-- src/database.h | 1 + src/map.cpp | 17 +++++++++++++++ src/map.h | 9 +++++--- src/script/lua_api/l_env.cpp | 35 +++++++++++++++++++++++++++++- src/script/lua_api/l_env.h | 13 ++++++----- 15 files changed, 187 insertions(+), 19 deletions(-) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 4f7b031aa..18b5dbe72 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -403,6 +403,46 @@ core.register_chatcommand("set", { end, }) +core.register_chatcommand("deleteblocks", { + params = "[here] [ ]", + 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", diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6d6625348..6359ef6de 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -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` diff --git a/src/database-dummy.cpp b/src/database-dummy.cpp index df1057274..62483c81b 100644 --- a/src/database-dummy.cpp +++ b/src/database-dummy.cpp @@ -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 &dst) { for(std::map::iterator x = m_database.begin(); x != m_database.end(); ++x) diff --git a/src/database-dummy.h b/src/database-dummy.h index e1c7b5b2d..a1535937d 100644 --- a/src/database-dummy.h +++ b/src/database-dummy.h @@ -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 &dst); virtual int Initialized(void); ~Database_Dummy(); diff --git a/src/database-leveldb.cpp b/src/database-leveldb.cpp index 1681b0195..de510e533 100644 --- a/src/database-leveldb.cpp +++ b/src/database-leveldb.cpp @@ -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 &dst) { leveldb::Iterator* it = m_database->NewIterator(leveldb::ReadOptions()); diff --git a/src/database-leveldb.h b/src/database-leveldb.h index ad165fcf2..c195260da 100644 --- a/src/database-leveldb.h +++ b/src/database-leveldb.h @@ -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 &dst); virtual int Initialized(void); ~Database_LevelDB(); diff --git a/src/database-redis.cpp b/src/database-redis.cpp index 028a0ab4d..b086f899d 100644 --- a/src/database-redis.cpp +++ b/src/database-redis.cpp @@ -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 &dst) { redisReply *reply; diff --git a/src/database-redis.h b/src/database-redis.h index cc33db7d4..34b90fa59 100644 --- a/src/database-redis.h +++ b/src/database-redis.h @@ -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 &dst); virtual int Initialized(void); ~Database_Redis(); diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp index 8e1501786..7faffb01c 100644 --- a/src/database-sqlite3.cpp +++ b/src/database-sqlite3.cpp @@ -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: "< &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 diff --git a/src/database.h b/src/database.h index ffec7c977..f04c4aa50 100644 --- a/src/database.h +++ b/src/database.h @@ -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 &dst) = 0; diff --git a/src/map.cpp b/src/map.cpp index 48585a170..fdc35558d 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -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: "; diff --git a/src/map.h b/src/map.h index 4e3f09a21..3335d9026 100644 --- 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 diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index c8c1ca0e1..cd5d253ac 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -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); diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 76c6594ab..bfaea1c4d 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -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);