Add ability to delete MapBlocks from map

Also add a Lua API and chatcommand for this
This commit is contained in:
kwolekr 2015-01-15 16:20:05 -05:00
parent 0330cec7ec
commit 9736548720
15 changed files with 187 additions and 19 deletions

@ -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",

@ -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`

@ -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)

@ -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();

@ -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());

@ -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();

@ -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;

@ -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();

@ -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);

@ -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

@ -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;

@ -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: ";

@ -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

@ -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);

@ -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);