]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/script/lua_api/l_env.cpp
[CSM] Remove non-functional minetest.get_day_count()
[dragonfireclient.git] / src / script / lua_api / l_env.cpp
index 89ba9798a8fe21e3420bf9a19f50043131edc434..8c2b6ffd874931c81a2af4c5a5b03665b16c3e67 100644 (file)
@@ -17,89 +17,253 @@ 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 <algorithm>
+#include "scripting_server.h"
 #include "environment.h"
+#include "mapblock.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 "mapgen/treegen.h"
 #include "emerge.h"
-#include "mapgen_v7.h"
-
+#include "pathfinder.h"
+#include "face_position_cache.h"
+#include "remoteplayer.h"
+#ifndef SERVER
+#include "client/client.h"
+#endif
 
-#define GET_ENV_PTR ServerEnvironment* env =                                   \
-                               dynamic_cast<ServerEnvironment*>(getEnv(L));                   \
-                               if( env == NULL) return 0
-                               
-struct EnumString ModApiEnvMod::es_MapgenObject[] =
+struct EnumString ModApiEnvMod::es_ClearObjectsMode[] =
 {
-       {MGOBJ_VMANIP,    "voxelmanip"},
-       {MGOBJ_HEIGHTMAP, "heightmap"},
-       {MGOBJ_BIOMEMAP,  "biomemap"},
-       {MGOBJ_HEATMAP,   "heatmap"},
-       {MGOBJ_HUMIDMAP,  "humiditymap"},
+       {CLEAR_OBJECTS_MODE_FULL,  "full"},
+       {CLEAR_OBJECTS_MODE_QUICK, "quick"},
        {0, NULL},
 };
 
-
 ///////////////////////////////////////////////////////////////////////////////
 
 
 void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n,
                u32 active_object_count, u32 active_object_count_wider)
 {
-       ScriptApi* scriptIface = SERVER_TO_SA(env);
+       ServerScripting *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 LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n)
+{
+       ServerScripting *scriptIface = env->getScriptIface();
+       scriptIface->realityCheck();
+
+       lua_State *L = scriptIface->getStack();
+       sanity_check(lua_checkstack(L, 20));
+       StackUnroller stack_unroller(L);
+
+       int error_handler = PUSH_ERROR_HANDLER(L);
+
+       // Get registered_lbms
+       lua_getglobal(L, "core");
+       lua_getfield(L, -1, "registered_lbms");
+       luaL_checktype(L, -1, LUA_TTABLE);
+       lua_remove(L, -2); // Remove core
+
+       // Get registered_lbms[m_id]
+       lua_pushnumber(L, m_id);
+       lua_gettable(L, -2);
+       FATAL_ERROR_IF(lua_isnil(L, -1), "Entry with given id not found in registered_lbms table");
+       lua_remove(L, -2); // Remove registered_lbms
+
+       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_lbms[m_id]
+       push_v3s16(L, p);
+       pushnode(L, n, env->getGameDef()->ndef());
+
+       int result = lua_pcall(L, 2, 0, error_handler);
+       if (result)
+               scriptIface->scriptError(result, "LuaLBM::trigger");
+
+       lua_pop(L, 1); // Pop error handler
+}
+
+int LuaRaycast::l_next(lua_State *L)
+{
+       MAP_LOCK_REQUIRED;
+
+       ScriptApiItem *script = getScriptApi<ScriptApiItem>(L);
+       GET_ENV_PTR;
+
+       LuaRaycast *o = checkobject(L, 1);
+       PointedThing pointed;
+       env->continueRaycast(&o->state, &pointed);
+       if (pointed.type == POINTEDTHING_NOTHING)
+               lua_pushnil(L);
+       else
+               script->pushPointedThing(pointed, true);
+
+       return 1;
+}
+
+int LuaRaycast::create_object(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+
+       bool objects = true;
+       bool liquids = false;
+
+       v3f pos1 = checkFloatPos(L, 1);
+       v3f pos2 = checkFloatPos(L, 2);
+       if (lua_isboolean(L, 3)) {
+               objects = readParam<bool>(L, 3);
+       }
+       if (lua_isboolean(L, 4)) {
+               liquids = readParam<bool>(L, 4);
+       }
+
+       LuaRaycast *o = new LuaRaycast(core::line3d<f32>(pos1, pos2),
+               objects, liquids);
+
+       *(void **) (lua_newuserdata(L, sizeof(void *))) = o;
+       luaL_getmetatable(L, className);
+       lua_setmetatable(L, -2);
+       return 1;
+}
+
+LuaRaycast *LuaRaycast::checkobject(lua_State *L, int narg)
+{
+       NO_MAP_LOCK_REQUIRED;
+
+       luaL_checktype(L, narg, LUA_TUSERDATA);
+       void *ud = luaL_checkudata(L, narg, className);
+       if (!ud)
+               luaL_typerror(L, narg, className);
+       return *(LuaRaycast **) ud;
+}
+
+int LuaRaycast::gc_object(lua_State *L)
+{
+       LuaRaycast *o = *(LuaRaycast **) (lua_touserdata(L, 1));
+       delete o;
+       return 0;
+}
+
+void LuaRaycast::Register(lua_State *L)
+{
+       lua_newtable(L);
+       int methodtable = lua_gettop(L);
+       luaL_newmetatable(L, className);
+       int metatable = lua_gettop(L);
+
+       lua_pushliteral(L, "__metatable");
+       lua_pushvalue(L, methodtable);
+       lua_settable(L, metatable);
+
+       lua_pushliteral(L, "__index");
+       lua_pushvalue(L, methodtable);
+       lua_settable(L, metatable);
+
+       lua_pushliteral(L, "__gc");
+       lua_pushcfunction(L, gc_object);
+       lua_settable(L, metatable);
+
+       lua_pushliteral(L, "__call");
+       lua_pushcfunction(L, l_next);
+       lua_settable(L, metatable);
+
+       lua_pop(L, 1);
+
+       luaL_openlib(L, 0, methods, 0);
+       lua_pop(L, 1);
+
+       lua_register(L, className, create_object);
+}
+
+const char LuaRaycast::className[] = "Raycast";
+const luaL_Reg LuaRaycast::methods[] =
+{
+       luamethod(LuaRaycast, next),
+       { 0, 0 }
+};
+
+void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param)
+{
+       ScriptCallbackState *state = (ScriptCallbackState *)param;
+       assert(state != NULL);
+       assert(state->script != NULL);
+       assert(state->refcount > 0);
+
+       // state must be protected by envlock
+       Server *server = state->script->getServer();
+       MutexAutoLock envlock(server->m_env_mutex);
+
+       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)
 {
        GET_ENV_PTR;
 
-       INodeDefManager *ndef = env->getGameDef()->ndef();
+       const NodeDefManager *ndef = env->getGameDef()->ndef();
        // parameters
        v3s16 pos = read_v3s16(L, 1);
        MapNode n = readnode(L, 2, ndef);
@@ -109,12 +273,45 @@ int ModApiEnvMod::l_set_node(lua_State *L)
        return 1;
 }
 
+// bulk_set_node([pos1, pos2, ...], node)
+// pos = {x=num, y=num, z=num}
+int ModApiEnvMod::l_bulk_set_node(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       const NodeDefManager *ndef = env->getGameDef()->ndef();
+       // parameters
+       if (!lua_istable(L, 1)) {
+               return 0;
+       }
+
+       s32 len = lua_objlen(L, 1);
+       if (len == 0) {
+               lua_pushboolean(L, true);
+               return 1;
+       }
+
+       MapNode n = readnode(L, 2, ndef);
+
+       // Do it
+       bool succeeded = true;
+       for (s32 i = 1; i <= len; i++) {
+               lua_rawgeti(L, 1, i);
+               if (!env->setNode(read_v3s16(L, -1), n))
+                       succeeded = false;
+               lua_pop(L, 1);
+       }
+
+       lua_pushboolean(L, succeeded);
+       return 1;
+}
+
 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 +325,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;
+
+       const NodeDefManager *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)
 {
@@ -137,13 +350,13 @@ int ModApiEnvMod::l_get_node(lua_State *L)
        // pos
        v3s16 pos = read_v3s16(L, 1);
        // Do it
-       MapNode n = env->getMap().getNodeNoEx(pos);
+       MapNode n = env->getMap().getNode(pos);
        // Return node
        pushnode(L, n, env->getGameDef()->ndef());
        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 +365,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().getNode(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,92 +390,179 @@ 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);
-               INodeDefManager *ndef = env->getGameDef()->ndef();
+
+       bool is_position_ok;
+       MapNode n = env->getMap().getNode(pos, &is_position_ok);
+       if (is_position_ok) {
+               const NodeDefManager *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);
+       const NodeDefManager *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);
+       MapNode n_old = env->getMap().getNode(pos);
        if(n_old.getContent() == CONTENT_IGNORE){
                lua_pushboolean(L, false);
                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);
+       ItemStack item(ndef->get(n).name, 1, 0, idef);
        // Make pointed position
        PointedThing pointed;
        pointed.type = POINTEDTHING_NODE;
        pointed.node_abovesurface = pos;
        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);
+       // Place it with a NULL placer (appears in Lua as nil)
+       bool success = scriptIfaceItem->item_OnPlace(item, nullptr, 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
-       MapNode n = env->getMap().getNodeNoEx(pos);
+       MapNode n = env->getMap().getNode(pos);
        if(n.getContent() == CONTENT_IGNORE){
                lua_pushboolean(L, false);
                return 1;
        }
        // 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
-       MapNode n = env->getMap().getNodeNoEx(pos);
+       MapNode n = env->getMap().getNode(pos);
        if(n.getContent() == CONTENT_IGNORE){
                lua_pushboolean(L, false);
                return 1;
        }
        // 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_meta(pos)
+// get_node_max_level(pos)
+// pos = {x=num, y=num, z=num}
+int ModApiEnvMod::l_get_node_max_level(lua_State *L)
+{
+       Environment *env = getEnv(L);
+       if (!env) {
+               return 0;
+       }
+
+       v3s16 pos = read_v3s16(L, 1);
+       MapNode n = env->getMap().getNode(pos);
+       lua_pushnumber(L, n.getMaxLevel(env->getGameDef()->ndef()));
+       return 1;
+}
+
+// get_node_level(pos)
+// pos = {x=num, y=num, z=num}
+int ModApiEnvMod::l_get_node_level(lua_State *L)
+{
+       Environment *env = getEnv(L);
+       if (!env) {
+               return 0;
+       }
+
+       v3s16 pos = read_v3s16(L, 1);
+       MapNode n = env->getMap().getNode(pos);
+       lua_pushnumber(L, n.getLevel(env->getGameDef()->ndef()));
+       return 1;
+}
+
+// set_node_level(pos, level)
+// pos = {x=num, y=num, z=num}
+// level: 0..63
+int ModApiEnvMod::l_set_node_level(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       v3s16 pos = read_v3s16(L, 1);
+       u8 level = 1;
+       if(lua_isnumber(L, 2))
+               level = lua_tonumber(L, 2);
+       MapNode n = env->getMap().getNode(pos);
+       lua_pushnumber(L, n.setLevel(env->getGameDef()->ndef(), level));
+       env->setNode(pos, n);
+       return 1;
+}
+
+// add_node_level(pos, level)
+// pos = {x=num, y=num, z=num}
+// level: 0..63
+int ModApiEnvMod::l_add_node_level(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       v3s16 pos = read_v3s16(L, 1);
+       u8 level = 1;
+       if(lua_isnumber(L, 2))
+               level = lua_tonumber(L, 2);
+       MapNode n = env->getMap().getNode(pos);
+       lua_pushnumber(L, n.addLevel(env->getGameDef()->ndef(), level));
+       env->setNode(pos, n);
+       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;
+}
+
+// get_meta(pos)
 int ModApiEnvMod::l_get_meta(lua_State *L)
 {
        GET_ENV_PTR;
@@ -274,7 +573,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;
@@ -285,7 +584,7 @@ int ModApiEnvMod::l_get_node_timer(lua_State *L)
        return 1;
 }
 
-// minetest.add_entity(pos, entityname) -> ObjectRef or nil
+// add_entity(pos, entityname, [staticdata]) -> ObjectRef or nil
 // pos = {x=num, y=num, z=num}
 int ModApiEnvMod::l_add_entity(lua_State *L)
 {
@@ -295,18 +594,20 @@ int ModApiEnvMod::l_add_entity(lua_State *L)
        v3f pos = checkFloatPos(L, 1);
        // content
        const char *name = luaL_checkstring(L, 2);
+       // staticdata
+       const char *staticdata = luaL_optstring(L, 3, "");
        // Do it
-       ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, "");
+       ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, staticdata);
        int objectid = env->addActiveObject(obj);
        // If failed to add, return nothing (reads as nil)
        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)
 {
@@ -315,43 +616,36 @@ int ModApiEnvMod::l_add_item(lua_State *L)
        // pos
        //v3f pos = checkFloatPos(L, 1);
        // item
-       ItemStack item = read_item(L, 2,getServer(L));
+       ItemStack item = read_item(L, 2,getServer(L)->idef());
        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;
 
        // Do it
        const char *name = luaL_checkstring(L, 1);
-       Player *player = env->getPlayer(name);
-       if(player == NULL){
+       RemotePlayer *player = dynamic_cast<RemotePlayer *>(env->getPlayer(name));
+       if (player == NULL){
                lua_pushnil(L);
                return 1;
        }
@@ -361,48 +655,43 @@ 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);
-               // 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));
+       float radius = readParam<float>(L, 2) * BS;
+       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);
+               if (!obj->isGone()) {
+                       // Insert object reference into table
+                       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)
 {
        GET_ENV_PTR;
 
        // Do it
-       float timeofday_f = luaL_checknumber(L, 1);
-       assert(timeofday_f >= 0.0 && timeofday_f <= 1.0);
+       float timeofday_f = readParam<float>(L, 1);
+       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
@@ -412,51 +701,87 @@ 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;
+       Environment *env = getEnv(L);
+       if (!env) {
+               return 0;
+       }
 
        // Do it
        int timeofday_mh = env->getTimeOfDay();
-       float timeofday_f = (float)timeofday_mh / 24000.0;
+       float timeofday_f = (float)timeofday_mh / 24000.0f;
        lua_pushnumber(L, timeofday_f);
        return 1;
 }
 
+// get_day_count() -> int
+int ModApiEnvMod::l_get_day_count(lua_State *L)
+{
+       Environment *env = getEnv(L);
+       if (!env) {
+               return 0;
+       }
 
-// minetest.find_node_near(pos, radius, nodenames) -> pos or nil
+       lua_pushnumber(L, env->getDayCount());
+       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;
+}
+
+
+// find_node_near(pos, radius, nodenames, search_center) -> pos or nil
 // nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
 int ModApiEnvMod::l_find_node_near(lua_State *L)
 {
-       GET_ENV_PTR;
+       Environment *env = getEnv(L);
+       if (!env) {
+               return 0;
+       }
 
-       INodeDefManager *ndef = getServer(L)->ndef();
+       const NodeDefManager *ndef = getGameDef(L)->ndef();
        v3s16 pos = read_v3s16(L, 1);
        int radius = luaL_checkinteger(L, 2);
-       std::set<content_t> filter;
-       if(lua_istable(L, 3)){
-               int table = 3;
+       std::vector<content_t> filter;
+       if (lua_istable(L, 3)) {
                lua_pushnil(L);
-               while(lua_next(L, table) != 0){
+               while (lua_next(L, 3) != 0) {
                        // key at index -2 and value at index -1
                        luaL_checktype(L, -1, LUA_TSTRING);
-                       ndef->getIds(lua_tostring(L, -1), filter);
+                       ndef->getIds(readParam<std::string>(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);
-       }
-
-       for(int d=1; d<=radius; d++){
-               std::list<v3s16> list;
-               getFacePositions(list, d);
-               for(std::list<v3s16>::iterator i = list.begin();
-                               i != list.end(); ++i){
-                       v3s16 p = pos + (*i);
-                       content_t c = env->getMap().getNodeNoEx(p).getContent();
-                       if(filter.count(c) != 0){
+       } else if (lua_isstring(L, 3)) {
+               ndef->getIds(readParam<std::string>(L, 3), filter);
+       }
+
+       int start_radius = (lua_isboolean(L, 4) && readParam<bool>(L, 4)) ? 0 : 1;
+
+#ifndef SERVER
+       // Client API limitations
+       if (getClient(L) &&
+                       getClient(L)->checkCSMRestrictionFlag(
+                       CSMRestrictionFlags::CSM_RF_LOOKUP_NODES)) {
+               radius = std::max<int>(radius, getClient(L)->getCSMNodeRangeLimit());
+       }
+#endif
+
+       for (int d = start_radius; d <= radius; d++) {
+               std::vector<v3s16> list = FacePositionCache::getFacePositions(d);
+               for (const v3s16 &i : list) {
+                       v3s16 p = pos + i;
+                       content_t c = env->getMap().getNode(p).getContent();
+                       if (CONTAINS(filter, c)) {
                                push_v3s16(L, p);
                                return 1;
                        }
@@ -465,270 +790,366 @@ 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)
 {
        GET_ENV_PTR;
 
-       INodeDefManager *ndef = getServer(L)->ndef();
+       const NodeDefManager *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;
+       sortBoxVerticies(minp, maxp);
+
+       v3s16 cube = maxp - minp + 1;
+       // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000
+       if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) {
+               luaL_error(L, "find_nodes_in_area(): area volume"
+                               " exceeds allowed value of 4096000");
+               return 0;
+       }
+
+       std::vector<content_t> filter;
+       if (lua_istable(L, 3)) {
                lua_pushnil(L);
-               while(lua_next(L, table) != 0){
+               while (lua_next(L, 3) != 0) {
                        // key at index -2 and value at index -1
                        luaL_checktype(L, -1, LUA_TSTRING);
-                       ndef->getIds(lua_tostring(L, -1), filter);
+                       ndef->getIds(readParam<std::string>(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);
+       } else if (lua_isstring(L, 3)) {
+               ndef->getIds(readParam<std::string>(L, 3), filter);
        }
 
-       // Get the table insert function
-       lua_getglobal(L, "table");
-       lua_getfield(L, -1, "insert");
-       int table_insert = lua_gettop(L);
+       std::vector<u32> individual_count;
+       individual_count.resize(filter.size());
 
        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);
-               content_t c = env->getMap().getNodeNoEx(p).getContent();
-               if(filter.count(c) != 0){
-                       lua_pushvalue(L, table_insert);
-                       lua_pushvalue(L, table);
+       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().getNode(p).getContent();
+
+               std::vector<content_t>::iterator it = std::find(filter.begin(), filter.end(), c);
+               if (it != filter.end()) {
                        push_v3s16(L, p);
-                       if(lua_pcall(L, 2, 0, 0))
-                               script_error(L, "error: %s", lua_tostring(L, -1));
+                       lua_rawseti(L, -2, ++i);
+
+                       u32 filt_index = it - filter.begin();
+                       individual_count[filt_index]++;
+               }
+       }
+       lua_newtable(L);
+       for (u32 i = 0; i < filter.size(); i++) {
+               lua_pushnumber(L, individual_count[i]);
+               lua_setfield(L, -2, ndef->get(filter[i]).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;
+
+       const NodeDefManager *ndef = getServer(L)->ndef();
+       v3s16 minp = read_v3s16(L, 1);
+       v3s16 maxp = read_v3s16(L, 2);
+       sortBoxVerticies(minp, maxp);
+
+       v3s16 cube = maxp - minp + 1;
+       // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000
+       if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) {
+               luaL_error(L, "find_nodes_in_area_under_air(): area volume"
+                               " exceeds allowed value of 4096000");
+               return 0;
+       }
+
+       std::vector<content_t> filter;
+
+       if (lua_istable(L, 3)) {
+               lua_pushnil(L);
+               while (lua_next(L, 3) != 0) {
+                       // key at index -2 and value at index -1
+                       luaL_checktype(L, -1, LUA_TSTRING);
+                       ndef->getIds(readParam<std::string>(L, -1), filter);
+                       // removes value, keeps key for next iteration
+                       lua_pop(L, 1);
+               }
+       } else if (lua_isstring(L, 3)) {
+               ndef->getIds(readParam<std::string>(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().getNode(p).getContent();
+               for (; y <= maxp.Y; y++) {
+                       v3s16 psurf(x, y + 1, z);
+                       content_t csurf = env->getMap().getNode(psurf).getContent();
+                       if (c != CONTENT_AIR && csurf == CONTENT_AIR &&
+                                       CONTAINS(filter, c)) {
+                               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;
 
-       int seeddiff = luaL_checkint(L, 1);
-       int octaves = luaL_checkint(L, 2);
-       float persistence = luaL_checknumber(L, 3);
-       float scale = luaL_checknumber(L, 4);
+       NoiseParams params;
 
-       LuaPerlinNoise *n = new LuaPerlinNoise(seeddiff + int(env->getServerMap().getSeed()), octaves, persistence, scale);
+       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 = readParam<float>(L, 3);
+               params.spread  = v3f(1, 1, 1) * readParam<float>(L, 4);
+       }
+
+       params.seed += (int)env->getServerMap().getSeed();
+
+       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);
+       s32 seed = (s32)(env->getServerMap().getSeed());
+       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([options])
+// clear all objects in the environment
+// where options = {mode = "full" or "quick"}
+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, false);
-                       *(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; }
+       ClearObjectsMode mode = CLEAR_OBJECTS_MODE_QUICK;
+       if (lua_istable(L, 1)) {
+               mode = (ClearObjectsMode)getenumfield(L, 1, "mode",
+                       ModApiEnvMod::es_ClearObjectsMode, mode);
+       }
+
+       env->clearObjects(mode);
+       return 0;
+}
+
+// line_of_sight(pos1, pos2) -> true/false, pos
+int ModApiEnvMod::l_line_of_sight(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       // read position 1 from lua
+       v3f pos1 = checkFloatPos(L, 1);
+       // read position 2 from lua
+       v3f pos2 = checkFloatPos(L, 2);
+
+       v3s16 p;
+
+       bool success = env->line_of_sight(pos1, pos2, &p);
+       lua_pushboolean(L, success);
+       if (!success) {
+               push_v3s16(L, p);
+               return 2;
        }
-       
-       return nargs;
+       return 1;
 }
 
-// minetest.set_mapgen_params(params)
-// set mapgen parameters
-int ModApiEnvMod::l_set_mapgen_params(lua_State *L)
+// fix_light(p1, p2)
+int ModApiEnvMod::l_fix_light(lua_State *L)
 {
-       if (!lua_istable(L, 1))
-               return 0;
+       GET_ENV_PTR;
 
-       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;
-       }
-       
-       lua_getfield(L, 1, "seed");
-       if (lua_isnumber(L, -1)) {
-               oparams->seed = lua_tointeger(L, -1);
-               paramsmodified |= MGPARAMS_SET_SEED;
-       }
-       
-       lua_getfield(L, 1, "water_level");
-       if (lua_isnumber(L, -1)) {
-               oparams->water_level = lua_tointeger(L, -1);
-               paramsmodified |= MGPARAMS_SET_WATER_LEVEL;
-       }
-
-       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);
+       v3s16 blockpos1 = getContainerPos(read_v3s16(L, 1), MAP_BLOCKSIZE);
+       v3s16 blockpos2 = getContainerPos(read_v3s16(L, 2), MAP_BLOCKSIZE);
+       ServerMap &map = env->getServerMap();
+       std::map<v3s16, MapBlock *> modified_blocks;
+       bool success = true;
+       v3s16 blockpos;
+       for (blockpos.X = blockpos1.X; blockpos.X <= blockpos2.X; blockpos.X++)
+       for (blockpos.Y = blockpos1.Y; blockpos.Y <= blockpos2.Y; blockpos.Y++)
+       for (blockpos.Z = blockpos1.Z; blockpos.Z <= blockpos2.Z; blockpos.Z++) {
+               success = success & map.repairBlockLight(blockpos, &modified_blocks);
+       }
+       if (!modified_blocks.empty()) {
+               MapEditEvent event;
+               event.type = MEET_OTHER;
+               for (auto &modified_block : modified_blocks)
+                       event.modified_blocks.insert(modified_block.first);
+
+               map.dispatchEvent(event);
+       }
+       lua_pushboolean(L, success);
+
+       return 1;
+}
+
+int ModApiEnvMod::l_raycast(lua_State *L)
+{
+       return LuaRaycast::create_object(L);
+}
+
+// load_area(p1, [p2])
+// load mapblocks in area p1..p2, but do not generate map
+int ModApiEnvMod::l_load_area(lua_State *L)
+{
+       GET_ENV_PTR;
+       MAP_LOCK_REQUIRED;
+
+       Map *map = &(env->getMap());
+       v3s16 bp1 = getNodeBlockPos(check_v3s16(L, 1));
+       if (!lua_istable(L, 2)) {
+               map->emergeBlock(bp1);
+       } else {
+               v3s16 bp2 = getNodeBlockPos(check_v3s16(L, 2));
+               sortBoxVerticies(bp1, bp2);
+               for (s16 z = bp1.Z; z <= bp2.Z; z++)
+               for (s16 y = bp1.Y; y <= bp2.Y; y++)
+               for (s16 x = bp1.X; x <= bp2.X; x++) {
+                       map->emergeBlock(v3s16(x, y, z));
                }
        }
-       
-       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)
+// 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;
 
-       env->clearAllObjects();
+       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();
+       }
+
+       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);
+       }
+
        return 0;
 }
 
-// minetest.line_of_sight(pos1, pos2, stepsize) -> true/false
-int ModApiEnvMod::l_line_of_sight(lua_State *L) {
-       float stepsize = 1.0;
-
+// delete_area(p1, p2)
+// delete mapblocks in area p1..p2
+int ModApiEnvMod::l_delete_area(lua_State *L)
+{
        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);
+       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)) {
+                       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)
 {
@@ -739,29 +1160,27 @@ int ModApiEnvMod::l_find_path(lua_State *L)
        unsigned int searchdistance = luaL_checkint(L, 3);
        unsigned int max_jump       = luaL_checkint(L, 4);
        unsigned int max_drop       = luaL_checkint(L, 5);
-       algorithm algo              = A_PLAIN_NP;
+       PathAlgorithm algo          = PA_PLAIN_NP;
        if (!lua_isnil(L, 6)) {
                std::string algorithm = luaL_checkstring(L,6);
 
                if (algorithm == "A*")
-                       algo = A_PLAIN;
+                       algo = PA_PLAIN;
 
                if (algorithm == "Dijkstra")
-                       algo = DIJKSTRA;
+                       algo = PA_DIJKSTRA;
        }
 
-       std::vector<v3s16> path =
-                       get_Path(env,pos1,pos2,searchdistance,max_jump,max_drop,algo);
+       std::vector<v3s16> path = get_path(env, pos1, pos2,
+               searchdistance, max_jump, max_drop, algo);
 
-       if (path.size() > 0)
-       {
+       if (!path.empty()) {
                lua_newtable(L);
                int top = lua_gettop(L);
                unsigned int index = 1;
-               for (std::vector<v3s16>::iterator i = path.begin(); i != path.end();i++)
-               {
+               for (const v3s16 &i : path) {
                        lua_pushnumber(L,index);
-                       push_v3s16(L, *i);
+                       push_v3s16(L, i);
                        lua_settable(L, top);
                        index++;
                }
@@ -771,7 +1190,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;
@@ -780,7 +1199,7 @@ int ModApiEnvMod::l_spawn_tree(lua_State *L)
 
        treegen::TreeDef tree_def;
        std::string trunk,leaves,fruit;
-       INodeDefManager *ndef = env->getGameDef()->ndef();
+       const NodeDefManager *ndef = env->getGameDef()->ndef();
 
        if(lua_istable(L, 2))
        {
@@ -795,66 +1214,123 @@ int ModApiEnvMod::l_spawn_tree(lua_State *L)
                tree_def.leavesnode=ndef->getId(leaves);
                tree_def.leaves2_chance=0;
                getstringfield(L, 2, "leaves2", leaves);
-               if (leaves !="")
-               {
+               if (!leaves.empty()) {
                        tree_def.leaves2node=ndef->getId(leaves);
                        getintfield(L, 2, "leaves2_chance", tree_def.leaves2_chance);
                }
                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;
                getstringfield(L, 2, "fruit", fruit);
-               if (fruit != "")
-               {
+               if (!fruit.empty()) {
                        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);
-       return 1;
-}
-
-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(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);
-
-       return retval;
-}
-
-ModApiEnvMod modapienv_prototype;
+
+       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;
+}
+
+// transforming_liquid_add(pos)
+int ModApiEnvMod::l_transforming_liquid_add(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       v3s16 p0 = read_v3s16(L, 1);
+       env->getMap().transforming_liquid_add(p0);
+       return 1;
+}
+
+// forceload_block(blockpos)
+// blockpos = {x=num, y=num, z=num}
+int ModApiEnvMod::l_forceload_block(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       v3s16 blockpos = read_v3s16(L, 1);
+       env->getForceloadedBlocks()->insert(blockpos);
+       return 0;
+}
+
+// forceload_free_block(blockpos)
+// blockpos = {x=num, y=num, z=num}
+int ModApiEnvMod::l_forceload_free_block(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       v3s16 blockpos = read_v3s16(L, 1);
+       env->getForceloadedBlocks()->erase(blockpos);
+       return 0;
+}
+
+void ModApiEnvMod::Initialize(lua_State *L, int top)
+{
+       API_FCT(set_node);
+       API_FCT(bulk_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(get_day_count);
+       API_FCT(find_node_near);
+       API_FCT(find_nodes_in_area);
+       API_FCT(find_nodes_in_area_under_air);
+       API_FCT(fix_light);
+       API_FCT(load_area);
+       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(raycast);
+       API_FCT(transforming_liquid_add);
+       API_FCT(forceload_block);
+       API_FCT(forceload_free_block);
+}
+
+void ModApiEnvMod::InitializeClient(lua_State *L, int top)
+{
+       API_FCT(get_timeofday);
+       API_FCT(get_node_max_level);
+       API_FCT(get_node_level);
+       API_FCT(find_node_near);
+}