3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "lua_api/l_env.h"
21 #include "lua_api/l_internal.h"
22 #include "lua_api/l_nodemeta.h"
23 #include "lua_api/l_nodetimer.h"
24 #include "lua_api/l_noise.h"
25 #include "lua_api/l_vmanip.h"
26 #include "common/c_converter.h"
27 #include "common/c_content.h"
29 #include "scripting_server.h"
30 #include "environment.h"
34 #include "daynightratio.h"
35 #include "util/pointedthing.h"
36 #include "content_sao.h"
37 #include "mapgen/treegen.h"
39 #include "pathfinder.h"
40 #include "face_position_cache.h"
41 #include "remoteplayer.h"
43 #include "client/client.h"
46 struct EnumString ModApiEnvMod::es_ClearObjectsMode[] =
48 {CLEAR_OBJECTS_MODE_FULL, "full"},
49 {CLEAR_OBJECTS_MODE_QUICK, "quick"},
53 ///////////////////////////////////////////////////////////////////////////////
56 void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n,
57 u32 active_object_count, u32 active_object_count_wider)
59 ServerScripting *scriptIface = env->getScriptIface();
60 scriptIface->realityCheck();
62 lua_State *L = scriptIface->getStack();
63 sanity_check(lua_checkstack(L, 20));
64 StackUnroller stack_unroller(L);
66 int error_handler = PUSH_ERROR_HANDLER(L);
68 // Get registered_abms
69 lua_getglobal(L, "core");
70 lua_getfield(L, -1, "registered_abms");
71 luaL_checktype(L, -1, LUA_TTABLE);
72 lua_remove(L, -2); // Remove core
74 // Get registered_abms[m_id]
75 lua_pushnumber(L, m_id);
79 lua_remove(L, -2); // Remove registered_abms
81 scriptIface->setOriginFromTable(-1);
84 luaL_checktype(L, -1, LUA_TTABLE);
85 lua_getfield(L, -1, "action");
86 luaL_checktype(L, -1, LUA_TFUNCTION);
87 lua_remove(L, -2); // Remove registered_abms[m_id]
89 pushnode(L, n, env->getGameDef()->ndef());
90 lua_pushnumber(L, active_object_count);
91 lua_pushnumber(L, active_object_count_wider);
93 int result = lua_pcall(L, 4, 0, error_handler);
95 scriptIface->scriptError(result, "LuaABM::trigger");
97 lua_pop(L, 1); // Pop error handler
100 void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n)
102 ServerScripting *scriptIface = env->getScriptIface();
103 scriptIface->realityCheck();
105 lua_State *L = scriptIface->getStack();
106 sanity_check(lua_checkstack(L, 20));
107 StackUnroller stack_unroller(L);
109 int error_handler = PUSH_ERROR_HANDLER(L);
111 // Get registered_lbms
112 lua_getglobal(L, "core");
113 lua_getfield(L, -1, "registered_lbms");
114 luaL_checktype(L, -1, LUA_TTABLE);
115 lua_remove(L, -2); // Remove core
117 // Get registered_lbms[m_id]
118 lua_pushnumber(L, m_id);
120 FATAL_ERROR_IF(lua_isnil(L, -1), "Entry with given id not found in registered_lbms table");
121 lua_remove(L, -2); // Remove registered_lbms
123 scriptIface->setOriginFromTable(-1);
126 luaL_checktype(L, -1, LUA_TTABLE);
127 lua_getfield(L, -1, "action");
128 luaL_checktype(L, -1, LUA_TFUNCTION);
129 lua_remove(L, -2); // Remove registered_lbms[m_id]
131 pushnode(L, n, env->getGameDef()->ndef());
133 int result = lua_pcall(L, 2, 0, error_handler);
135 scriptIface->scriptError(result, "LuaLBM::trigger");
137 lua_pop(L, 1); // Pop error handler
140 int LuaRaycast::l_next(lua_State *L)
146 csm = getClient(L) != nullptr;
149 LuaRaycast *o = checkobject(L, 1);
150 PointedThing pointed;
151 env->continueRaycast(&o->state, &pointed);
152 if (pointed.type == POINTEDTHING_NOTHING)
155 push_pointed_thing(L, pointed, csm, true);
160 int LuaRaycast::create_object(lua_State *L)
162 NO_MAP_LOCK_REQUIRED;
165 bool liquids = false;
167 v3f pos1 = checkFloatPos(L, 1);
168 v3f pos2 = checkFloatPos(L, 2);
169 if (lua_isboolean(L, 3)) {
170 objects = readParam<bool>(L, 3);
172 if (lua_isboolean(L, 4)) {
173 liquids = readParam<bool>(L, 4);
176 LuaRaycast *o = new LuaRaycast(core::line3d<f32>(pos1, pos2),
179 *(void **) (lua_newuserdata(L, sizeof(void *))) = o;
180 luaL_getmetatable(L, className);
181 lua_setmetatable(L, -2);
185 LuaRaycast *LuaRaycast::checkobject(lua_State *L, int narg)
187 NO_MAP_LOCK_REQUIRED;
189 luaL_checktype(L, narg, LUA_TUSERDATA);
190 void *ud = luaL_checkudata(L, narg, className);
192 luaL_typerror(L, narg, className);
193 return *(LuaRaycast **) ud;
196 int LuaRaycast::gc_object(lua_State *L)
198 LuaRaycast *o = *(LuaRaycast **) (lua_touserdata(L, 1));
203 void LuaRaycast::Register(lua_State *L)
206 int methodtable = lua_gettop(L);
207 luaL_newmetatable(L, className);
208 int metatable = lua_gettop(L);
210 lua_pushliteral(L, "__metatable");
211 lua_pushvalue(L, methodtable);
212 lua_settable(L, metatable);
214 lua_pushliteral(L, "__index");
215 lua_pushvalue(L, methodtable);
216 lua_settable(L, metatable);
218 lua_pushliteral(L, "__gc");
219 lua_pushcfunction(L, gc_object);
220 lua_settable(L, metatable);
222 lua_pushliteral(L, "__call");
223 lua_pushcfunction(L, l_next);
224 lua_settable(L, metatable);
228 luaL_openlib(L, 0, methods, 0);
231 lua_register(L, className, create_object);
234 const char LuaRaycast::className[] = "Raycast";
235 const luaL_Reg LuaRaycast::methods[] =
237 luamethod(LuaRaycast, next),
241 void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param)
243 ScriptCallbackState *state = (ScriptCallbackState *)param;
244 assert(state != NULL);
245 assert(state->script != NULL);
246 assert(state->refcount > 0);
248 // state must be protected by envlock
249 Server *server = state->script->getServer();
250 MutexAutoLock envlock(server->m_env_mutex);
254 state->script->on_emerge_area_completion(blockpos, action, state);
256 if (state->refcount == 0)
260 // Exported functions
262 // set_node(pos, node)
263 // pos = {x=num, y=num, z=num}
264 int ModApiEnvMod::l_set_node(lua_State *L)
268 const NodeDefManager *ndef = env->getGameDef()->ndef();
270 v3s16 pos = read_v3s16(L, 1);
271 MapNode n = readnode(L, 2, ndef);
273 bool succeeded = env->setNode(pos, n);
274 lua_pushboolean(L, succeeded);
278 // bulk_set_node([pos1, pos2, ...], node)
279 // pos = {x=num, y=num, z=num}
280 int ModApiEnvMod::l_bulk_set_node(lua_State *L)
284 const NodeDefManager *ndef = env->getGameDef()->ndef();
286 if (!lua_istable(L, 1)) {
290 s32 len = lua_objlen(L, 1);
292 lua_pushboolean(L, true);
296 MapNode n = readnode(L, 2, ndef);
299 bool succeeded = true;
300 for (s32 i = 1; i <= len; i++) {
301 lua_rawgeti(L, 1, i);
302 if (!env->setNode(read_v3s16(L, -1), n))
307 lua_pushboolean(L, succeeded);
311 int ModApiEnvMod::l_add_node(lua_State *L)
313 return l_set_node(L);
317 // pos = {x=num, y=num, z=num}
318 int ModApiEnvMod::l_remove_node(lua_State *L)
323 v3s16 pos = read_v3s16(L, 1);
325 bool succeeded = env->removeNode(pos);
326 lua_pushboolean(L, succeeded);
330 // swap_node(pos, node)
331 // pos = {x=num, y=num, z=num}
332 int ModApiEnvMod::l_swap_node(lua_State *L)
336 const NodeDefManager *ndef = env->getGameDef()->ndef();
338 v3s16 pos = read_v3s16(L, 1);
339 MapNode n = readnode(L, 2, ndef);
341 bool succeeded = env->swapNode(pos, n);
342 lua_pushboolean(L, succeeded);
347 // pos = {x=num, y=num, z=num}
348 int ModApiEnvMod::l_get_node(lua_State *L)
353 v3s16 pos = read_v3s16(L, 1);
355 MapNode n = env->getMap().getNode(pos);
357 pushnode(L, n, env->getGameDef()->ndef());
361 // get_node_or_nil(pos)
362 // pos = {x=num, y=num, z=num}
363 int ModApiEnvMod::l_get_node_or_nil(lua_State *L)
368 v3s16 pos = read_v3s16(L, 1);
371 MapNode n = env->getMap().getNode(pos, &pos_ok);
374 pushnode(L, n, env->getGameDef()->ndef());
381 // get_node_light(pos, timeofday)
382 // pos = {x=num, y=num, z=num}
383 // timeofday: nil = current time, 0 = night, 0.5 = day
384 int ModApiEnvMod::l_get_node_light(lua_State *L)
389 v3s16 pos = read_v3s16(L, 1);
390 u32 time_of_day = env->getTimeOfDay();
391 if(lua_isnumber(L, 2))
392 time_of_day = 24000.0 * lua_tonumber(L, 2);
393 time_of_day %= 24000;
394 u32 dnr = time_to_daynight_ratio(time_of_day, true);
397 MapNode n = env->getMap().getNode(pos, &is_position_ok);
398 if (is_position_ok) {
399 const NodeDefManager *ndef = env->getGameDef()->ndef();
400 lua_pushinteger(L, n.getLightBlend(dnr, ndef));
407 // place_node(pos, node)
408 // pos = {x=num, y=num, z=num}
409 int ModApiEnvMod::l_place_node(lua_State *L)
413 ScriptApiItem *scriptIfaceItem = getScriptApi<ScriptApiItem>(L);
414 Server *server = getServer(L);
415 const NodeDefManager *ndef = server->ndef();
416 IItemDefManager *idef = server->idef();
418 v3s16 pos = read_v3s16(L, 1);
419 MapNode n = readnode(L, 2, ndef);
421 // Don't attempt to load non-loaded area as of now
422 MapNode n_old = env->getMap().getNode(pos);
423 if(n_old.getContent() == CONTENT_IGNORE){
424 lua_pushboolean(L, false);
427 // Create item to place
428 ItemStack item(ndef->get(n).name, 1, 0, idef);
429 // Make pointed position
430 PointedThing pointed;
431 pointed.type = POINTEDTHING_NODE;
432 pointed.node_abovesurface = pos;
433 pointed.node_undersurface = pos + v3s16(0,-1,0);
434 // Place it with a NULL placer (appears in Lua as nil)
435 bool success = scriptIfaceItem->item_OnPlace(item, nullptr, pointed);
436 lua_pushboolean(L, success);
441 // pos = {x=num, y=num, z=num}
442 int ModApiEnvMod::l_dig_node(lua_State *L)
446 ScriptApiNode *scriptIfaceNode = getScriptApi<ScriptApiNode>(L);
448 v3s16 pos = read_v3s16(L, 1);
450 // Don't attempt to load non-loaded area as of now
451 MapNode n = env->getMap().getNode(pos);
452 if(n.getContent() == CONTENT_IGNORE){
453 lua_pushboolean(L, false);
456 // Dig it out with a NULL digger (appears in Lua as a
457 // non-functional ObjectRef)
458 bool success = scriptIfaceNode->node_on_dig(pos, n, NULL);
459 lua_pushboolean(L, success);
464 // pos = {x=num, y=num, z=num}
465 int ModApiEnvMod::l_punch_node(lua_State *L)
469 ScriptApiNode *scriptIfaceNode = getScriptApi<ScriptApiNode>(L);
471 v3s16 pos = read_v3s16(L, 1);
473 // Don't attempt to load non-loaded area as of now
474 MapNode n = env->getMap().getNode(pos);
475 if(n.getContent() == CONTENT_IGNORE){
476 lua_pushboolean(L, false);
479 // Punch it with a NULL puncher (appears in Lua as a non-functional
481 bool success = scriptIfaceNode->node_on_punch(pos, n, NULL, PointedThing());
482 lua_pushboolean(L, success);
486 // get_node_max_level(pos)
487 // pos = {x=num, y=num, z=num}
488 int ModApiEnvMod::l_get_node_max_level(lua_State *L)
492 v3s16 pos = read_v3s16(L, 1);
493 MapNode n = env->getMap().getNode(pos);
494 lua_pushnumber(L, n.getMaxLevel(env->getGameDef()->ndef()));
498 // get_node_level(pos)
499 // pos = {x=num, y=num, z=num}
500 int ModApiEnvMod::l_get_node_level(lua_State *L)
504 v3s16 pos = read_v3s16(L, 1);
505 MapNode n = env->getMap().getNode(pos);
506 lua_pushnumber(L, n.getLevel(env->getGameDef()->ndef()));
510 // set_node_level(pos, level)
511 // pos = {x=num, y=num, z=num}
513 int ModApiEnvMod::l_set_node_level(lua_State *L)
517 v3s16 pos = read_v3s16(L, 1);
519 if(lua_isnumber(L, 2))
520 level = lua_tonumber(L, 2);
521 MapNode n = env->getMap().getNode(pos);
522 lua_pushnumber(L, n.setLevel(env->getGameDef()->ndef(), level));
523 env->setNode(pos, n);
527 // add_node_level(pos, level)
528 // pos = {x=num, y=num, z=num}
530 int ModApiEnvMod::l_add_node_level(lua_State *L)
534 v3s16 pos = read_v3s16(L, 1);
536 if(lua_isnumber(L, 2))
537 level = lua_tonumber(L, 2);
538 MapNode n = env->getMap().getNode(pos);
539 lua_pushnumber(L, n.addLevel(env->getGameDef()->ndef(), level));
540 env->setNode(pos, n);
544 // find_nodes_with_meta(pos1, pos2)
545 int ModApiEnvMod::l_find_nodes_with_meta(lua_State *L)
549 std::vector<v3s16> positions = env->getMap().findNodesWithMetadata(
550 check_v3s16(L, 1), check_v3s16(L, 2));
553 for (size_t i = 0; i != positions.size(); i++) {
554 push_v3s16(L, positions[i]);
555 lua_rawseti(L, -2, i + 1);
562 int ModApiEnvMod::l_get_meta(lua_State *L)
567 v3s16 p = read_v3s16(L, 1);
568 NodeMetaRef::create(L, p, env);
572 // get_node_timer(pos)
573 int ModApiEnvMod::l_get_node_timer(lua_State *L)
578 v3s16 p = read_v3s16(L, 1);
579 NodeTimerRef::create(L, p, env);
583 // add_entity(pos, entityname, [staticdata]) -> ObjectRef or nil
584 // pos = {x=num, y=num, z=num}
585 int ModApiEnvMod::l_add_entity(lua_State *L)
590 v3f pos = checkFloatPos(L, 1);
592 const char *name = luaL_checkstring(L, 2);
594 const char *staticdata = luaL_optstring(L, 3, "");
596 ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, staticdata);
597 int objectid = env->addActiveObject(obj);
598 // If failed to add, return nothing (reads as nil)
602 getScriptApiBase(L)->objectrefGetOrCreate(L, obj);
606 // add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil
607 // pos = {x=num, y=num, z=num}
608 int ModApiEnvMod::l_add_item(lua_State *L)
613 //v3f pos = checkFloatPos(L, 1);
615 ItemStack item = read_item(L, 2,getServer(L)->idef());
616 if(item.empty() || !item.isKnown(getServer(L)->idef()))
619 int error_handler = PUSH_ERROR_HANDLER(L);
621 // Use spawn_item to spawn a __builtin:item
622 lua_getglobal(L, "core");
623 lua_getfield(L, -1, "spawn_item");
624 lua_remove(L, -2); // Remove core
628 lua_pushstring(L, item.getItemString().c_str());
630 PCALL_RESL(L, lua_pcall(L, 2, 1, error_handler));
632 lua_remove(L, error_handler);
636 // get_connected_players()
637 int ModApiEnvMod::l_get_connected_players(lua_State *L)
639 ServerEnvironment *env = (ServerEnvironment *) getEnv(L);
641 log_deprecated(L, "Calling get_connected_players() at mod load time"
643 lua_createtable(L, 0, 0);
647 lua_createtable(L, env->getPlayerCount(), 0);
649 for (RemotePlayer *player : env->getPlayers()) {
650 if (player->getPeerId() == PEER_ID_INEXISTENT)
652 PlayerSAO *sao = player->getPlayerSAO();
653 if (sao && !sao->isGone()) {
654 getScriptApiBase(L)->objectrefGetOrCreate(L, sao);
655 lua_rawseti(L, -2, ++i);
661 // get_player_by_name(name)
662 int ModApiEnvMod::l_get_player_by_name(lua_State *L)
667 const char *name = luaL_checkstring(L, 1);
668 RemotePlayer *player = env->getPlayer(name);
669 if (!player || player->getPeerId() == PEER_ID_INEXISTENT)
671 PlayerSAO *sao = player->getPlayerSAO();
672 if (!sao || sao->isGone())
674 // Put player on stack
675 getScriptApiBase(L)->objectrefGetOrCreate(L, sao);
679 // get_objects_inside_radius(pos, radius)
680 int ModApiEnvMod::l_get_objects_inside_radius(lua_State *L)
685 v3f pos = checkFloatPos(L, 1);
686 float radius = readParam<float>(L, 2) * BS;
687 std::vector<u16> ids;
688 env->getObjectsInsideRadius(ids, pos, radius);
689 ScriptApiBase *script = getScriptApiBase(L);
690 lua_createtable(L, ids.size(), 0);
691 std::vector<u16>::const_iterator iter = ids.begin();
692 for(u32 i = 0; iter != ids.end(); ++iter) {
693 ServerActiveObject *obj = env->getActiveObject(*iter);
694 if (!obj->isGone()) {
695 // Insert object reference into table
696 script->objectrefGetOrCreate(L, obj);
697 lua_rawseti(L, -2, ++i);
703 // set_timeofday(val)
705 int ModApiEnvMod::l_set_timeofday(lua_State *L)
710 float timeofday_f = readParam<float>(L, 1);
711 sanity_check(timeofday_f >= 0.0 && timeofday_f <= 1.0);
712 int timeofday_mh = (int)(timeofday_f * 24000.0);
713 // This should be set directly in the environment but currently
714 // such changes aren't immediately sent to the clients, so call
715 // the server instead.
716 //env->setTimeOfDay(timeofday_mh);
717 getServer(L)->setTimeOfDay(timeofday_mh);
721 // get_timeofday() -> 0...1
722 int ModApiEnvMod::l_get_timeofday(lua_State *L)
727 int timeofday_mh = env->getTimeOfDay();
728 float timeofday_f = (float)timeofday_mh / 24000.0f;
729 lua_pushnumber(L, timeofday_f);
733 // get_day_count() -> int
734 int ModApiEnvMod::l_get_day_count(lua_State *L)
738 lua_pushnumber(L, env->getDayCount());
743 int ModApiEnvMod::l_get_gametime(lua_State *L)
747 int game_time = env->getGameTime();
748 lua_pushnumber(L, game_time);
753 // find_node_near(pos, radius, nodenames, search_center) -> pos or nil
754 // nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
755 int ModApiEnvMod::l_find_node_near(lua_State *L)
759 const NodeDefManager *ndef = env->getGameDef()->ndef();
760 v3s16 pos = read_v3s16(L, 1);
761 int radius = luaL_checkinteger(L, 2);
762 std::vector<content_t> filter;
763 if (lua_istable(L, 3)) {
765 while (lua_next(L, 3) != 0) {
766 // key at index -2 and value at index -1
767 luaL_checktype(L, -1, LUA_TSTRING);
768 ndef->getIds(readParam<std::string>(L, -1), filter);
769 // removes value, keeps key for next iteration
772 } else if (lua_isstring(L, 3)) {
773 ndef->getIds(readParam<std::string>(L, 3), filter);
776 int start_radius = (lua_isboolean(L, 4) && readParam<bool>(L, 4)) ? 0 : 1;
779 // Client API limitations
781 radius = getClient(L)->CSMClampRadius(pos, radius);
784 for (int d = start_radius; d <= radius; d++) {
785 std::vector<v3s16> list = FacePositionCache::getFacePositions(d);
786 for (const v3s16 &i : list) {
788 content_t c = env->getMap().getNode(p).getContent();
789 if (CONTAINS(filter, c)) {
798 // find_nodes_in_area(minp, maxp, nodenames) -> list of positions
799 // nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
800 int ModApiEnvMod::l_find_nodes_in_area(lua_State *L)
804 v3s16 minp = read_v3s16(L, 1);
805 v3s16 maxp = read_v3s16(L, 2);
806 sortBoxVerticies(minp, maxp);
808 const NodeDefManager *ndef = env->getGameDef()->ndef();
812 minp = getClient(L)->CSMClampPos(minp);
813 maxp = getClient(L)->CSMClampPos(maxp);
817 v3s16 cube = maxp - minp + 1;
818 // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000
819 if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) {
820 luaL_error(L, "find_nodes_in_area(): area volume"
821 " exceeds allowed value of 4096000");
825 std::vector<content_t> filter;
826 if (lua_istable(L, 3)) {
828 while (lua_next(L, 3) != 0) {
829 // key at index -2 and value at index -1
830 luaL_checktype(L, -1, LUA_TSTRING);
831 ndef->getIds(readParam<std::string>(L, -1), filter);
832 // removes value, keeps key for next iteration
835 } else if (lua_isstring(L, 3)) {
836 ndef->getIds(readParam<std::string>(L, 3), filter);
839 std::vector<u32> individual_count;
840 individual_count.resize(filter.size());
844 for (s16 x = minp.X; x <= maxp.X; x++)
845 for (s16 y = minp.Y; y <= maxp.Y; y++)
846 for (s16 z = minp.Z; z <= maxp.Z; z++) {
848 content_t c = env->getMap().getNode(p).getContent();
850 std::vector<content_t>::iterator it = std::find(filter.begin(), filter.end(), c);
851 if (it != filter.end()) {
853 lua_rawseti(L, -2, ++i);
855 u32 filt_index = it - filter.begin();
856 individual_count[filt_index]++;
860 for (u32 i = 0; i < filter.size(); i++) {
861 lua_pushnumber(L, individual_count[i]);
862 lua_setfield(L, -2, ndef->get(filter[i]).name.c_str());
867 // find_nodes_in_area_under_air(minp, maxp, nodenames) -> list of positions
868 // nodenames: e.g. {"ignore", "group:tree"} or "default:dirt"
869 int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L)
871 /* Note: A similar but generalized (and therefore slower) version of this
872 * function could be created -- e.g. find_nodes_in_area_under -- which
873 * would accept a node name (or ID?) or list of names that the "above node"
880 v3s16 minp = read_v3s16(L, 1);
881 v3s16 maxp = read_v3s16(L, 2);
882 sortBoxVerticies(minp, maxp);
884 const NodeDefManager *ndef = env->getGameDef()->ndef();
888 minp = getClient(L)->CSMClampPos(minp);
889 maxp = getClient(L)->CSMClampPos(maxp);
893 v3s16 cube = maxp - minp + 1;
894 // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000
895 if ((u64)cube.X * (u64)cube.Y * (u64)cube.Z > 4096000) {
896 luaL_error(L, "find_nodes_in_area_under_air(): area volume"
897 " exceeds allowed value of 4096000");
901 std::vector<content_t> filter;
903 if (lua_istable(L, 3)) {
905 while (lua_next(L, 3) != 0) {
906 // key at index -2 and value at index -1
907 luaL_checktype(L, -1, LUA_TSTRING);
908 ndef->getIds(readParam<std::string>(L, -1), filter);
909 // removes value, keeps key for next iteration
912 } else if (lua_isstring(L, 3)) {
913 ndef->getIds(readParam<std::string>(L, 3), filter);
918 for (s16 x = minp.X; x <= maxp.X; x++)
919 for (s16 z = minp.Z; z <= maxp.Z; z++) {
922 content_t c = env->getMap().getNode(p).getContent();
923 for (; y <= maxp.Y; y++) {
924 v3s16 psurf(x, y + 1, z);
925 content_t csurf = env->getMap().getNode(psurf).getContent();
926 if (c != CONTENT_AIR && csurf == CONTENT_AIR &&
927 CONTAINS(filter, c)) {
928 push_v3s16(L, v3s16(x, y, z));
929 lua_rawseti(L, -2, ++i);
937 // get_perlin(seeddiff, octaves, persistence, scale)
938 // returns world-specific PerlinNoise
939 int ModApiEnvMod::l_get_perlin(lua_State *L)
941 GET_ENV_PTR_NO_MAP_LOCK;
945 if (lua_istable(L, 1)) {
946 read_noiseparams(L, 1, ¶ms);
948 params.seed = luaL_checkint(L, 1);
949 params.octaves = luaL_checkint(L, 2);
950 params.persist = readParam<float>(L, 3);
951 params.spread = v3f(1, 1, 1) * readParam<float>(L, 4);
954 params.seed += (int)env->getServerMap().getSeed();
956 LuaPerlinNoise *n = new LuaPerlinNoise(¶ms);
957 *(void **)(lua_newuserdata(L, sizeof(void *))) = n;
958 luaL_getmetatable(L, "PerlinNoise");
959 lua_setmetatable(L, -2);
963 // get_perlin_map(noiseparams, size)
964 // returns world-specific PerlinNoiseMap
965 int ModApiEnvMod::l_get_perlin_map(lua_State *L)
967 GET_ENV_PTR_NO_MAP_LOCK;
970 if (!read_noiseparams(L, 1, &np))
972 v3s16 size = read_v3s16(L, 2);
974 s32 seed = (s32)(env->getServerMap().getSeed());
975 LuaPerlinNoiseMap *n = new LuaPerlinNoiseMap(&np, seed, size);
976 *(void **)(lua_newuserdata(L, sizeof(void *))) = n;
977 luaL_getmetatable(L, "PerlinNoiseMap");
978 lua_setmetatable(L, -2);
983 // returns voxel manipulator
984 int ModApiEnvMod::l_get_voxel_manip(lua_State *L)
988 Map *map = &(env->getMap());
989 LuaVoxelManip *o = (lua_istable(L, 1) && lua_istable(L, 2)) ?
990 new LuaVoxelManip(map, read_v3s16(L, 1), read_v3s16(L, 2)) :
991 new LuaVoxelManip(map);
993 *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
994 luaL_getmetatable(L, "VoxelManip");
995 lua_setmetatable(L, -2);
999 // clear_objects([options])
1000 // clear all objects in the environment
1001 // where options = {mode = "full" or "quick"}
1002 int ModApiEnvMod::l_clear_objects(lua_State *L)
1006 ClearObjectsMode mode = CLEAR_OBJECTS_MODE_QUICK;
1007 if (lua_istable(L, 1)) {
1008 mode = (ClearObjectsMode)getenumfield(L, 1, "mode",
1009 ModApiEnvMod::es_ClearObjectsMode, mode);
1012 env->clearObjects(mode);
1016 // line_of_sight(pos1, pos2) -> true/false, pos
1017 int ModApiEnvMod::l_line_of_sight(lua_State *L)
1021 // read position 1 from lua
1022 v3f pos1 = checkFloatPos(L, 1);
1023 // read position 2 from lua
1024 v3f pos2 = checkFloatPos(L, 2);
1028 bool success = env->line_of_sight(pos1, pos2, &p);
1029 lua_pushboolean(L, success);
1037 // fix_light(p1, p2)
1038 int ModApiEnvMod::l_fix_light(lua_State *L)
1042 v3s16 blockpos1 = getContainerPos(read_v3s16(L, 1), MAP_BLOCKSIZE);
1043 v3s16 blockpos2 = getContainerPos(read_v3s16(L, 2), MAP_BLOCKSIZE);
1044 ServerMap &map = env->getServerMap();
1045 std::map<v3s16, MapBlock *> modified_blocks;
1046 bool success = true;
1048 for (blockpos.X = blockpos1.X; blockpos.X <= blockpos2.X; blockpos.X++)
1049 for (blockpos.Y = blockpos1.Y; blockpos.Y <= blockpos2.Y; blockpos.Y++)
1050 for (blockpos.Z = blockpos1.Z; blockpos.Z <= blockpos2.Z; blockpos.Z++) {
1051 success = success & map.repairBlockLight(blockpos, &modified_blocks);
1053 if (!modified_blocks.empty()) {
1055 event.type = MEET_OTHER;
1056 for (auto &modified_block : modified_blocks)
1057 event.modified_blocks.insert(modified_block.first);
1059 map.dispatchEvent(event);
1061 lua_pushboolean(L, success);
1066 int ModApiEnvMod::l_raycast(lua_State *L)
1068 return LuaRaycast::create_object(L);
1071 // load_area(p1, [p2])
1072 // load mapblocks in area p1..p2, but do not generate map
1073 int ModApiEnvMod::l_load_area(lua_State *L)
1078 Map *map = &(env->getMap());
1079 v3s16 bp1 = getNodeBlockPos(check_v3s16(L, 1));
1080 if (!lua_istable(L, 2)) {
1081 map->emergeBlock(bp1);
1083 v3s16 bp2 = getNodeBlockPos(check_v3s16(L, 2));
1084 sortBoxVerticies(bp1, bp2);
1085 for (s16 z = bp1.Z; z <= bp2.Z; z++)
1086 for (s16 y = bp1.Y; y <= bp2.Y; y++)
1087 for (s16 x = bp1.X; x <= bp2.X; x++) {
1088 map->emergeBlock(v3s16(x, y, z));
1095 // emerge_area(p1, p2, [callback, context])
1096 // emerge mapblocks in area p1..p2, calls callback with context upon completion
1097 int ModApiEnvMod::l_emerge_area(lua_State *L)
1101 EmergeCompletionCallback callback = NULL;
1102 ScriptCallbackState *state = NULL;
1104 EmergeManager *emerge = getServer(L)->getEmergeManager();
1106 v3s16 bpmin = getNodeBlockPos(read_v3s16(L, 1));
1107 v3s16 bpmax = getNodeBlockPos(read_v3s16(L, 2));
1108 sortBoxVerticies(bpmin, bpmax);
1110 size_t num_blocks = VoxelArea(bpmin, bpmax).getVolume();
1111 assert(num_blocks != 0);
1113 if (lua_isfunction(L, 3)) {
1114 callback = LuaEmergeAreaCallback;
1116 lua_pushvalue(L, 3);
1117 int callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
1119 lua_pushvalue(L, 4);
1120 int args_ref = luaL_ref(L, LUA_REGISTRYINDEX);
1122 state = new ScriptCallbackState;
1123 state->script = getServer(L)->getScriptIface();
1124 state->callback_ref = callback_ref;
1125 state->args_ref = args_ref;
1126 state->refcount = num_blocks;
1127 state->origin = getScriptApiBase(L)->getOrigin();
1130 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1131 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1132 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1133 emerge->enqueueBlockEmergeEx(v3s16(x, y, z), PEER_ID_INEXISTENT,
1134 BLOCK_EMERGE_ALLOW_GEN | BLOCK_EMERGE_FORCE_QUEUE, callback, state);
1140 // delete_area(p1, p2)
1141 // delete mapblocks in area p1..p2
1142 int ModApiEnvMod::l_delete_area(lua_State *L)
1146 v3s16 bpmin = getNodeBlockPos(read_v3s16(L, 1));
1147 v3s16 bpmax = getNodeBlockPos(read_v3s16(L, 2));
1148 sortBoxVerticies(bpmin, bpmax);
1150 ServerMap &map = env->getServerMap();
1153 event.type = MEET_OTHER;
1155 bool success = true;
1156 for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
1157 for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
1158 for (s16 x = bpmin.X; x <= bpmax.X; x++) {
1160 if (map.deleteBlock(bp)) {
1161 env->setStaticForActiveObjectsInBlock(bp, false);
1162 event.modified_blocks.insert(bp);
1168 map.dispatchEvent(event);
1169 lua_pushboolean(L, success);
1173 // find_path(pos1, pos2, searchdistance,
1174 // max_jump, max_drop, algorithm) -> table containing path
1175 int ModApiEnvMod::l_find_path(lua_State *L)
1179 v3s16 pos1 = read_v3s16(L, 1);
1180 v3s16 pos2 = read_v3s16(L, 2);
1181 unsigned int searchdistance = luaL_checkint(L, 3);
1182 unsigned int max_jump = luaL_checkint(L, 4);
1183 unsigned int max_drop = luaL_checkint(L, 5);
1184 PathAlgorithm algo = PA_PLAIN_NP;
1185 if (!lua_isnoneornil(L, 6)) {
1186 std::string algorithm = luaL_checkstring(L,6);
1188 if (algorithm == "A*")
1191 if (algorithm == "Dijkstra")
1195 std::vector<v3s16> path = get_path(env, pos1, pos2,
1196 searchdistance, max_jump, max_drop, algo);
1198 if (!path.empty()) {
1200 int top = lua_gettop(L);
1201 unsigned int index = 1;
1202 for (const v3s16 &i : path) {
1203 lua_pushnumber(L,index);
1205 lua_settable(L, top);
1214 // spawn_tree(pos, treedef)
1215 int ModApiEnvMod::l_spawn_tree(lua_State *L)
1219 v3s16 p0 = read_v3s16(L, 1);
1221 treegen::TreeDef tree_def;
1222 std::string trunk,leaves,fruit;
1223 const NodeDefManager *ndef = env->getGameDef()->ndef();
1225 if(lua_istable(L, 2))
1227 getstringfield(L, 2, "axiom", tree_def.initial_axiom);
1228 getstringfield(L, 2, "rules_a", tree_def.rules_a);
1229 getstringfield(L, 2, "rules_b", tree_def.rules_b);
1230 getstringfield(L, 2, "rules_c", tree_def.rules_c);
1231 getstringfield(L, 2, "rules_d", tree_def.rules_d);
1232 getstringfield(L, 2, "trunk", trunk);
1233 tree_def.trunknode=ndef->getId(trunk);
1234 getstringfield(L, 2, "leaves", leaves);
1235 tree_def.leavesnode=ndef->getId(leaves);
1236 tree_def.leaves2_chance=0;
1237 getstringfield(L, 2, "leaves2", leaves);
1238 if (!leaves.empty()) {
1239 tree_def.leaves2node=ndef->getId(leaves);
1240 getintfield(L, 2, "leaves2_chance", tree_def.leaves2_chance);
1242 getintfield(L, 2, "angle", tree_def.angle);
1243 getintfield(L, 2, "iterations", tree_def.iterations);
1244 if (!getintfield(L, 2, "random_level", tree_def.iterations_random_level))
1245 tree_def.iterations_random_level = 0;
1246 getstringfield(L, 2, "trunk_type", tree_def.trunk_type);
1247 getboolfield(L, 2, "thin_branches", tree_def.thin_branches);
1248 tree_def.fruit_chance=0;
1249 getstringfield(L, 2, "fruit", fruit);
1250 if (!fruit.empty()) {
1251 tree_def.fruitnode=ndef->getId(fruit);
1252 getintfield(L, 2, "fruit_chance",tree_def.fruit_chance);
1254 tree_def.explicit_seed = getintfield(L, 2, "seed", tree_def.seed);
1260 if ((e = treegen::spawn_ltree (env, p0, ndef, tree_def)) != treegen::SUCCESS) {
1261 if (e == treegen::UNBALANCED_BRACKETS) {
1262 luaL_error(L, "spawn_tree(): closing ']' has no matching opening bracket");
1264 luaL_error(L, "spawn_tree(): unknown error");
1271 // transforming_liquid_add(pos)
1272 int ModApiEnvMod::l_transforming_liquid_add(lua_State *L)
1276 v3s16 p0 = read_v3s16(L, 1);
1277 env->getMap().transforming_liquid_add(p0);
1281 // forceload_block(blockpos)
1282 // blockpos = {x=num, y=num, z=num}
1283 int ModApiEnvMod::l_forceload_block(lua_State *L)
1287 v3s16 blockpos = read_v3s16(L, 1);
1288 env->getForceloadedBlocks()->insert(blockpos);
1292 // forceload_free_block(blockpos)
1293 // blockpos = {x=num, y=num, z=num}
1294 int ModApiEnvMod::l_forceload_free_block(lua_State *L)
1298 v3s16 blockpos = read_v3s16(L, 1);
1299 env->getForceloadedBlocks()->erase(blockpos);
1303 void ModApiEnvMod::Initialize(lua_State *L, int top)
1306 API_FCT(bulk_set_node);
1310 API_FCT(remove_node);
1312 API_FCT(get_node_or_nil);
1313 API_FCT(get_node_light);
1314 API_FCT(place_node);
1316 API_FCT(punch_node);
1317 API_FCT(get_node_max_level);
1318 API_FCT(get_node_level);
1319 API_FCT(set_node_level);
1320 API_FCT(add_node_level);
1321 API_FCT(add_entity);
1322 API_FCT(find_nodes_with_meta);
1324 API_FCT(get_node_timer);
1325 API_FCT(get_connected_players);
1326 API_FCT(get_player_by_name);
1327 API_FCT(get_objects_inside_radius);
1328 API_FCT(set_timeofday);
1329 API_FCT(get_timeofday);
1330 API_FCT(get_gametime);
1331 API_FCT(get_day_count);
1332 API_FCT(find_node_near);
1333 API_FCT(find_nodes_in_area);
1334 API_FCT(find_nodes_in_area_under_air);
1337 API_FCT(emerge_area);
1338 API_FCT(delete_area);
1339 API_FCT(get_perlin);
1340 API_FCT(get_perlin_map);
1341 API_FCT(get_voxel_manip);
1342 API_FCT(clear_objects);
1343 API_FCT(spawn_tree);
1345 API_FCT(line_of_sight);
1347 API_FCT(transforming_liquid_add);
1348 API_FCT(forceload_block);
1349 API_FCT(forceload_free_block);
1352 void ModApiEnvMod::InitializeClient(lua_State *L, int top)
1354 API_FCT(get_node_light);
1355 API_FCT(get_timeofday);
1356 API_FCT(get_node_max_level);
1357 API_FCT(get_node_level);
1358 API_FCT(find_nodes_with_meta);
1359 API_FCT(find_node_near);
1360 API_FCT(find_nodes_in_area);
1361 API_FCT(find_nodes_in_area_under_air);
1362 API_FCT(line_of_sight);