X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fscript%2Flua_api%2Fl_env.cpp;h=7ea8b64b5be4d334170b9c9367c17da37ca85281;hb=3face01a202040e4feff3b0936b4aa89c051c98d;hp=8b0d8cd6ec506bfea9e164b9453a4607aa0120be;hpb=6291fd1cbb9c2808e336532c833e26330ff2642b;p=dragonfireclient.git diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 8b0d8cd6e..7ea8b64b5 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -25,67 +25,245 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_vmanip.h" #include "common/c_converter.h" #include "common/c_content.h" -#include "scripting_game.h" +#include +#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 "treegen.h" +#include "mapgen/treegen.h" +#include "emerge.h" #include "pathfinder.h" +#include "face_position_cache.h" +#include "remoteplayer.h" +#ifndef SERVER +#include "client.h" +#endif +struct EnumString ModApiEnvMod::es_ClearObjectsMode[] = +{ + {CLEAR_OBJECTS_MODE_FULL, "full"}, + {CLEAR_OBJECTS_MODE_QUICK, "quick"}, + {0, NULL}, +}; -#define GET_ENV_PTR ServerEnvironment* env = \ - dynamic_cast(getEnv(L)); \ - if( env == NULL) return 0 - /////////////////////////////////////////////////////////////////////////////// void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, u32 active_object_count, u32 active_object_count_wider) { - GameScripting *scriptIface = env->getScriptIface(); + ServerScripting *scriptIface = env->getScriptIface(); scriptIface->realityCheck(); lua_State *L = scriptIface->getStack(); - assert(lua_checkstack(L, 20)); + 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(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); + + 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 = lua_toboolean(L, 3); + } + if (lua_isboolean(L, 4)) { + liquids = lua_toboolean(L, 4); + } + + LuaRaycast *o = new LuaRaycast(core::line3d(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); @@ -95,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) { @@ -114,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) { @@ -129,7 +356,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) { @@ -138,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().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) @@ -164,19 +390,19 @@ 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().getNodeNoEx(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) { @@ -184,7 +410,7 @@ int ModApiEnvMod::l_place_node(lua_State *L) ScriptApiItem *scriptIfaceItem = getScriptApi(L); Server *server = getServer(L); - INodeDefManager *ndef = server->ndef(); + const NodeDefManager *ndef = server->ndef(); IItemDefManager *idef = server->idef(); v3s16 pos = read_v3s16(L, 1); @@ -197,20 +423,19 @@ int ModApiEnvMod::l_place_node(lua_State *L) return 1; } // Create item to place - 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 = scriptIfaceItem->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) { @@ -233,7 +458,7 @@ int ModApiEnvMod::l_dig_node(lua_State *L) return 1; } -// minetest.punch_node(pos) +// punch_node(pos) // pos = {x=num, y=num, z=num} int ModApiEnvMod::l_punch_node(lua_State *L) { @@ -251,16 +476,19 @@ int ModApiEnvMod::l_punch_node(lua_State *L) } // Punch it with a NULL puncher (appears in Lua as a non-functional // ObjectRef) - bool success = scriptIfaceNode->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) { - GET_ENV_PTR; + Environment *env = getEnv(L); + if (!env) { + return 0; + } v3s16 pos = read_v3s16(L, 1); MapNode n = env->getMap().getNodeNoEx(pos); @@ -268,11 +496,14 @@ 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) { - GET_ENV_PTR; + Environment *env = getEnv(L); + if (!env) { + return 0; + } v3s16 pos = read_v3s16(L, 1); MapNode n = env->getMap().getNodeNoEx(pos); @@ -280,7 +511,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) @@ -297,7 +528,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) @@ -314,8 +545,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 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); + } -// minetest.get_meta(pos) + return 1; +} + +// get_meta(pos) int ModApiEnvMod::l_get_meta(lua_State *L) { GET_ENV_PTR; @@ -326,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; @@ -337,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) { @@ -347,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 - getScriptApiBase(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) { @@ -367,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(env->getPlayer(name)); + if (player == NULL){ lua_pushnil(L); return 1; } @@ -413,40 +655,35 @@ int ModApiEnvMod::l_get_player_by_name(lua_State *L) return 1; } // Put player on stack - getScriptApiBase(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 ids = env->getObjectsInsideRadius(pos, radius); - lua_newtable(L); - int table = lua_gettop(L); - for(std::set::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); - getScriptApiBase(L)->objectrefGetOrCreate(obj); - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); + std::vector ids; + env->getObjectsInsideRadius(ids, pos, radius); + ScriptApiBase *script = getScriptApiBase(L); + lua_createtable(L, ids.size(), 0); + std::vector::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) { @@ -454,7 +691,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 @@ -464,19 +701,34 @@ 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; } -// minetest.get_gametime() +// get_day_count() -> int +int ModApiEnvMod::l_get_day_count(lua_State *L) +{ + Environment *env = getEnv(L); + if (!env) { + return 0; + } + + lua_pushnumber(L, env->getDayCount()); + return 1; +} + +// get_gametime() int ModApiEnvMod::l_get_gametime(lua_State *L) { GET_ENV_PTR; @@ -487,38 +739,48 @@ int ModApiEnvMod::l_get_gametime(lua_State *L) } -// minetest.find_node_near(pos, radius, nodenames) -> pos or nil +// 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 filter; - if(lua_istable(L, 3)){ - int table = 3; + std::vector 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); // 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); } - for(int d=1; d<=radius; d++){ - std::list list; - getFacePositions(list, d); - for(std::list::iterator i = list.begin(); - i != list.end(); ++i){ - v3s16 p = pos + (*i); + int start_radius = (lua_toboolean(L, 4)) ? 0 : 1; + +#ifndef SERVER + // Client API limitations + if (getClient(L) && + getClient(L)->checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_LOOKUP_NODES)) { + radius = std::max(radius, getClient(L)->getCSMNodeRangeLimit()); + } +#endif + + for (int d = start_radius; d <= radius; d++) { + std::vector list = FacePositionCache::getFacePositions(d); + for (const v3s16 &i : list) { + v3s16 p = pos + i; content_t c = env->getMap().getNodeNoEx(p).getContent(); - if(filter.count(c) != 0){ + if (CONTAINS(filter, c)) { push_v3s16(L, p); return 1; } @@ -527,134 +789,350 @@ 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 filter; - if(lua_istable(L, 3)){ - int table = 3; + sortBoxVerticies(minp, maxp); + + v3s16 cube = maxp - minp + 1; + + /* Limit for too large areas, assume default values + * and give tolerances of 1 node on each side + * (chunksize * MAP_BLOCKSIZE + 2)^3 = 551368 + */ + if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 551368) { + luaL_error(L, "find_nodes_in_area(): area volume" + " exceeds allowed value of 551368"); + return 0; + } + + std::vector 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); // 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::vector 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); + 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){ - lua_pushvalue(L, table_insert); - lua_pushvalue(L, table); + + std::vector::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; + + /* Limit for too large areas, assume default values + * and give tolerances of 1 node on each side + * (chunksize * MAP_BLOCKSIZE + 2)^3 = 551368 + */ + if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 551368) { + luaL_error(L, "find_nodes_in_area_under_air(): area volume" + " exceeds allowed value of 551368"); + return 0; + } + + std::vector 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(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(); + 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 && + 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; + + NoiseParams params; + + if (lua_istable(L, 1)) { + read_noiseparams(L, 1, ¶ms); + } 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); + } - int seeddiff = luaL_checkint(L, 1); - int octaves = luaL_checkint(L, 2); - float persistence = luaL_checknumber(L, 3); - float scale = 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(¶ms); *(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.clear_objects() +// clear_objects([options]) // clear all objects in the environment +// where options = {mode = "full" or "quick"} int ModApiEnvMod::l_clear_objects(lua_State *L) { GET_ENV_PTR; - env->clearAllObjects(); + 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; } -// minetest.line_of_sight(pos1, pos2, stepsize) -> true/false -int ModApiEnvMod::l_line_of_sight(lua_State *L) { - float stepsize = 1.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); - //read step size from lua - if (lua_isnumber(L, 3)) - stepsize = lua_tonumber(L, 3); - return (env->line_of_sight(pos1,pos2,stepsize)); + v3s16 p; + + bool success = env->line_of_sight(pos1, pos2, &p); + lua_pushboolean(L, success); + if (!success) { + push_v3s16(L, p); + return 2; + } + return 1; +} + +// fix_light(p1, p2) +int ModApiEnvMod::l_fix_light(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 blockpos1 = getContainerPos(read_v3s16(L, 1), MAP_BLOCKSIZE); + v3s16 blockpos2 = getContainerPos(read_v3s16(L, 2), MAP_BLOCKSIZE); + ServerMap &map = env->getServerMap(); + std::map 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); +} + +// 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(); + } + + 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; +} + +// delete_area(p1, p2) +// delete mapblocks in area p1..p2 +int ModApiEnvMod::l_delete_area(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 bpmin = getNodeBlockPos(read_v3s16(L, 1)); + v3s16 bpmax = getNodeBlockPos(read_v3s16(L, 2)); + sortBoxVerticies(bpmin, bpmax); + + ServerMap &map = env->getServerMap(); + + MapEditEvent event; + event.type = MEET_OTHER; + + bool success = true; + for (s16 z = bpmin.Z; z <= bpmax.Z; z++) + for (s16 y = bpmin.Y; y <= bpmax.Y; y++) + for (s16 x = bpmin.X; x <= bpmax.X; x++) { + v3s16 bp(x, y, z); + if (map.deleteBlock(bp)) { + env->setStaticForActiveObjectsInBlock(bp, false); + event.modified_blocks.insert(bp); + } else { + success = false; + } + } + + 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) { @@ -665,29 +1143,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 path = - get_Path(env,pos1,pos2,searchdistance,max_jump,max_drop,algo); + std::vector 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::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++; } @@ -697,7 +1173,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; @@ -706,7 +1182,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)) { @@ -721,33 +1197,40 @@ 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); + + 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; @@ -757,33 +1240,34 @@ 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; } - 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); @@ -797,6 +1281,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) 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); @@ -804,8 +1289,13 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) 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(emerge_area); + API_FCT(delete_area); API_FCT(get_perlin); API_FCT(get_perlin_map); API_FCT(get_voxel_manip); @@ -813,7 +1303,17 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) API_FCT(spawn_tree); API_FCT(find_path); API_FCT(line_of_sight); + API_FCT(raycast); API_FCT(transforming_liquid_add); - API_FCT(get_heat); - API_FCT(get_humidity); + API_FCT(forceload_block); + API_FCT(forceload_free_block); +} + +void ModApiEnvMod::InitializeClient(lua_State *L, int top) +{ + API_FCT(get_timeofday); + API_FCT(get_day_count); + API_FCT(get_node_max_level); + API_FCT(get_node_level); + API_FCT(find_node_near); }