A handful of minor fixes to various things
[oweals/minetest.git] / src / script / lua_api / luaapi.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 extern "C" {
21 #include "lua.h"
22 #include "lauxlib.h"
23 }
24
25 #include "lua_api/l_base.h"
26 #include "common/c_internal.h"
27 #include "server.h"
28 #include "common/c_converter.h"
29 #include "common/c_content.h"
30 #include "lua_api/luaapi.h"
31 #include "settings.h"
32 #include "tool.h"
33 #include "rollback.h"
34 #include "log.h"
35 #include "emerge.h"
36 #include "main.h"  //required for g_settings
37
38 struct EnumString ModApiBasic::es_OreType[] =
39 {
40         {ORE_SCATTER,  "scatter"},
41         {ORE_SHEET,    "sheet"},
42         {ORE_CLAYLIKE, "claylike"},
43         {0, NULL},
44 };
45
46 struct EnumString ModApiBasic::es_DecorationType[] =
47 {
48         {DECO_SIMPLE,    "simple"},
49         {DECO_SCHEMATIC, "schematic"},
50         {DECO_LSYSTEM,   "lsystem"},
51         {0, NULL},
52 };
53
54
55 ModApiBasic::ModApiBasic() : ModApiBase() {
56 }
57
58 bool ModApiBasic::Initialize(lua_State* L,int top) {
59
60         bool retval = true;
61
62         retval &= API_FCT(debug);
63         retval &= API_FCT(log);
64         retval &= API_FCT(request_shutdown);
65         retval &= API_FCT(get_server_status);
66
67         retval &= API_FCT(register_biome);
68
69         retval &= API_FCT(setting_set);
70         retval &= API_FCT(setting_get);
71         retval &= API_FCT(setting_getbool);
72         retval &= API_FCT(setting_save);
73
74         retval &= API_FCT(chat_send_all);
75         retval &= API_FCT(chat_send_player);
76         retval &= API_FCT(show_formspec);
77
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);
86
87         retval &= API_FCT(get_dig_params);
88         retval &= API_FCT(get_hit_params);
89
90         retval &= API_FCT(get_current_modname);
91         retval &= API_FCT(get_modpath);
92         retval &= API_FCT(get_modnames);
93
94         retval &= API_FCT(get_worldpath);
95         retval &= API_FCT(is_singleplayer);
96         retval &= API_FCT(sound_play);
97         retval &= API_FCT(sound_stop);
98
99         retval &= API_FCT(rollback_get_last_node_actor);
100         retval &= API_FCT(rollback_revert_actions_by);
101
102         retval &= API_FCT(register_ore);
103         retval &= API_FCT(register_decoration);
104         retval &= API_FCT(create_schematic);
105         retval &= API_FCT(place_schematic);
106
107         return retval;
108 }
109
110 // debug(...)
111 // Writes a line to dstream
112 int ModApiBasic::l_debug(lua_State *L)
113 {
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++){
119                 /*
120                         Call tostring(i-th argument).
121                         This is what print() does, and it behaves a bit
122                         differently from directly calling lua_tostring.
123                 */
124                 lua_pushvalue(L, -1);  /* function to be called */
125                 lua_pushvalue(L, i);   /* value to print */
126                 lua_call(L, 1, 1);
127                 const char *s = lua_tostring(L, -1);
128                 if(i>1)
129                         dstream << "\t";
130                 if(s)
131                         dstream << s;
132                 lua_pop(L, 1);
133         }
134         dstream << std::endl;
135         return 0;
136 }
137
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)
143 {
144         NO_MAP_LOCK_REQUIRED;
145         std::string text;
146         LogMessageLevel level = LMT_INFO;
147         if(lua_isnone(L, 2))
148         {
149                 text = lua_tostring(L, 1);
150         }
151         else
152         {
153                 std::string levelname = luaL_checkstring(L, 1);
154                 text = luaL_checkstring(L, 2);
155                 if(levelname == "error")
156                         level = LMT_ERROR;
157                 else if(levelname == "action")
158                         level = LMT_ACTION;
159                 else if(levelname == "verbose")
160                         level = LMT_VERBOSE;
161         }
162         log_printline(level, text);
163         return 0;
164 }
165
166 // request_shutdown()
167 int ModApiBasic::l_request_shutdown(lua_State *L)
168 {
169         getServer(L)->requestShutdown();
170         return 0;
171 }
172
173 // get_server_status()
174 int ModApiBasic::l_get_server_status(lua_State *L)
175 {
176         NO_MAP_LOCK_REQUIRED;
177         lua_pushstring(L, wide_to_narrow(getServer(L)->getStatusString()).c_str());
178         return 1;
179 }
180
181 // register_biome({lots of stuff})
182 int ModApiBasic::l_register_biome(lua_State *L)
183 {
184         int index = 1;
185         luaL_checktype(L, index, LUA_TTABLE);
186
187         BiomeDefManager *bmgr = getServer(L)->getEmergeManager()->biomedef;
188         if (!bmgr) {
189                 verbosestream << "register_biome: BiomeDefManager not active" << std::endl;
190                 return 0;
191         }
192
193         enum BiomeTerrainType terrain = (BiomeTerrainType)getenumfield(L, index,
194                                 "terrain_type", es_BiomeTerrainType, BIOME_TERRAIN_NORMAL);
195         Biome *b = bmgr->createBiome(terrain);
196
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.);
206
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;
211         bmgr->addBiome(b);
212
213         return 0;
214 }
215
216 // setting_set(name, value)
217 int ModApiBasic::l_setting_set(lua_State *L)
218 {
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);
223         return 0;
224 }
225
226 // setting_get(name)
227 int ModApiBasic::l_setting_get(lua_State *L)
228 {
229         NO_MAP_LOCK_REQUIRED;
230         const char *name = luaL_checkstring(L, 1);
231         try{
232                 std::string value = g_settings->get(name);
233                 lua_pushstring(L, value.c_str());
234         } catch(SettingNotFoundException &e){
235                 lua_pushnil(L);
236         }
237         return 1;
238 }
239
240 // setting_getbool(name)
241 int ModApiBasic::l_setting_getbool(lua_State *L)
242 {
243         NO_MAP_LOCK_REQUIRED;
244         const char *name = luaL_checkstring(L, 1);
245         try{
246                 bool value = g_settings->getBool(name);
247                 lua_pushboolean(L, value);
248         } catch(SettingNotFoundException &e){
249                 lua_pushnil(L);
250         }
251         return 1;
252 }
253
254 // setting_save()
255 int ModApiBasic::l_setting_save(lua_State *L)
256 {
257         NO_MAP_LOCK_REQUIRED;
258         getServer(L)->saveConfig();
259         return 0;
260 }
261
262 // chat_send_all(text)
263 int ModApiBasic::l_chat_send_all(lua_State *L)
264 {
265         NO_MAP_LOCK_REQUIRED;
266         const char *text = luaL_checkstring(L, 1);
267         // Get server from registry
268         Server *server = getServer(L);
269         // Send
270         server->notifyPlayers(narrow_to_wide(text));
271         return 0;
272 }
273
274 // chat_send_player(name, text, prepend)
275 int ModApiBasic::l_chat_send_player(lua_State *L)
276 {
277         NO_MAP_LOCK_REQUIRED;
278         const char *name = luaL_checkstring(L, 1);
279         const char *text = luaL_checkstring(L, 2);
280         bool prepend = true;
281
282         if (lua_isboolean(L, 3))
283                 prepend = lua_toboolean(L, 3);
284
285         // Get server from registry
286         Server *server = getServer(L);
287         // Send
288         server->notifyPlayer(name, narrow_to_wide(text), prepend);
289         return 0;
290 }
291
292 // get_player_privs(name, text)
293 int ModApiBasic::l_get_player_privs(lua_State *L)
294 {
295         NO_MAP_LOCK_REQUIRED;
296         const char *name = luaL_checkstring(L, 1);
297         // Get server from registry
298         Server *server = getServer(L);
299         // Do it
300         lua_newtable(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());
307         }
308         lua_pushvalue(L, table);
309         return 1;
310 }
311
312 // get_player_ip()
313 int ModApiBasic::l_get_player_ip(lua_State *L)
314 {
315         NO_MAP_LOCK_REQUIRED;
316         const char * name = luaL_checkstring(L, 1);
317         Player *player = getEnv(L)->getPlayer(name);
318         if(player == NULL)
319         {
320                 lua_pushnil(L); // no such player
321                 return 1;
322         }
323         try
324         {
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());
328                 return 1;
329         }
330         catch(con::PeerNotFoundException) // unlikely
331         {
332                 dstream << __FUNCTION_NAME << ": peer was not found" << std::endl;
333                 lua_pushnil(L); // error
334                 return 1;
335         }
336 }
337
338 // get_ban_list()
339 int ModApiBasic::l_get_ban_list(lua_State *L)
340 {
341         NO_MAP_LOCK_REQUIRED;
342         lua_pushstring(L, getServer(L)->getBanDescription("").c_str());
343         return 1;
344 }
345
346 // get_ban_description()
347 int ModApiBasic::l_get_ban_description(lua_State *L)
348 {
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());
352         return 1;
353 }
354
355 // ban_player()
356 int ModApiBasic::l_ban_player(lua_State *L)
357 {
358         NO_MAP_LOCK_REQUIRED;
359         const char * name = luaL_checkstring(L, 1);
360         Player *player = getEnv(L)->getPlayer(name);
361         if(player == NULL)
362         {
363                 lua_pushboolean(L, false); // no such player
364                 return 1;
365         }
366         try
367         {
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);
371         }
372         catch(con::PeerNotFoundException) // unlikely
373         {
374                 dstream << __FUNCTION_NAME << ": peer was not found" << std::endl;
375                 lua_pushboolean(L, false); // error
376                 return 1;
377         }
378         lua_pushboolean(L, true);
379         return 1;
380 }
381
382 // unban_player_or_ip()
383 int ModApiBasic::l_unban_player_or_ip(lua_State *L)
384 {
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);
389         return 1;
390 }
391
392 // show_formspec(playername,formname,formspec)
393 int ModApiBasic::l_show_formspec(lua_State *L)
394 {
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);
399
400         if(getServer(L)->showFormspec(playername,formspec,formname))
401         {
402                 lua_pushboolean(L, true);
403         }else{
404                 lua_pushboolean(L, false);
405         }
406         return 1;
407 }
408
409 // get_dig_params(groups, tool_capabilities[, time_from_last_punch])
410 int ModApiBasic::l_get_dig_params(lua_State *L)
411 {
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));
418         else
419                 push_dig_params(L, getDigParams(groups, &tp,
420                                         luaL_checknumber(L, 3)));
421         return 1;
422 }
423
424 // get_hit_params(groups, tool_capabilities[, time_from_last_punch])
425 int ModApiBasic::l_get_hit_params(lua_State *L)
426 {
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));
433         else
434                 push_hit_params(L, getHitParams(groups, &tp,
435                                         luaL_checknumber(L, 3)));
436         return 1;
437 }
438
439 // get_current_modname()
440 int ModApiBasic::l_get_current_modname(lua_State *L)
441 {
442         NO_MAP_LOCK_REQUIRED;
443         lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
444         return 1;
445 }
446
447 // get_modpath(modname)
448 int ModApiBasic::l_get_modpath(lua_State *L)
449 {
450         NO_MAP_LOCK_REQUIRED;
451         std::string modname = luaL_checkstring(L, 1);
452         // Do it
453         if(modname == "__builtin"){
454                 std::string path = getServer(L)->getBuiltinLuaPath();
455                 lua_pushstring(L, path.c_str());
456                 return 1;
457         }
458         const ModSpec *mod = getServer(L)->getModSpec(modname);
459         if(!mod){
460                 lua_pushnil(L);
461                 return 1;
462         }
463         lua_pushstring(L, mod->path.c_str());
464         return 1;
465 }
466
467 // get_modnames()
468 // the returned list is sorted alphabetically for you
469 int ModApiBasic::l_get_modnames(lua_State *L)
470 {
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);
475
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)
481         {
482                 bool added = false;
483                 for(std::list<std::string>::iterator x = mods_sorted.begin();
484                         x != mods_sorted.end(); ++x)
485                 {
486                         // I doubt anybody using Minetest will be using
487                         // anything not ASCII based :)
488                         if((*i).compare(*x) <= 0)
489                         {
490                                 mods_sorted.insert(x, *i);
491                                 added = true;
492                                 break;
493                         }
494                 }
495                 if(!added)
496                         mods_sorted.push_back(*i);
497         }
498
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);
503
504         // Package them up for Lua
505         lua_newtable(L);
506         int new_table = lua_gettop(L);
507         std::list<std::string>::iterator i = mods_sorted.begin();
508         while(i != mods_sorted.end())
509         {
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)
514                 {
515                         script_error(L, "error: %s", lua_tostring(L, -1));
516                 }
517                 ++i;
518         }
519         return 1;
520 }
521
522 // get_worldpath()
523 int ModApiBasic::l_get_worldpath(lua_State *L)
524 {
525         NO_MAP_LOCK_REQUIRED;
526         std::string worldpath = getServer(L)->getWorldPath();
527         lua_pushstring(L, worldpath.c_str());
528         return 1;
529 }
530
531 // sound_play(spec, parameters)
532 int ModApiBasic::l_sound_play(lua_State *L)
533 {
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);
541         return 1;
542 }
543
544 // sound_stop(handle)
545 int ModApiBasic::l_sound_stop(lua_State *L)
546 {
547         NO_MAP_LOCK_REQUIRED;
548         int handle = luaL_checkinteger(L, 1);
549         getServer(L)->stopSound(handle);
550         return 0;
551 }
552
553 // is_singleplayer()
554 int ModApiBasic::l_is_singleplayer(lua_State *L)
555 {
556         NO_MAP_LOCK_REQUIRED;
557         lua_pushboolean(L, getServer(L)->isSingleplayer());
558         return 1;
559 }
560
561 // get_password_hash(name, raw_password)
562 int ModApiBasic::l_get_password_hash(lua_State *L)
563 {
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());
570         return 1;
571 }
572
573 // notify_authentication_modified(name)
574 int ModApiBasic::l_notify_authentication_modified(lua_State *L)
575 {
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);
581         return 0;
582 }
583
584 // rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds
585 int ModApiBasic::l_rollback_get_last_node_actor(lua_State *L)
586 {
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();
592         v3s16 act_p;
593         int act_seconds = 0;
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);
598         return 3;
599 }
600
601 // rollback_revert_actions_by(actor, seconds) -> bool, log messages
602 int ModApiBasic::l_rollback_revert_actions_by(lua_State *L)
603 {
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);
617         lua_newtable(L);
618         int table = lua_gettop(L);
619         for(std::list<std::string>::const_iterator i = log.begin();
620                         i != log.end(); i++)
621         {
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));
627         }
628         lua_remove(L, -2); // Remove table
629         lua_remove(L, -2); // Remove insert
630         return 2;
631 }
632
633 int ModApiBasic::l_register_ore(lua_State *L)
634 {
635         int index = 1;
636         luaL_checktype(L, index, LUA_TTABLE);
637
638         EmergeManager *emerge = getServer(L)->getEmergeManager();
639
640         enum OreType oretype = (OreType)getenumfield(L, index,
641                                 "ore_type", es_OreType, ORE_SCATTER);
642         Ore *ore = createOre(oretype);
643         if (!ore) {
644                 errorstream << "register_ore: ore_type "
645                         << oretype << " not implemented";
646                 return 0;
647         }
648
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.);
659
660         lua_getfield(L, index, "noise_params");
661         ore->np = read_noiseparams(L, -1);
662         lua_pop(L, 1);
663
664         ore->noise = NULL;
665
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;
669                 delete ore;
670                 return 0;
671         }
672
673         emerge->ores.push_back(ore);
674
675         verbosestream << "register_ore: ore '" << ore->ore_name
676                 << "' registered" << std::endl;
677         return 1;
678 }
679
680 // register_decoration({lots of stuff})
681 int ModApiBasic::l_register_decoration(lua_State *L)
682 {
683         int index = 1;
684         luaL_checktype(L, index, LUA_TTABLE);
685
686         EmergeManager *emerge = getServer(L)->getEmergeManager();
687         BiomeDefManager *bdef = emerge->biomedef;
688
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";
694                 return 0;
695         }
696         
697         Decoration *deco = createDecoration(decotype);
698         if (!deco) {
699                 errorstream << "register_decoration: decoration placement type "
700                         << decotype << " not implemented";
701                 return 0;
702         }
703
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;
711                 delete deco;
712                 return 0;
713         }
714         
715         lua_getfield(L, index, "noise_params");
716         deco->np = read_noiseparams(L, -1);
717         lua_pop(L, 1);
718         
719         lua_getfield(L, index, "biomes");
720         if (lua_istable(L, -1)) {
721                 lua_pushnil(L);
722                 while (lua_next(L, -2)) {
723                         const char *s = lua_tostring(L, -1);
724                         u8 biomeid = bdef->getBiomeIdByName(s);
725                         if (biomeid)
726                                 deco->biomes.insert(biomeid);
727
728                         lua_pop(L, 1);
729                 }
730                 lua_pop(L, 1);
731         }
732         
733         switch (decotype) {
734                 case DECO_SIMPLE: {
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);
742                         
743                         lua_getfield(L, index, "decoration");
744                         if (lua_istable(L, -1)) {
745                                 lua_pushnil(L);
746                                 while (lua_next(L, -2)) {
747                                         const char *s = lua_tostring(L, -1);
748                                         std::string str(s);
749                                         dsimple->decolist_names.push_back(str);
750
751                                         lua_pop(L, 1);
752                                 }
753                         } else if (lua_isstring(L, -1)) {
754                                 dsimple->deco_name = std::string(lua_tostring(L, -1));
755                         } else {
756                                 dsimple->deco_name = std::string("air");
757                         }
758                         lua_pop(L, 1);
759                         
760                         if (dsimple->deco_height <= 0) {
761                                 errorstream << "register_decoration: simple decoration height"
762                                         " must be greater than 0" << std::endl;
763                                 delete dsimple;
764                                 return 0;
765                         }
766
767                         break; }
768                 case DECO_SCHEMATIC: {
769                         DecoSchematic *dschem = (DecoSchematic *)deco;
770                         dschem->flags = getflagsfield(L, index, "flags", flagdesc_deco_schematic);
771                         
772                         lua_getfield(L, index, "schematic");
773                         if (!read_schematic(L, -1, dschem, getServer(L))) {
774                                 delete dschem;
775                                 return 0;
776                         }
777                         lua_pop(L, -1);
778                         
779                         if (!dschem->filename.empty() && !dschem->loadSchematicFile()) {
780                                 errorstream << "register_decoration: failed to load schematic file '"
781                                         << dschem->filename << "'" << std::endl;
782                                 delete dschem;
783                                 return 0;
784                         }
785                         break; }
786                 case DECO_LSYSTEM: {
787                         //DecoLSystem *decolsystem = (DecoLSystem *)deco;
788                 
789                         break; }
790         }
791
792         emerge->decorations.push_back(deco);
793
794         verbosestream << "register_decoration: decoration '" << deco->getName()
795                 << "' registered" << std::endl;
796         return 1;
797 }
798
799 // create_schematic(p1, p2, probability_list, filename)
800 int ModApiBasic::l_create_schematic(lua_State *L)
801 {
802         DecoSchematic dschem;
803
804         Map *map = &(getEnv(L)->getMap());
805         INodeDefManager *ndef = getServer(L)->getNodeDefManager();
806
807         v3s16 p1 = read_v3s16(L, 1);
808         v3s16 p2 = read_v3s16(L, 2);
809         sortBoxVerticies(p1, p2);
810         
811         std::vector<std::pair<v3s16, u8> > probability_list;
812         if (lua_istable(L, 3)) {
813                 lua_pushnil(L);
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);
818                                 lua_pop(L, 1);
819                                 
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;
824                                 } else {
825                                         probability_list.push_back(std::make_pair(pos, (u8)prob));
826                                 }
827                         }
828
829                         lua_pop(L, 1);
830                 }
831         }
832         
833         dschem.filename = std::string(lua_tostring(L, 4));
834
835         if (!dschem.getSchematicFromMap(map, p1, p2)) {
836                 errorstream << "create_schematic: failed to get schematic "
837                         "from map" << std::endl;
838                 return 0;
839         }
840         
841         dschem.applyProbabilities(&probability_list, p1);
842         
843         dschem.saveSchematicFile(ndef);
844         actionstream << "create_schematic: saved schematic file '"
845                 << dschem.filename << "'." << std::endl;
846
847         return 1;
848 }
849
850
851 // place_schematic(p, schematic)
852 int ModApiBasic::l_place_schematic(lua_State *L)
853 {
854         DecoSchematic dschem;
855
856         Map *map = &(getEnv(L)->getMap());
857         INodeDefManager *ndef = getServer(L)->getNodeDefManager();
858
859         v3s16 p = read_v3s16(L, 1);
860         if (!read_schematic(L, 2, &dschem, getServer(L)))
861                 return 0;
862
863         if (!dschem.filename.empty()) {
864                 if (!dschem.loadSchematicFile()) {
865                         errorstream << "place_schematic: failed to load schematic file '"
866                                 << dschem.filename << "'" << std::endl;
867                         return 0;
868                 }
869                 dschem.resolveNodeNames(ndef);
870         }
871         
872         dschem.placeStructure(map, p);
873
874         return 1;
875 }
876
877
878 ModApiBasic modapibasic_prototype;