aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkwolekr <kwolekr@minetest.net>2015-01-15 16:20:05 -0500
committerkwolekr <kwolekr@minetest.net>2015-01-15 16:48:56 -0500
commit9736548720a96c9c7f739edb0435d9ba4ad80652 (patch)
tree3f92b63d017e9baac042ed3837bbdf040d40c73f
parent0330cec7ec55126c9cc441373cde1253bed895ee (diff)
downloadminetest-9736548720a96c9c7f739edb0435d9ba4ad80652.tar.gz
minetest-9736548720a96c9c7f739edb0435d9ba4ad80652.tar.bz2
minetest-9736548720a96c9c7f739edb0435d9ba4ad80652.zip
Add ability to delete MapBlocks from map
Also add a Lua API and chatcommand for this
-rw-r--r--builtin/game/chatcommands.lua40
-rw-r--r--doc/lua_api.txt2
-rw-r--r--src/database-dummy.cpp5
-rw-r--r--src/database-dummy.h1
-rw-r--r--src/database-leveldb.cpp13
-rw-r--r--src/database-leveldb.h1
-rw-r--r--src/database-redis.cpp24
-rw-r--r--src/database-redis.h1
-rw-r--r--src/database-sqlite3.cpp41
-rw-r--r--src/database-sqlite3.h3
-rw-r--r--src/database.h1
-rw-r--r--src/map.cpp17
-rw-r--r--src/map.h9
-rw-r--r--src/script/lua_api/l_env.cpp35
-rw-r--r--src/script/lua_api/l_env.h13
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] [<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",
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<v3s16> &dst)
{
for(std::map<u64, std::string>::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<v3s16> &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<v3s16> &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<v3s16> &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<v3s16> &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<v3s16> &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: "<<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);
diff --git a/src/database-sqlite3.h b/src/database-sqlite3.h
index 45619b885..5035c67c8 100644
--- a/src/database-sqlite3.h
+++ b/src/database-sqlite3.h
@@ -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
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<v3s16> &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);