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.
25 #include "lua_api/l_base.h"
26 #include "common/c_internal.h"
28 #include "common/c_converter.h"
29 #include "common/c_content.h"
30 #include "lua_api/luaapi.h"
36 #include "main.h" //required for g_settings
38 struct EnumString ModApiBasic::es_OreType[] =
40 {ORE_SCATTER, "scatter"},
42 {ORE_CLAYLIKE, "claylike"},
46 struct EnumString ModApiBasic::es_DecorationType[] =
48 {DECO_SIMPLE, "simple"},
49 {DECO_SCHEMATIC, "schematic"},
50 {DECO_LSYSTEM, "lsystem"},
55 ModApiBasic::ModApiBasic() : ModApiBase() {
58 bool ModApiBasic::Initialize(lua_State* L,int top) {
62 retval &= API_FCT(debug);
63 retval &= API_FCT(log);
64 retval &= API_FCT(request_shutdown);
65 retval &= API_FCT(get_server_status);
67 retval &= API_FCT(register_biome);
69 retval &= API_FCT(setting_set);
70 retval &= API_FCT(setting_get);
71 retval &= API_FCT(setting_getbool);
72 retval &= API_FCT(setting_save);
74 retval &= API_FCT(chat_send_all);
75 retval &= API_FCT(chat_send_player);
76 retval &= API_FCT(show_formspec);
78 retval &= API_FCT(get_player_privs);
79 retval &= API_FCT(get_player_ip);
80 retval &= API_FCT(get_ban_list);
81 retval &= API_FCT(get_ban_description);
82 retval &= API_FCT(ban_player);
83 retval &= API_FCT(unban_player_or_ip);
84 retval &= API_FCT(get_password_hash);
85 retval &= API_FCT(notify_authentication_modified);
87 retval &= API_FCT(get_dig_params);
88 retval &= API_FCT(get_hit_params);
90 retval &= API_FCT(get_current_modname);
91 retval &= API_FCT(get_modpath);
92 retval &= API_FCT(get_modnames);
94 retval &= API_FCT(get_worldpath);
95 retval &= API_FCT(is_singleplayer);
96 retval &= API_FCT(sound_play);
97 retval &= API_FCT(sound_stop);
99 retval &= API_FCT(rollback_get_last_node_actor);
100 retval &= API_FCT(rollback_revert_actions_by);
102 retval &= API_FCT(register_ore);
103 retval &= API_FCT(register_decoration);
104 retval &= API_FCT(create_schematic);
105 retval &= API_FCT(place_schematic);
111 // Writes a line to dstream
112 int ModApiBasic::l_debug(lua_State *L)
114 NO_MAP_LOCK_REQUIRED;
115 // Handle multiple parameters to behave like standard lua print()
116 int n = lua_gettop(L);
117 lua_getglobal(L, "tostring");
118 for(int i = 1; i <= n; i++){
120 Call tostring(i-th argument).
121 This is what print() does, and it behaves a bit
122 differently from directly calling lua_tostring.
124 lua_pushvalue(L, -1); /* function to be called */
125 lua_pushvalue(L, i); /* value to print */
127 const char *s = lua_tostring(L, -1);
134 dstream << std::endl;
138 // log([level,] text)
139 // Writes a line to the logger.
140 // The one-argument version logs to infostream.
141 // The two-argument version accept a log level: error, action, info, or verbose.
142 int ModApiBasic::l_log(lua_State *L)
144 NO_MAP_LOCK_REQUIRED;
146 LogMessageLevel level = LMT_INFO;
149 text = lua_tostring(L, 1);
153 std::string levelname = luaL_checkstring(L, 1);
154 text = luaL_checkstring(L, 2);
155 if(levelname == "error")
157 else if(levelname == "action")
159 else if(levelname == "verbose")
162 log_printline(level, text);
166 // request_shutdown()
167 int ModApiBasic::l_request_shutdown(lua_State *L)
169 getServer(L)->requestShutdown();
173 // get_server_status()
174 int ModApiBasic::l_get_server_status(lua_State *L)
176 NO_MAP_LOCK_REQUIRED;
177 lua_pushstring(L, wide_to_narrow(getServer(L)->getStatusString()).c_str());
181 // register_biome({lots of stuff})
182 int ModApiBasic::l_register_biome(lua_State *L)
185 luaL_checktype(L, index, LUA_TTABLE);
187 BiomeDefManager *bmgr = getServer(L)->getEmergeManager()->biomedef;
189 verbosestream << "register_biome: BiomeDefManager not active" << std::endl;
193 enum BiomeTerrainType terrain = (BiomeTerrainType)getenumfield(L, index,
194 "terrain_type", es_BiomeTerrainType, BIOME_TERRAIN_NORMAL);
195 Biome *b = bmgr->createBiome(terrain);
197 b->name = getstringfield_default(L, index, "name", "");
198 b->top_nodename = getstringfield_default(L, index, "top_node", "");
199 b->top_depth = getintfield_default(L, index, "top_depth", 0);
200 b->filler_nodename = getstringfield_default(L, index, "filler_node", "");
201 b->filler_height = getintfield_default(L, index, "filler_height", 0);
202 b->height_min = getintfield_default(L, index, "height_min", 0);
203 b->height_max = getintfield_default(L, index, "height_max", 0);
204 b->heat_point = getfloatfield_default(L, index, "heat_point", 0.);
205 b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.);
207 b->flags = 0; //reserved
208 b->c_top = CONTENT_IGNORE;
209 b->c_filler = CONTENT_IGNORE;
210 verbosestream << "register_biome: " << b->name << std::endl;
216 // setting_set(name, value)
217 int ModApiBasic::l_setting_set(lua_State *L)
219 NO_MAP_LOCK_REQUIRED;
220 const char *name = luaL_checkstring(L, 1);
221 const char *value = luaL_checkstring(L, 2);
222 g_settings->set(name, value);
227 int ModApiBasic::l_setting_get(lua_State *L)
229 NO_MAP_LOCK_REQUIRED;
230 const char *name = luaL_checkstring(L, 1);
232 std::string value = g_settings->get(name);
233 lua_pushstring(L, value.c_str());
234 } catch(SettingNotFoundException &e){
240 // setting_getbool(name)
241 int ModApiBasic::l_setting_getbool(lua_State *L)
243 NO_MAP_LOCK_REQUIRED;
244 const char *name = luaL_checkstring(L, 1);
246 bool value = g_settings->getBool(name);
247 lua_pushboolean(L, value);
248 } catch(SettingNotFoundException &e){
255 int ModApiBasic::l_setting_save(lua_State *L)
257 NO_MAP_LOCK_REQUIRED;
258 getServer(L)->saveConfig();
262 // chat_send_all(text)
263 int ModApiBasic::l_chat_send_all(lua_State *L)
265 NO_MAP_LOCK_REQUIRED;
266 const char *text = luaL_checkstring(L, 1);
267 // Get server from registry
268 Server *server = getServer(L);
270 server->notifyPlayers(narrow_to_wide(text));
274 // chat_send_player(name, text, prepend)
275 int ModApiBasic::l_chat_send_player(lua_State *L)
277 NO_MAP_LOCK_REQUIRED;
278 const char *name = luaL_checkstring(L, 1);
279 const char *text = luaL_checkstring(L, 2);
282 if (lua_isboolean(L, 3))
283 prepend = lua_toboolean(L, 3);
285 // Get server from registry
286 Server *server = getServer(L);
288 server->notifyPlayer(name, narrow_to_wide(text), prepend);
292 // get_player_privs(name, text)
293 int ModApiBasic::l_get_player_privs(lua_State *L)
295 NO_MAP_LOCK_REQUIRED;
296 const char *name = luaL_checkstring(L, 1);
297 // Get server from registry
298 Server *server = getServer(L);
301 int table = lua_gettop(L);
302 std::set<std::string> privs_s = server->getPlayerEffectivePrivs(name);
303 for(std::set<std::string>::const_iterator
304 i = privs_s.begin(); i != privs_s.end(); i++){
305 lua_pushboolean(L, true);
306 lua_setfield(L, table, i->c_str());
308 lua_pushvalue(L, table);
313 int ModApiBasic::l_get_player_ip(lua_State *L)
315 NO_MAP_LOCK_REQUIRED;
316 const char * name = luaL_checkstring(L, 1);
317 Player *player = getEnv(L)->getPlayer(name);
320 lua_pushnil(L); // no such player
325 Address addr = getServer(L)->getPeerAddress(getEnv(L)->getPlayer(name)->peer_id);
326 std::string ip_str = addr.serializeString();
327 lua_pushstring(L, ip_str.c_str());
330 catch(con::PeerNotFoundException) // unlikely
332 dstream << __FUNCTION_NAME << ": peer was not found" << std::endl;
333 lua_pushnil(L); // error
339 int ModApiBasic::l_get_ban_list(lua_State *L)
341 NO_MAP_LOCK_REQUIRED;
342 lua_pushstring(L, getServer(L)->getBanDescription("").c_str());
346 // get_ban_description()
347 int ModApiBasic::l_get_ban_description(lua_State *L)
349 NO_MAP_LOCK_REQUIRED;
350 const char * ip_or_name = luaL_checkstring(L, 1);
351 lua_pushstring(L, getServer(L)->getBanDescription(std::string(ip_or_name)).c_str());
356 int ModApiBasic::l_ban_player(lua_State *L)
358 NO_MAP_LOCK_REQUIRED;
359 const char * name = luaL_checkstring(L, 1);
360 Player *player = getEnv(L)->getPlayer(name);
363 lua_pushboolean(L, false); // no such player
368 Address addr = getServer(L)->getPeerAddress(getEnv(L)->getPlayer(name)->peer_id);
369 std::string ip_str = addr.serializeString();
370 getServer(L)->setIpBanned(ip_str, name);
372 catch(con::PeerNotFoundException) // unlikely
374 dstream << __FUNCTION_NAME << ": peer was not found" << std::endl;
375 lua_pushboolean(L, false); // error
378 lua_pushboolean(L, true);
382 // unban_player_or_ip()
383 int ModApiBasic::l_unban_player_or_ip(lua_State *L)
385 NO_MAP_LOCK_REQUIRED;
386 const char * ip_or_name = luaL_checkstring(L, 1);
387 getServer(L)->unsetIpBanned(ip_or_name);
388 lua_pushboolean(L, true);
392 // show_formspec(playername,formname,formspec)
393 int ModApiBasic::l_show_formspec(lua_State *L)
395 NO_MAP_LOCK_REQUIRED;
396 const char *playername = luaL_checkstring(L, 1);
397 const char *formname = luaL_checkstring(L, 2);
398 const char *formspec = luaL_checkstring(L, 3);
400 if(getServer(L)->showFormspec(playername,formspec,formname))
402 lua_pushboolean(L, true);
404 lua_pushboolean(L, false);
409 // get_dig_params(groups, tool_capabilities[, time_from_last_punch])
410 int ModApiBasic::l_get_dig_params(lua_State *L)
412 NO_MAP_LOCK_REQUIRED;
413 std::map<std::string, int> groups;
414 read_groups(L, 1, groups);
415 ToolCapabilities tp = read_tool_capabilities(L, 2);
416 if(lua_isnoneornil(L, 3))
417 push_dig_params(L, getDigParams(groups, &tp));
419 push_dig_params(L, getDigParams(groups, &tp,
420 luaL_checknumber(L, 3)));
424 // get_hit_params(groups, tool_capabilities[, time_from_last_punch])
425 int ModApiBasic::l_get_hit_params(lua_State *L)
427 NO_MAP_LOCK_REQUIRED;
428 std::map<std::string, int> groups;
429 read_groups(L, 1, groups);
430 ToolCapabilities tp = read_tool_capabilities(L, 2);
431 if(lua_isnoneornil(L, 3))
432 push_hit_params(L, getHitParams(groups, &tp));
434 push_hit_params(L, getHitParams(groups, &tp,
435 luaL_checknumber(L, 3)));
439 // get_current_modname()
440 int ModApiBasic::l_get_current_modname(lua_State *L)
442 NO_MAP_LOCK_REQUIRED;
443 lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
447 // get_modpath(modname)
448 int ModApiBasic::l_get_modpath(lua_State *L)
450 NO_MAP_LOCK_REQUIRED;
451 std::string modname = luaL_checkstring(L, 1);
453 if(modname == "__builtin"){
454 std::string path = getServer(L)->getBuiltinLuaPath();
455 lua_pushstring(L, path.c_str());
458 const ModSpec *mod = getServer(L)->getModSpec(modname);
463 lua_pushstring(L, mod->path.c_str());
468 // the returned list is sorted alphabetically for you
469 int ModApiBasic::l_get_modnames(lua_State *L)
471 NO_MAP_LOCK_REQUIRED;
472 // Get a list of mods
473 std::list<std::string> mods_unsorted, mods_sorted;
474 getServer(L)->getModNames(mods_unsorted);
476 // Take unsorted items from mods_unsorted and sort them into
477 // mods_sorted; not great performance but the number of mods on a
478 // server will likely be small.
479 for(std::list<std::string>::iterator i = mods_unsorted.begin();
480 i != mods_unsorted.end(); ++i)
483 for(std::list<std::string>::iterator x = mods_sorted.begin();
484 x != mods_sorted.end(); ++x)
486 // I doubt anybody using Minetest will be using
487 // anything not ASCII based :)
488 if((*i).compare(*x) <= 0)
490 mods_sorted.insert(x, *i);
496 mods_sorted.push_back(*i);
499 // Get the table insertion function from Lua.
500 lua_getglobal(L, "table");
501 lua_getfield(L, -1, "insert");
502 int insertion_func = lua_gettop(L);
504 // Package them up for Lua
506 int new_table = lua_gettop(L);
507 std::list<std::string>::iterator i = mods_sorted.begin();
508 while(i != mods_sorted.end())
510 lua_pushvalue(L, insertion_func);
511 lua_pushvalue(L, new_table);
512 lua_pushstring(L, (*i).c_str());
513 if(lua_pcall(L, 2, 0, 0) != 0)
515 script_error(L, "error: %s", lua_tostring(L, -1));
523 int ModApiBasic::l_get_worldpath(lua_State *L)
525 NO_MAP_LOCK_REQUIRED;
526 std::string worldpath = getServer(L)->getWorldPath();
527 lua_pushstring(L, worldpath.c_str());
531 // sound_play(spec, parameters)
532 int ModApiBasic::l_sound_play(lua_State *L)
534 NO_MAP_LOCK_REQUIRED;
535 SimpleSoundSpec spec;
536 read_soundspec(L, 1, spec);
537 ServerSoundParams params;
538 read_server_sound_params(L, 2, params);
539 s32 handle = getServer(L)->playSound(spec, params);
540 lua_pushinteger(L, handle);
544 // sound_stop(handle)
545 int ModApiBasic::l_sound_stop(lua_State *L)
547 NO_MAP_LOCK_REQUIRED;
548 int handle = luaL_checkinteger(L, 1);
549 getServer(L)->stopSound(handle);
554 int ModApiBasic::l_is_singleplayer(lua_State *L)
556 NO_MAP_LOCK_REQUIRED;
557 lua_pushboolean(L, getServer(L)->isSingleplayer());
561 // get_password_hash(name, raw_password)
562 int ModApiBasic::l_get_password_hash(lua_State *L)
564 NO_MAP_LOCK_REQUIRED;
565 std::string name = luaL_checkstring(L, 1);
566 std::string raw_password = luaL_checkstring(L, 2);
567 std::string hash = translatePassword(name,
568 narrow_to_wide(raw_password));
569 lua_pushstring(L, hash.c_str());
573 // notify_authentication_modified(name)
574 int ModApiBasic::l_notify_authentication_modified(lua_State *L)
576 NO_MAP_LOCK_REQUIRED;
577 std::string name = "";
578 if(lua_isstring(L, 1))
579 name = lua_tostring(L, 1);
580 getServer(L)->reportPrivsModified(name);
584 // rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds
585 int ModApiBasic::l_rollback_get_last_node_actor(lua_State *L)
587 v3s16 p = read_v3s16(L, 1);
588 int range = luaL_checknumber(L, 2);
589 int seconds = luaL_checknumber(L, 3);
590 Server *server = getServer(L);
591 IRollbackManager *rollback = server->getRollbackManager();
594 std::string actor = rollback->getLastNodeActor(p, range, seconds, &act_p, &act_seconds);
595 lua_pushstring(L, actor.c_str());
596 push_v3s16(L, act_p);
597 lua_pushnumber(L, act_seconds);
601 // rollback_revert_actions_by(actor, seconds) -> bool, log messages
602 int ModApiBasic::l_rollback_revert_actions_by(lua_State *L)
604 std::string actor = luaL_checkstring(L, 1);
605 int seconds = luaL_checknumber(L, 2);
606 Server *server = getServer(L);
607 IRollbackManager *rollback = server->getRollbackManager();
608 std::list<RollbackAction> actions = rollback->getRevertActions(actor, seconds);
609 std::list<std::string> log;
610 bool success = server->rollbackRevertActions(actions, &log);
611 // Push boolean result
612 lua_pushboolean(L, success);
613 // Get the table insert function and push the log table
614 lua_getglobal(L, "table");
615 lua_getfield(L, -1, "insert");
616 int table_insert = lua_gettop(L);
618 int table = lua_gettop(L);
619 for(std::list<std::string>::const_iterator i = log.begin();
622 lua_pushvalue(L, table_insert);
623 lua_pushvalue(L, table);
624 lua_pushstring(L, i->c_str());
625 if(lua_pcall(L, 2, 0, 0))
626 script_error(L, "error: %s", lua_tostring(L, -1));
628 lua_remove(L, -2); // Remove table
629 lua_remove(L, -2); // Remove insert
633 int ModApiBasic::l_register_ore(lua_State *L)
636 luaL_checktype(L, index, LUA_TTABLE);
638 EmergeManager *emerge = getServer(L)->getEmergeManager();
640 enum OreType oretype = (OreType)getenumfield(L, index,
641 "ore_type", es_OreType, ORE_SCATTER);
642 Ore *ore = createOre(oretype);
644 errorstream << "register_ore: ore_type "
645 << oretype << " not implemented";
649 ore->ore_name = getstringfield_default(L, index, "ore", "");
650 ore->ore_param2 = (u8)getintfield_default(L, index, "ore_param2", 0);
651 ore->wherein_name = getstringfield_default(L, index, "wherein", "");
652 ore->clust_scarcity = getintfield_default(L, index, "clust_scarcity", 1);
653 ore->clust_num_ores = getintfield_default(L, index, "clust_num_ores", 1);
654 ore->clust_size = getintfield_default(L, index, "clust_size", 0);
655 ore->height_min = getintfield_default(L, index, "height_min", 0);
656 ore->height_max = getintfield_default(L, index, "height_max", 0);
657 ore->flags = getflagsfield(L, index, "flags", flagdesc_ore);
658 ore->nthresh = getfloatfield_default(L, index, "noise_threshhold", 0.);
660 lua_getfield(L, index, "noise_params");
661 ore->np = read_noiseparams(L, -1);
666 if (ore->clust_scarcity <= 0 || ore->clust_num_ores <= 0) {
667 errorstream << "register_ore: clust_scarcity and clust_num_ores"
668 "must be greater than 0" << std::endl;
673 emerge->ores.push_back(ore);
675 verbosestream << "register_ore: ore '" << ore->ore_name
676 << "' registered" << std::endl;
680 // register_decoration({lots of stuff})
681 int ModApiBasic::l_register_decoration(lua_State *L)
684 luaL_checktype(L, index, LUA_TTABLE);
686 EmergeManager *emerge = getServer(L)->getEmergeManager();
687 BiomeDefManager *bdef = emerge->biomedef;
689 enum DecorationType decotype = (DecorationType)getenumfield(L, index,
690 "deco_type", es_DecorationType, -1);
691 if (decotype == -1) {
692 errorstream << "register_decoration: unrecognized "
693 "decoration placement type";
697 Decoration *deco = createDecoration(decotype);
699 errorstream << "register_decoration: decoration placement type "
700 << decotype << " not implemented";
704 deco->c_place_on = CONTENT_IGNORE;
705 deco->place_on_name = getstringfield_default(L, index, "place_on", "ignore");
706 deco->fill_ratio = getfloatfield_default(L, index, "fill_ratio", 0.02);
707 deco->sidelen = getintfield_default(L, index, "sidelen", 8);
708 if (deco->sidelen <= 0) {
709 errorstream << "register_decoration: sidelen must be "
710 "greater than 0" << std::endl;
715 lua_getfield(L, index, "noise_params");
716 deco->np = read_noiseparams(L, -1);
719 lua_getfield(L, index, "biomes");
720 if (lua_istable(L, -1)) {
722 while (lua_next(L, -2)) {
723 const char *s = lua_tostring(L, -1);
724 u8 biomeid = bdef->getBiomeIdByName(s);
726 deco->biomes.insert(biomeid);
735 DecoSimple *dsimple = (DecoSimple *)deco;
736 dsimple->c_deco = CONTENT_IGNORE;
737 dsimple->c_spawnby = CONTENT_IGNORE;
738 dsimple->spawnby_name = getstringfield_default(L, index, "spawn_by", "air");
739 dsimple->deco_height = getintfield_default(L, index, "height", 1);
740 dsimple->deco_height_max = getintfield_default(L, index, "height_max", 0);
741 dsimple->nspawnby = getintfield_default(L, index, "num_spawn_by", -1);
743 lua_getfield(L, index, "decoration");
744 if (lua_istable(L, -1)) {
746 while (lua_next(L, -2)) {
747 const char *s = lua_tostring(L, -1);
749 dsimple->decolist_names.push_back(str);
753 } else if (lua_isstring(L, -1)) {
754 dsimple->deco_name = std::string(lua_tostring(L, -1));
756 dsimple->deco_name = std::string("air");
760 if (dsimple->deco_height <= 0) {
761 errorstream << "register_decoration: simple decoration height"
762 " must be greater than 0" << std::endl;
768 case DECO_SCHEMATIC: {
769 DecoSchematic *dschem = (DecoSchematic *)deco;
770 dschem->flags = getflagsfield(L, index, "flags", flagdesc_deco_schematic);
772 lua_getfield(L, index, "schematic");
773 if (!read_schematic(L, -1, dschem, getServer(L))) {
779 if (!dschem->filename.empty() && !dschem->loadSchematicFile()) {
780 errorstream << "register_decoration: failed to load schematic file '"
781 << dschem->filename << "'" << std::endl;
787 //DecoLSystem *decolsystem = (DecoLSystem *)deco;
792 emerge->decorations.push_back(deco);
794 verbosestream << "register_decoration: decoration '" << deco->getName()
795 << "' registered" << std::endl;
799 // create_schematic(p1, p2, probability_list, filename)
800 int ModApiBasic::l_create_schematic(lua_State *L)
802 DecoSchematic dschem;
804 Map *map = &(getEnv(L)->getMap());
805 INodeDefManager *ndef = getServer(L)->getNodeDefManager();
807 v3s16 p1 = read_v3s16(L, 1);
808 v3s16 p2 = read_v3s16(L, 2);
809 sortBoxVerticies(p1, p2);
811 std::vector<std::pair<v3s16, u8> > probability_list;
812 if (lua_istable(L, 3)) {
814 while (lua_next(L, 3)) {
815 if (lua_istable(L, -1)) {
816 lua_getfield(L, -1, "pos");
817 v3s16 pos = read_v3s16(L, -1);
820 int prob = getintfield_default(L, -1, "prob", 0);
821 if (prob < 0 || prob >= UCHAR_MAX) {
822 errorstream << "create_schematic: probability value of "
823 << prob << " at " << PP(pos) << " out of range" << std::endl;
825 probability_list.push_back(std::make_pair(pos, (u8)prob));
833 dschem.filename = std::string(lua_tostring(L, 4));
835 if (!dschem.getSchematicFromMap(map, p1, p2)) {
836 errorstream << "create_schematic: failed to get schematic "
837 "from map" << std::endl;
841 dschem.applyProbabilities(&probability_list, p1);
843 dschem.saveSchematicFile(ndef);
844 actionstream << "create_schematic: saved schematic file '"
845 << dschem.filename << "'." << std::endl;
851 // place_schematic(p, schematic)
852 int ModApiBasic::l_place_schematic(lua_State *L)
854 DecoSchematic dschem;
856 Map *map = &(getEnv(L)->getMap());
857 INodeDefManager *ndef = getServer(L)->getNodeDefManager();
859 v3s16 p = read_v3s16(L, 1);
860 if (!read_schematic(L, 2, &dschem, getServer(L)))
863 if (!dschem.filename.empty()) {
864 if (!dschem.loadSchematicFile()) {
865 errorstream << "place_schematic: failed to load schematic file '"
866 << dschem.filename << "'" << std::endl;
869 dschem.resolveNodeNames(ndef);
872 dschem.placeStructure(map, p);
878 ModApiBasic modapibasic_prototype;