]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/script/lua_api/l_env.cpp
Add callback parameter for core.emerge_area()
[dragonfireclient.git] / src / script / lua_api / l_env.cpp
index 47bc9baf7bcc43858d7fa42b358cbf5c7c1467b1..084b1b4408fd8655bfa586e55f523f86fbf2575b 100644 (file)
@@ -17,42 +17,24 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
-#include "cpp_api/scriptapi.h"
-#include "lua_api/l_base.h"
 #include "lua_api/l_env.h"
+#include "lua_api/l_internal.h"
+#include "lua_api/l_nodemeta.h"
+#include "lua_api/l_nodetimer.h"
+#include "lua_api/l_noise.h"
 #include "lua_api/l_vmanip.h"
+#include "common/c_converter.h"
+#include "common/c_content.h"
+#include "scripting_game.h"
 #include "environment.h"
 #include "server.h"
+#include "nodedef.h"
 #include "daynightratio.h"
 #include "util/pointedthing.h"
 #include "content_sao.h"
-
-#include "common/c_converter.h"
-#include "common/c_content.h"
-#include "common/c_internal.h"
-#include "lua_api/l_nodemeta.h"
-#include "lua_api/l_nodetimer.h"
-#include "lua_api/l_noise.h"
 #include "treegen.h"
-#include "pathfinder.h"
 #include "emerge.h"
-#include "mapgen_v7.h"
-
-
-#define GET_ENV_PTR ServerEnvironment* env =                                   \
-                               dynamic_cast<ServerEnvironment*>(getEnv(L));                   \
-                               if( env == NULL) return 0
-                               
-struct EnumString ModApiEnvMod::es_MapgenObject[] =
-{
-       {MGOBJ_VMANIP,    "voxelmanip"},
-       {MGOBJ_HEIGHTMAP, "heightmap"},
-       {MGOBJ_BIOMEMAP,  "biomemap"},
-       {MGOBJ_HEATMAP,   "heatmap"},
-       {MGOBJ_HUMIDMAP,  "humiditymap"},
-       {0, NULL},
-};
-
+#include "pathfinder.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -60,40 +42,65 @@ struct EnumString ModApiEnvMod::es_MapgenObject[] =
 void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n,
                u32 active_object_count, u32 active_object_count_wider)
 {
-       ScriptApi* scriptIface = SERVER_TO_SA(env);
+       GameScripting *scriptIface = env->getScriptIface();
        scriptIface->realityCheck();
 
-       lua_StateL = scriptIface->getStack();
-       assert(lua_checkstack(L, 20));
+       lua_State *L = scriptIface->getStack();
+       sanity_check(lua_checkstack(L, 20));
        StackUnroller stack_unroller(L);
 
-       // Get minetest.registered_abms
-       lua_getglobal(L, "minetest");
+       int error_handler = PUSH_ERROR_HANDLER(L);
+
+       // Get registered_abms
+       lua_getglobal(L, "core");
        lua_getfield(L, -1, "registered_abms");
        luaL_checktype(L, -1, LUA_TTABLE);
-       int registered_abms = lua_gettop(L);
+       lua_remove(L, -2); // Remove core
 
-       // Get minetest.registered_abms[m_id]
+       // Get registered_abms[m_id]
        lua_pushnumber(L, m_id);
-       lua_gettable(L, registered_abms);
+       lua_gettable(L, -2);
        if(lua_isnil(L, -1))
-               assert(0);
+               FATAL_ERROR("");
+       lua_remove(L, -2); // Remove registered_abms
+
+       scriptIface->setOriginFromTable(-1);
 
        // Call action
        luaL_checktype(L, -1, LUA_TTABLE);
        lua_getfield(L, -1, "action");
        luaL_checktype(L, -1, LUA_TFUNCTION);
+       lua_remove(L, -2); // Remove registered_abms[m_id]
        push_v3s16(L, p);
        pushnode(L, n, env->getGameDef()->ndef());
        lua_pushnumber(L, active_object_count);
        lua_pushnumber(L, active_object_count_wider);
-       if(lua_pcall(L, 4, 0, 0))
-               script_error(L, "error: %s", lua_tostring(L, -1));
+
+       int result = lua_pcall(L, 4, 0, error_handler);
+       if (result)
+               scriptIface->scriptError(result, "LuaABM::trigger");
+
+       lua_pop(L, 1); // Pop error handler
+}
+
+void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param)
+{
+       ScriptCallbackState *state = (ScriptCallbackState *)param;
+       assert(state != NULL);
+       assert(state->script != NULL);
+       assert(state->refcount > 0);
+
+       state->refcount--;
+
+       state->script->on_emerge_area_completion(blockpos, action, state);
+
+       if (state->refcount == 0)
+               delete state;
 }
 
 // Exported functions
 
-// minetest.set_node(pos, node)
+// set_node(pos, node)
 // pos = {x=num, y=num, z=num}
 int ModApiEnvMod::l_set_node(lua_State *L)
 {
@@ -114,7 +121,7 @@ int ModApiEnvMod::l_add_node(lua_State *L)
        return l_set_node(L);
 }
 
-// minetest.remove_node(pos)
+// remove_node(pos)
 // pos = {x=num, y=num, z=num}
 int ModApiEnvMod::l_remove_node(lua_State *L)
 {
@@ -128,7 +135,23 @@ int ModApiEnvMod::l_remove_node(lua_State *L)
        return 1;
 }
 
-// minetest.get_node(pos)
+// swap_node(pos, node)
+// pos = {x=num, y=num, z=num}
+int ModApiEnvMod::l_swap_node(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       INodeDefManager *ndef = env->getGameDef()->ndef();
+       // parameters
+       v3s16 pos = read_v3s16(L, 1);
+       MapNode n = readnode(L, 2, ndef);
+       // Do it
+       bool succeeded = env->swapNode(pos, n);
+       lua_pushboolean(L, succeeded);
+       return 1;
+}
+
+// get_node(pos)
 // pos = {x=num, y=num, z=num}
 int ModApiEnvMod::l_get_node(lua_State *L)
 {
@@ -143,7 +166,7 @@ int ModApiEnvMod::l_get_node(lua_State *L)
        return 1;
 }
 
-// minetest.get_node_or_nil(pos)
+// get_node_or_nil(pos)
 // pos = {x=num, y=num, z=num}
 int ModApiEnvMod::l_get_node_or_nil(lua_State *L)
 {
@@ -152,19 +175,18 @@ int ModApiEnvMod::l_get_node_or_nil(lua_State *L)
        // pos
        v3s16 pos = read_v3s16(L, 1);
        // Do it
-       try{
-               MapNode n = env->getMap().getNode(pos);
+       bool pos_ok;
+       MapNode n = env->getMap().getNodeNoEx(pos, &pos_ok);
+       if (pos_ok) {
                // Return node
                pushnode(L, n, env->getGameDef()->ndef());
-               return 1;
-       } catch(InvalidPositionException &e)
-       {
+       } else {
                lua_pushnil(L);
-               return 1;
        }
+       return 1;
 }
 
-// minetest.get_node_light(pos, timeofday)
+// get_node_light(pos, timeofday)
 // pos = {x=num, y=num, z=num}
 // timeofday: nil = current time, 0 = night, 0.5 = day
 int ModApiEnvMod::l_get_node_light(lua_State *L)
@@ -178,26 +200,31 @@ int ModApiEnvMod::l_get_node_light(lua_State *L)
                time_of_day = 24000.0 * lua_tonumber(L, 2);
        time_of_day %= 24000;
        u32 dnr = time_to_daynight_ratio(time_of_day, true);
-       try{
-               MapNode n = env->getMap().getNode(pos);
+
+       bool is_position_ok;
+       MapNode n = env->getMap().getNodeNoEx(pos, &is_position_ok);
+       if (is_position_ok) {
                INodeDefManager *ndef = env->getGameDef()->ndef();
                lua_pushinteger(L, n.getLightBlend(dnr, ndef));
-               return 1;
-       } catch(InvalidPositionException &e)
-       {
+       } else {
                lua_pushnil(L);
-               return 1;
        }
+       return 1;
 }
 
-// minetest.place_node(pos, node)
+// place_node(pos, node)
 // pos = {x=num, y=num, z=num}
 int ModApiEnvMod::l_place_node(lua_State *L)
 {
        GET_ENV_PTR;
 
+       ScriptApiItem *scriptIfaceItem = getScriptApi<ScriptApiItem>(L);
+       Server *server = getServer(L);
+       INodeDefManager *ndef = server->ndef();
+       IItemDefManager *idef = server->idef();
+
        v3s16 pos = read_v3s16(L, 1);
-       MapNode n = readnode(L, 2, env->getGameDef()->ndef());
+       MapNode n = readnode(L, 2, ndef);
 
        // Don't attempt to load non-loaded area as of now
        MapNode n_old = env->getMap().getNodeNoEx(pos);
@@ -206,8 +233,6 @@ int ModApiEnvMod::l_place_node(lua_State *L)
                return 1;
        }
        // Create item to place
-       INodeDefManager *ndef = getServer(L)->ndef();
-       IItemDefManager *idef = getServer(L)->idef();
        ItemStack item(ndef->get(n).name, 1, 0, "", idef);
        // Make pointed position
        PointedThing pointed;
@@ -216,17 +241,19 @@ int ModApiEnvMod::l_place_node(lua_State *L)
        pointed.node_undersurface = pos + v3s16(0,-1,0);
        // Place it with a NULL placer (appears in Lua as a non-functional
        // ObjectRef)
-       bool success = get_scriptapi(L)->item_OnPlace(item, NULL, pointed);
+       bool success = scriptIfaceItem->item_OnPlace(item, NULL, pointed);
        lua_pushboolean(L, success);
        return 1;
 }
 
-// minetest.dig_node(pos)
+// dig_node(pos)
 // pos = {x=num, y=num, z=num}
 int ModApiEnvMod::l_dig_node(lua_State *L)
 {
        GET_ENV_PTR;
 
+       ScriptApiNode *scriptIfaceNode = getScriptApi<ScriptApiNode>(L);
+
        v3s16 pos = read_v3s16(L, 1);
 
        // Don't attempt to load non-loaded area as of now
@@ -237,17 +264,19 @@ int ModApiEnvMod::l_dig_node(lua_State *L)
        }
        // Dig it out with a NULL digger (appears in Lua as a
        // non-functional ObjectRef)
-       bool success = get_scriptapi(L)->node_on_dig(pos, n, NULL);
+       bool success = scriptIfaceNode->node_on_dig(pos, n, NULL);
        lua_pushboolean(L, success);
        return 1;
 }
 
-// minetest.punch_node(pos)
+// punch_node(pos)
 // pos = {x=num, y=num, z=num}
 int ModApiEnvMod::l_punch_node(lua_State *L)
 {
        GET_ENV_PTR;
 
+       ScriptApiNode *scriptIfaceNode = getScriptApi<ScriptApiNode>(L);
+
        v3s16 pos = read_v3s16(L, 1);
 
        // Don't attempt to load non-loaded area as of now
@@ -258,12 +287,12 @@ int ModApiEnvMod::l_punch_node(lua_State *L)
        }
        // Punch it with a NULL puncher (appears in Lua as a non-functional
        // ObjectRef)
-       bool success = get_scriptapi(L)->node_on_punch(pos, n, NULL);
+       bool success = scriptIfaceNode->node_on_punch(pos, n, NULL, PointedThing());
        lua_pushboolean(L, success);
        return 1;
 }
 
-// minetest.get_node_max_level(pos)
+// get_node_max_level(pos)
 // pos = {x=num, y=num, z=num}
 int ModApiEnvMod::l_get_node_max_level(lua_State *L)
 {
@@ -275,7 +304,7 @@ int ModApiEnvMod::l_get_node_max_level(lua_State *L)
        return 1;
 }
 
-// minetest.get_node_level(pos)
+// get_node_level(pos)
 // pos = {x=num, y=num, z=num}
 int ModApiEnvMod::l_get_node_level(lua_State *L)
 {
@@ -287,7 +316,7 @@ int ModApiEnvMod::l_get_node_level(lua_State *L)
        return 1;
 }
 
-// minetest.set_node_level(pos, level)
+// set_node_level(pos, level)
 // pos = {x=num, y=num, z=num}
 // level: 0..63
 int ModApiEnvMod::l_set_node_level(lua_State *L)
@@ -304,7 +333,7 @@ int ModApiEnvMod::l_set_node_level(lua_State *L)
        return 1;
 }
 
-// minetest.add_node_level(pos, level)
+// add_node_level(pos, level)
 // pos = {x=num, y=num, z=num}
 // level: 0..63
 int ModApiEnvMod::l_add_node_level(lua_State *L)
@@ -321,8 +350,24 @@ int ModApiEnvMod::l_add_node_level(lua_State *L)
        return 1;
 }
 
+// find_nodes_with_meta(pos1, pos2)
+int ModApiEnvMod::l_find_nodes_with_meta(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       std::vector<v3s16> positions = env->getMap().findNodesWithMetadata(
+               check_v3s16(L, 1), check_v3s16(L, 2));
+
+       lua_newtable(L);
+       for (size_t i = 0; i != positions.size(); i++) {
+               push_v3s16(L, positions[i]);
+               lua_rawseti(L, -2, i + 1);
+       }
+
+       return 1;
+}
 
-// minetest.get_meta(pos)
+// get_meta(pos)
 int ModApiEnvMod::l_get_meta(lua_State *L)
 {
        GET_ENV_PTR;
@@ -333,7 +378,7 @@ int ModApiEnvMod::l_get_meta(lua_State *L)
        return 1;
 }
 
-// minetest.get_node_timer(pos)
+// get_node_timer(pos)
 int ModApiEnvMod::l_get_node_timer(lua_State *L)
 {
        GET_ENV_PTR;
@@ -344,7 +389,7 @@ int ModApiEnvMod::l_get_node_timer(lua_State *L)
        return 1;
 }
 
-// minetest.add_entity(pos, entityname) -> ObjectRef or nil
+// add_entity(pos, entityname) -> ObjectRef or nil
 // pos = {x=num, y=num, z=num}
 int ModApiEnvMod::l_add_entity(lua_State *L)
 {
@@ -361,11 +406,11 @@ int ModApiEnvMod::l_add_entity(lua_State *L)
        if(objectid == 0)
                return 0;
        // Return ObjectRef
-       get_scriptapi(L)->objectrefGetOrCreate(obj);
+       getScriptApiBase(L)->objectrefGetOrCreate(L, obj);
        return 1;
 }
 
-// minetest.add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil
+// add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil
 // pos = {x=num, y=num, z=num}
 int ModApiEnvMod::l_add_item(lua_State *L)
 {
@@ -377,32 +422,25 @@ int ModApiEnvMod::l_add_item(lua_State *L)
        ItemStack item = read_item(L, 2,getServer(L));
        if(item.empty() || !item.isKnown(getServer(L)->idef()))
                return 0;
-       // Use minetest.spawn_item to spawn a __builtin:item
-       lua_getglobal(L, "minetest");
+
+       int error_handler = PUSH_ERROR_HANDLER(L);
+
+       // Use spawn_item to spawn a __builtin:item
+       lua_getglobal(L, "core");
        lua_getfield(L, -1, "spawn_item");
+       lua_remove(L, -2); // Remove core
        if(lua_isnil(L, -1))
                return 0;
        lua_pushvalue(L, 1);
        lua_pushstring(L, item.getItemString().c_str());
-       if(lua_pcall(L, 2, 1, 0))
-               script_error(L, "error: %s", lua_tostring(L, -1));
+
+       PCALL_RESL(L, lua_pcall(L, 2, 1, error_handler));
+
+       lua_remove(L, error_handler);
        return 1;
-       /*lua_pushvalue(L, 1);
-       lua_pushstring(L, "__builtin:item");
-       lua_pushstring(L, item.getItemString().c_str());
-       return l_add_entity(L);*/
-       /*// Do it
-       ServerActiveObject *obj = createItemSAO(env, pos, item.getItemString());
-       int objectid = env->addActiveObject(obj);
-       // If failed to add, return nothing (reads as nil)
-       if(objectid == 0)
-               return 0;
-       // Return ObjectRef
-       objectrefGetOrCreate(L, obj);
-       return 1;*/
 }
 
-// minetest.get_player_by_name(name)
+// get_player_by_name(name)
 int ModApiEnvMod::l_get_player_by_name(lua_State *L)
 {
        GET_ENV_PTR;
@@ -420,40 +458,33 @@ int ModApiEnvMod::l_get_player_by_name(lua_State *L)
                return 1;
        }
        // Put player on stack
-       get_scriptapi(L)->objectrefGetOrCreate(sao);
+       getScriptApiBase(L)->objectrefGetOrCreate(L, sao);
        return 1;
 }
 
-// minetest.get_objects_inside_radius(pos, radius)
+// get_objects_inside_radius(pos, radius)
 int ModApiEnvMod::l_get_objects_inside_radius(lua_State *L)
 {
-       // Get the table insert function
-       lua_getglobal(L, "table");
-       lua_getfield(L, -1, "insert");
-       int table_insert = lua_gettop(L);
-
        GET_ENV_PTR;
 
        // Do it
        v3f pos = checkFloatPos(L, 1);
        float radius = luaL_checknumber(L, 2) * BS;
-       std::set<u16> ids = env->getObjectsInsideRadius(pos, radius);
-       lua_newtable(L);
-       int table = lua_gettop(L);
-       for(std::set<u16>::const_iterator
-                       i = ids.begin(); i != ids.end(); i++){
-               ServerActiveObject *obj = env->getActiveObject(*i);
+       std::vector<u16> ids;
+       env->getObjectsInsideRadius(ids, pos, radius);
+       ScriptApiBase *script = getScriptApiBase(L);
+       lua_createtable(L, ids.size(), 0);
+       std::vector<u16>::const_iterator iter = ids.begin();
+       for(u32 i = 0; iter != ids.end(); iter++) {
+               ServerActiveObject *obj = env->getActiveObject(*iter);
                // Insert object reference into table
-               lua_pushvalue(L, table_insert);
-               lua_pushvalue(L, table);
-               get_scriptapi(L)->objectrefGetOrCreate(obj);
-               if(lua_pcall(L, 2, 0, 0))
-                       script_error(L, "error: %s", lua_tostring(L, -1));
+               script->objectrefGetOrCreate(L, obj);
+               lua_rawseti(L, -2, ++i);
        }
        return 1;
 }
 
-// minetest.set_timeofday(val)
+// set_timeofday(val)
 // val = 0...1
 int ModApiEnvMod::l_set_timeofday(lua_State *L)
 {
@@ -461,7 +492,7 @@ int ModApiEnvMod::l_set_timeofday(lua_State *L)
 
        // Do it
        float timeofday_f = luaL_checknumber(L, 1);
-       assert(timeofday_f >= 0.0 && timeofday_f <= 1.0);
+       sanity_check(timeofday_f >= 0.0 && timeofday_f <= 1.0);
        int timeofday_mh = (int)(timeofday_f * 24000.0);
        // This should be set directly in the environment but currently
        // such changes aren't immediately sent to the clients, so call
@@ -471,7 +502,7 @@ int ModApiEnvMod::l_set_timeofday(lua_State *L)
        return 0;
 }
 
-// minetest.get_timeofday() -> 0...1
+// get_timeofday() -> 0...1
 int ModApiEnvMod::l_get_timeofday(lua_State *L)
 {
        GET_ENV_PTR;
@@ -483,8 +514,18 @@ int ModApiEnvMod::l_get_timeofday(lua_State *L)
        return 1;
 }
 
+// get_gametime()
+int ModApiEnvMod::l_get_gametime(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       int game_time = env->getGameTime();
+       lua_pushnumber(L, game_time);
+       return 1;
+}
+
 
-// minetest.find_node_near(pos, radius, nodenames) -> pos or nil
+// find_node_near(pos, radius, nodenames) -> pos or nil
 // nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
 int ModApiEnvMod::l_find_node_near(lua_State *L)
 {
@@ -509,9 +550,8 @@ int ModApiEnvMod::l_find_node_near(lua_State *L)
        }
 
        for(int d=1; d<=radius; d++){
-               std::list<v3s16> list;
-               getFacePositions(list, d);
-               for(std::list<v3s16>::iterator i = list.begin();
+               std::vector<v3s16> list = FacePositionCache::getFacePositions(d);
+               for(std::vector<v3s16>::iterator i = list.begin();
                                i != list.end(); ++i){
                        v3s16 p = pos + (*i);
                        content_t c = env->getMap().getNodeNoEx(p).getContent();
@@ -524,7 +564,7 @@ int ModApiEnvMod::l_find_node_near(lua_State *L)
        return 0;
 }
 
-// minetest.find_nodes_in_area(minp, maxp, nodenames) -> list of positions
+// find_nodes_in_area(minp, maxp, nodenames) -> list of positions
 // nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
 int ModApiEnvMod::l_find_nodes_in_area(lua_State *L)
 {
@@ -534,260 +574,274 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L)
        v3s16 minp = read_v3s16(L, 1);
        v3s16 maxp = read_v3s16(L, 2);
        std::set<content_t> filter;
-       if(lua_istable(L, 3)){
+       if(lua_istable(L, 3)) {
                int table = 3;
                lua_pushnil(L);
-               while(lua_next(L, table) != 0){
+               while(lua_next(L, table) != 0) {
                        // key at index -2 and value at index -1
                        luaL_checktype(L, -1, LUA_TSTRING);
                        ndef->getIds(lua_tostring(L, -1), filter);
                        // removes value, keeps key for next iteration
                        lua_pop(L, 1);
                }
-       } else if(lua_isstring(L, 3)){
+       } else if(lua_isstring(L, 3)) {
                ndef->getIds(lua_tostring(L, 3), filter);
        }
 
-       // Get the table insert function
-       lua_getglobal(L, "table");
-       lua_getfield(L, -1, "insert");
-       int table_insert = lua_gettop(L);
+       std::map<content_t, u16> individual_count;
 
        lua_newtable(L);
-       int table = lua_gettop(L);
-       for(s16 x=minp.X; x<=maxp.X; x++)
-       for(s16 y=minp.Y; y<=maxp.Y; y++)
-       for(s16 z=minp.Z; z<=maxp.Z; z++)
-       {
-               v3s16 p(x,y,z);
+       u64 i = 0;
+       for (s16 x = minp.X; x <= maxp.X; x++)
+               for (s16 y = minp.Y; y <= maxp.Y; y++)
+                       for (s16 z = minp.Z; z <= maxp.Z; z++) {
+                               v3s16 p(x, y, z);
+                               content_t c = env->getMap().getNodeNoEx(p).getContent();
+                               if (filter.count(c) != 0) {
+                                       push_v3s16(L, p);
+                                       lua_rawseti(L, -2, ++i);
+                                       individual_count[c]++;
+                               }
+       }
+       lua_newtable(L);
+       for (std::set<content_t>::iterator it = filter.begin();
+                       it != filter.end(); ++it) {
+               lua_pushnumber(L, individual_count[*it]);
+               lua_setfield(L, -2, ndef->get(*it).name.c_str());
+       }
+       return 2;
+}
+
+// find_nodes_in_area_under_air(minp, maxp, nodenames) -> list of positions
+// nodenames: e.g. {"ignore", "group:tree"} or "default:dirt"
+int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L)
+{
+       /* Note: A similar but generalized (and therefore slower) version of this
+        * function could be created -- e.g. find_nodes_in_area_under -- which
+        * would accept a node name (or ID?) or list of names that the "above node"
+        * should be.
+        * TODO
+        */
+
+       GET_ENV_PTR;
+
+       INodeDefManager *ndef = getServer(L)->ndef();
+       v3s16 minp = read_v3s16(L, 1);
+       v3s16 maxp = read_v3s16(L, 2);
+       std::set<content_t> filter;
+
+       if (lua_istable(L, 3)) {
+               int table = 3;
+               lua_pushnil(L);
+               while(lua_next(L, table) != 0) {
+                       // key at index -2 and value at index -1
+                       luaL_checktype(L, -1, LUA_TSTRING);
+                       ndef->getIds(lua_tostring(L, -1), filter);
+                       // removes value, keeps key for next iteration
+                       lua_pop(L, 1);
+               }
+       } else if (lua_isstring(L, 3)) {
+               ndef->getIds(lua_tostring(L, 3), filter);
+       }
+
+       lua_newtable(L);
+       u64 i = 0;
+       for (s16 x = minp.X; x <= maxp.X; x++)
+       for (s16 z = minp.Z; z <= maxp.Z; z++) {
+               s16 y = minp.Y;
+               v3s16 p(x, y, z);
                content_t c = env->getMap().getNodeNoEx(p).getContent();
-               if(filter.count(c) != 0){
-                       lua_pushvalue(L, table_insert);
-                       lua_pushvalue(L, table);
-                       push_v3s16(L, p);
-                       if(lua_pcall(L, 2, 0, 0))
-                               script_error(L, "error: %s", lua_tostring(L, -1));
+               for (; y <= maxp.Y; y++) {
+                       v3s16 psurf(x, y + 1, z);
+                       content_t csurf = env->getMap().getNodeNoEx(psurf).getContent();
+                       if(c != CONTENT_AIR && csurf == CONTENT_AIR &&
+                                       filter.count(c) != 0) {
+                               push_v3s16(L, v3s16(x, y, z));
+                               lua_rawseti(L, -2, ++i);
+                       }
+                       c = csurf;
                }
        }
        return 1;
 }
 
-// minetest.get_perlin(seeddiff, octaves, persistence, scale)
+// get_perlin(seeddiff, octaves, persistence, scale)
 // returns world-specific PerlinNoise
 int ModApiEnvMod::l_get_perlin(lua_State *L)
 {
-       GET_ENV_PTR;
+       GET_ENV_PTR_NO_MAP_LOCK;
+
+       NoiseParams params;
 
-       int seeddiff = luaL_checkint(L, 1);
-       int octaves = luaL_checkint(L, 2);
-       float persistence = luaL_checknumber(L, 3);
-       float scale = luaL_checknumber(L, 4);
+       if (lua_istable(L, 1)) {
+               read_noiseparams(L, 1, &params);
+       } else {
+               params.seed    = luaL_checkint(L, 1);
+               params.octaves = luaL_checkint(L, 2);
+               params.persist = luaL_checknumber(L, 3);
+               params.spread  = v3f(1, 1, 1) * luaL_checknumber(L, 4);
+       }
+
+       params.seed += (int)env->getServerMap().getSeed();
 
-       LuaPerlinNoise *n = new LuaPerlinNoise(seeddiff + int(env->getServerMap().getSeed()), octaves, persistence, scale);
+       LuaPerlinNoise *n = new LuaPerlinNoise(&params);
        *(void **)(lua_newuserdata(L, sizeof(void *))) = n;
        luaL_getmetatable(L, "PerlinNoise");
        lua_setmetatable(L, -2);
        return 1;
 }
 
-// minetest.get_perlin_map(noiseparams, size)
+// get_perlin_map(noiseparams, size)
 // returns world-specific PerlinNoiseMap
 int ModApiEnvMod::l_get_perlin_map(lua_State *L)
 {
-       GET_ENV_PTR;
+       GET_ENV_PTR_NO_MAP_LOCK;
 
-       NoiseParams *np = read_noiseparams(L, 1);
-       if (!np)
+       NoiseParams np;
+       if (!read_noiseparams(L, 1, &np))
                return 0;
        v3s16 size = read_v3s16(L, 2);
 
        int seed = (int)(env->getServerMap().getSeed());
-       LuaPerlinNoiseMap *n = new LuaPerlinNoiseMap(np, seed, size);
+       LuaPerlinNoiseMap *n = new LuaPerlinNoiseMap(&np, seed, size);
        *(void **)(lua_newuserdata(L, sizeof(void *))) = n;
        luaL_getmetatable(L, "PerlinNoiseMap");
        lua_setmetatable(L, -2);
        return 1;
 }
 
-// minetest.get_voxel_manip()
+// get_voxel_manip()
 // returns voxel manipulator
 int ModApiEnvMod::l_get_voxel_manip(lua_State *L)
 {
        GET_ENV_PTR;
 
        Map *map = &(env->getMap());
-       LuaVoxelManip *o = new LuaVoxelManip(map);
-       
+       LuaVoxelManip *o = (lua_istable(L, 1) && lua_istable(L, 2)) ?
+               new LuaVoxelManip(map, read_v3s16(L, 1), read_v3s16(L, 2)) :
+               new LuaVoxelManip(map);
+
        *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
        luaL_getmetatable(L, "VoxelManip");
        lua_setmetatable(L, -2);
        return 1;
 }
 
-// minetest.get_mapgen_object(objectname)
-// returns the requested object used during map generation
-int ModApiEnvMod::l_get_mapgen_object(lua_State *L)
+// clear_objects()
+// clear all objects in the environment
+int ModApiEnvMod::l_clear_objects(lua_State *L)
 {
-       const char *mgobjstr = lua_tostring(L, 1);
-       
-       int mgobjint;
-       if (!string_to_enum(es_MapgenObject, mgobjint, mgobjstr ? mgobjstr : ""))
-               return 0;
-               
-       enum MapgenObject mgobj = (MapgenObject)mgobjint;
+       GET_ENV_PTR;
 
-       EmergeManager *emerge = getServer(L)->getEmergeManager();
-       Mapgen *mg = emerge->getCurrentMapgen();
-       if (!mg)
-               return 0;
-       
-       size_t maplen = mg->csize.X * mg->csize.Z;
-       
-       int nargs = 1;
-       
-       switch (mgobj) {
-               case MGOBJ_VMANIP: {
-                       ManualMapVoxelManipulator *vm = mg->vm;
-                       
-                       // VoxelManip object
-                       LuaVoxelManip *o = new LuaVoxelManip(vm, true);
-                       *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
-                       luaL_getmetatable(L, "VoxelManip");
-                       lua_setmetatable(L, -2);
-                       
-                       // emerged min pos
-                       push_v3s16(L, vm->m_area.MinEdge);
-
-                       // emerged max pos
-                       push_v3s16(L, vm->m_area.MaxEdge);
-                       
-                       nargs = 3;
-                       
-                       break; }
-               case MGOBJ_HEIGHTMAP: {
-                       if (!mg->heightmap)
-                               return 0;
-                       
-                       lua_newtable(L);
-                       for (size_t i = 0; i != maplen; i++) {
-                               lua_pushinteger(L, mg->heightmap[i]);
-                               lua_rawseti(L, -2, i + 1);
-                       }
-                       break; }
-               case MGOBJ_BIOMEMAP: {
-                       if (!mg->biomemap)
-                               return 0;
-                       
-                       lua_newtable(L);
-                       for (size_t i = 0; i != maplen; i++) {
-                               lua_pushinteger(L, mg->biomemap[i]);
-                               lua_rawseti(L, -2, i + 1);
-                       }
-                       break; }
-               case MGOBJ_HEATMAP: { // Mapgen V7 specific objects
-               case MGOBJ_HUMIDMAP:
-                       if (strcmp(emerge->params->mg_name.c_str(), "v7"))
-                               return 0;
-                       
-                       MapgenV7 *mgv7 = (MapgenV7 *)mg;
-
-                       float *arr = (mgobj == MGOBJ_HEATMAP) ? 
-                               mgv7->noise_heat->result : mgv7->noise_humidity->result;
-                       if (!arr)
-                               return 0;
-                       
-                       lua_newtable(L);
-                       for (size_t i = 0; i != maplen; i++) {
-                               lua_pushnumber(L, arr[i]);
-                               lua_rawseti(L, -2, i + 1);
-                       }
-                       break; }
-       }
-       
-       return nargs;
+       env->clearAllObjects();
+       return 0;
 }
 
-// minetest.set_mapgen_params(params)
-// set mapgen parameters
-int ModApiEnvMod::l_set_mapgen_params(lua_State *L)
+// line_of_sight(pos1, pos2, stepsize) -> true/false, pos
+int ModApiEnvMod::l_line_of_sight(lua_State *L)
 {
-       if (!lua_istable(L, 1))
-               return 0;
+       float stepsize = 1.0;
 
-       EmergeManager *emerge = getServer(L)->getEmergeManager();
-       if (emerge->mapgen.size())
-               return 0;
-       
-       MapgenParams *oparams = new MapgenParams;
-       u32 paramsmodified = 0;
-       u32 flagmask = 0;
-       
-       lua_getfield(L, 1, "mgname");
-       if (lua_isstring(L, -1)) {
-               oparams->mg_name = std::string(lua_tostring(L, -1));
-               paramsmodified |= MGPARAMS_SET_MGNAME;
+       GET_ENV_PTR;
+
+       // read position 1 from lua
+       v3f pos1 = checkFloatPos(L, 1);
+       // read position 2 from lua
+       v3f pos2 = checkFloatPos(L, 2);
+       //read step size from lua
+       if (lua_isnumber(L, 3)) {
+               stepsize = lua_tonumber(L, 3);
        }
-       
-       lua_getfield(L, 1, "seed");
-       if (lua_isnumber(L, -1)) {
-               oparams->seed = lua_tointeger(L, -1);
-               paramsmodified |= MGPARAMS_SET_SEED;
+
+       v3s16 p;
+       bool success = env->line_of_sight(pos1, pos2, stepsize, &p);
+       lua_pushboolean(L, success);
+       if (!success) {
+               push_v3s16(L, p);
+               return 2;
        }
-       
-       lua_getfield(L, 1, "water_level");
-       if (lua_isnumber(L, -1)) {
-               oparams->water_level = lua_tointeger(L, -1);
-               paramsmodified |= MGPARAMS_SET_WATER_LEVEL;
+       return 1;
+}
+
+// emerge_area(p1, p2, [callback, context])
+// emerge mapblocks in area p1..p2, calls callback with context upon completion
+int ModApiEnvMod::l_emerge_area(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       EmergeCompletionCallback callback = NULL;
+       ScriptCallbackState *state = NULL;
+
+       EmergeManager *emerge = getServer(L)->getEmergeManager();
+
+       v3s16 bpmin = getNodeBlockPos(read_v3s16(L, 1));
+       v3s16 bpmax = getNodeBlockPos(read_v3s16(L, 2));
+       sortBoxVerticies(bpmin, bpmax);
+
+       size_t num_blocks = VoxelArea(bpmin, bpmax).getVolume();
+       assert(num_blocks != 0);
+
+       if (lua_isfunction(L, 3)) {
+               callback = LuaEmergeAreaCallback;
+
+               lua_pushvalue(L, 3);
+               int callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+               lua_pushvalue(L, 4);
+               int args_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+               state = new ScriptCallbackState;
+               state->script       = getServer(L)->getScriptIface();
+               state->callback_ref = callback_ref;
+               state->args_ref     = args_ref;
+               state->refcount     = num_blocks;
+               state->origin       = getScriptApiBase(L)->getOrigin();
        }
 
-       lua_getfield(L, 1, "flags");
-       if (lua_isstring(L, -1)) {
-               std::string flagstr = std::string(lua_tostring(L, -1));
-               oparams->flags = readFlagString(flagstr, flagdesc_mapgen);
-               paramsmodified |= MGPARAMS_SET_FLAGS;
-       
-               lua_getfield(L, 1, "flagmask");
-               if (lua_isstring(L, -1)) {
-                       flagstr = std::string(lua_tostring(L, -1));
-                       flagmask = readFlagString(flagstr, flagdesc_mapgen);
-               }
+       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++) {
+               emerge->enqueueBlockEmergeEx(v3s16(x, y, z), PEER_ID_INEXISTENT,
+                       BLOCK_EMERGE_ALLOW_GEN | BLOCK_EMERGE_FORCE_QUEUE, callback, state);
        }
-       
-       emerge->luaoverride_params          = oparams;
-       emerge->luaoverride_params_modified = paramsmodified;
-       emerge->luaoverride_flagmask        = flagmask;
-       
+
        return 0;
 }
 
-// minetest.clear_objects()
-// clear all objects in the environment
-int ModApiEnvMod::l_clear_objects(lua_State *L)
+// delete_area(p1, p2)
+// delete mapblocks in area p1..p2
+int ModApiEnvMod::l_delete_area(lua_State *L)
 {
        GET_ENV_PTR;
 
-       env->clearAllObjects();
-       return 0;
-}
+       v3s16 bpmin = getNodeBlockPos(read_v3s16(L, 1));
+       v3s16 bpmax = getNodeBlockPos(read_v3s16(L, 2));
+       sortBoxVerticies(bpmin, bpmax);
 
-// minetest.line_of_sight(pos1, pos2, stepsize) -> true/false
-int ModApiEnvMod::l_line_of_sight(lua_State *L) {
-       float stepsize = 1.0;
+       ServerMap &map = env->getServerMap();
 
-       GET_ENV_PTR;
+       MapEditEvent event;
+       event.type = MEET_OTHER;
 
-       // read position 1 from lua
-       v3f pos1 = checkFloatPos(L, 1);
-       // read position 2 from lua
-       v3f pos2 = checkFloatPos(L, 2);
-       //read step size from lua
-       if (lua_isnumber(L, 3))
-               stepsize = lua_tonumber(L, 3);
+       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)) {
+                       env->setStaticForActiveObjectsInBlock(bp, false);
+                       event.modified_blocks.insert(bp);
+               } else {
+                       success = false;
+               }
+       }
 
-       return (env->line_of_sight(pos1,pos2,stepsize));
+       map.dispatchEvent(&event);
+       lua_pushboolean(L, success);
+       return 1;
 }
 
-// minetest.find_path(pos1, pos2, searchdistance,
+// find_path(pos1, pos2, searchdistance,
 //     max_jump, max_drop, algorithm) -> table containing path
 int ModApiEnvMod::l_find_path(lua_State *L)
 {
@@ -830,7 +884,7 @@ int ModApiEnvMod::l_find_path(lua_State *L)
        return 0;
 }
 
-// minetest.spawn_tree(pos, treedef)
+// spawn_tree(pos, treedef)
 int ModApiEnvMod::l_spawn_tree(lua_State *L)
 {
        GET_ENV_PTR;
@@ -861,7 +915,8 @@ int ModApiEnvMod::l_spawn_tree(lua_State *L)
                }
                getintfield(L, 2, "angle", tree_def.angle);
                getintfield(L, 2, "iterations", tree_def.iterations);
-               getintfield(L, 2, "random_level", tree_def.iterations_random_level);
+               if (!getintfield(L, 2, "random_level", tree_def.iterations_random_level))
+                       tree_def.iterations_random_level = 0;
                getstringfield(L, 2, "trunk_type", tree_def.trunk_type);
                getboolfield(L, 2, "thin_branches", tree_def.thin_branches);
                tree_def.fruit_chance=0;
@@ -871,16 +926,24 @@ int ModApiEnvMod::l_spawn_tree(lua_State *L)
                        tree_def.fruitnode=ndef->getId(fruit);
                        getintfield(L, 2, "fruit_chance",tree_def.fruit_chance);
                }
-               getintfield(L, 2, "seed", tree_def.seed);
+               tree_def.explicit_seed = getintfield(L, 2, "seed", tree_def.seed);
        }
        else
                return 0;
-       treegen::spawn_ltree (env, p0, ndef, tree_def);
+
+       treegen::error e;
+       if ((e = treegen::spawn_ltree (env, p0, ndef, tree_def)) != treegen::SUCCESS) {
+               if (e == treegen::UNBALANCED_BRACKETS) {
+                       luaL_error(L, "spawn_tree(): closing ']' has no matching opening bracket");
+               } else {
+                       luaL_error(L, "spawn_tree(): unknown error");
+               }
+       }
+
        return 1;
 }
 
-
-// minetest.transforming_liquid_add(pos)
+// transforming_liquid_add(pos)
 int ModApiEnvMod::l_transforming_liquid_add(lua_State *L)
 {
        GET_ENV_PTR;
@@ -890,71 +953,67 @@ int ModApiEnvMod::l_transforming_liquid_add(lua_State *L)
        return 1;
 }
 
-// minetest.get_heat(pos)
-// pos = {x=num, y=num, z=num}
-int ModApiEnvMod::l_get_heat(lua_State *L)
+// forceload_block(blockpos)
+// blockpos = {x=num, y=num, z=num}
+int ModApiEnvMod::l_forceload_block(lua_State *L)
 {
        GET_ENV_PTR;
 
-       v3s16 pos = read_v3s16(L, 1);
-       lua_pushnumber(L, env->getServerMap().getHeat(env, pos));
-       return 1;
+       v3s16 blockpos = read_v3s16(L, 1);
+       env->getForceloadedBlocks()->insert(blockpos);
+       return 0;
 }
 
-// minetest.get_humidity(pos)
-// pos = {x=num, y=num, z=num}
-int ModApiEnvMod::l_get_humidity(lua_State *L)
+// forceload_free_block(blockpos)
+// blockpos = {x=num, y=num, z=num}
+int ModApiEnvMod::l_forceload_free_block(lua_State *L)
 {
        GET_ENV_PTR;
 
-       v3s16 pos = read_v3s16(L, 1);
-       lua_pushnumber(L, env->getServerMap().getHumidity(env, pos));
-       return 1;
+       v3s16 blockpos = read_v3s16(L, 1);
+       env->getForceloadedBlocks()->erase(blockpos);
+       return 0;
 }
 
-
-bool ModApiEnvMod::Initialize(lua_State *L,int top)
-{
-
-       bool retval = true;
-
-       retval &= API_FCT(set_node);
-       retval &= API_FCT(add_node);
-       retval &= API_FCT(add_item);
-       retval &= API_FCT(remove_node);
-       retval &= API_FCT(get_node);
-       retval &= API_FCT(get_node_or_nil);
-       retval &= API_FCT(get_node_light);
-       retval &= API_FCT(place_node);
-       retval &= API_FCT(dig_node);
-       retval &= API_FCT(punch_node);
-       retval &= API_FCT(get_node_max_level);
-       retval &= API_FCT(get_node_level);
-       retval &= API_FCT(set_node_level);
-       retval &= API_FCT(add_node_level);
-       retval &= API_FCT(add_entity);
-       retval &= API_FCT(get_meta);
-       retval &= API_FCT(get_node_timer);
-       retval &= API_FCT(get_player_by_name);
-       retval &= API_FCT(get_objects_inside_radius);
-       retval &= API_FCT(set_timeofday);
-       retval &= API_FCT(get_timeofday);
-       retval &= API_FCT(find_node_near);
-       retval &= API_FCT(find_nodes_in_area);
-       retval &= API_FCT(get_perlin);
-       retval &= API_FCT(get_perlin_map);
-       retval &= API_FCT(get_voxel_manip);
-       retval &= API_FCT(get_mapgen_object);
-       retval &= API_FCT(set_mapgen_params);
-       retval &= API_FCT(clear_objects);
-       retval &= API_FCT(spawn_tree);
-       retval &= API_FCT(find_path);
-       retval &= API_FCT(line_of_sight);
-       retval &= API_FCT(transforming_liquid_add);
-       retval &= API_FCT(get_heat);
-       retval &= API_FCT(get_humidity);
-
-       return retval;
-}
-
-ModApiEnvMod modapienv_prototype;
+void ModApiEnvMod::Initialize(lua_State *L, int top)
+{
+       API_FCT(set_node);
+       API_FCT(add_node);
+       API_FCT(swap_node);
+       API_FCT(add_item);
+       API_FCT(remove_node);
+       API_FCT(get_node);
+       API_FCT(get_node_or_nil);
+       API_FCT(get_node_light);
+       API_FCT(place_node);
+       API_FCT(dig_node);
+       API_FCT(punch_node);
+       API_FCT(get_node_max_level);
+       API_FCT(get_node_level);
+       API_FCT(set_node_level);
+       API_FCT(add_node_level);
+       API_FCT(add_entity);
+       API_FCT(find_nodes_with_meta);
+       API_FCT(get_meta);
+       API_FCT(get_node_timer);
+       API_FCT(get_player_by_name);
+       API_FCT(get_objects_inside_radius);
+       API_FCT(set_timeofday);
+       API_FCT(get_timeofday);
+       API_FCT(get_gametime);
+       API_FCT(find_node_near);
+       API_FCT(find_nodes_in_area);
+       API_FCT(find_nodes_in_area_under_air);
+       API_FCT(emerge_area);
+       API_FCT(delete_area);
+       API_FCT(get_perlin);
+       API_FCT(get_perlin_map);
+       API_FCT(get_voxel_manip);
+       API_FCT(clear_objects);
+       API_FCT(spawn_tree);
+       API_FCT(find_path);
+       API_FCT(line_of_sight);
+       API_FCT(transforming_liquid_add);
+       API_FCT(forceload_block);
+       API_FCT(forceload_free_block);
+}