]> git.lizzy.rs Git - minetest.git/blob - src/script/lua_api/l_mainmenu.cpp
Add minetest.get_player_window_information() (#12367)
[minetest.git] / src / script / lua_api / l_mainmenu.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 sapier
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 #include "lua_api/l_mainmenu.h"
21 #include "lua_api/l_internal.h"
22 #include "common/c_content.h"
23 #include "cpp_api/s_async.h"
24 #include "scripting_mainmenu.h"
25 #include "gui/guiEngine.h"
26 #include "gui/guiMainMenu.h"
27 #include "gui/guiKeyChangeMenu.h"
28 #include "gui/guiPathSelectMenu.h"
29 #include "version.h"
30 #include "porting.h"
31 #include "filesys.h"
32 #include "convert_json.h"
33 #include "content/content.h"
34 #include "content/subgames.h"
35 #include "serverlist.h"
36 #include "mapgen/mapgen.h"
37 #include "settings.h"
38 #include "client/client.h"
39 #include "client/renderingengine.h"
40 #include "network/networkprotocol.h"
41 #include "content/mod_configuration.h"
42 #include "threading/mutex_auto_lock.h"
43 #include "common/c_converter.h"
44
45 /******************************************************************************/
46 std::string ModApiMainMenu::getTextData(lua_State *L, std::string name)
47 {
48         lua_getglobal(L, "gamedata");
49
50         lua_getfield(L, -1, name.c_str());
51
52         if(lua_isnil(L, -1))
53                 return "";
54
55         return luaL_checkstring(L, -1);
56 }
57
58 /******************************************************************************/
59 int ModApiMainMenu::getIntegerData(lua_State *L, std::string name,bool& valid)
60 {
61         lua_getglobal(L, "gamedata");
62
63         lua_getfield(L, -1, name.c_str());
64
65         if(lua_isnil(L, -1)) {
66                 valid = false;
67                 return -1;
68                 }
69
70         valid = true;
71         return luaL_checkinteger(L, -1);
72 }
73
74 /******************************************************************************/
75 int ModApiMainMenu::getBoolData(lua_State *L, std::string name,bool& valid)
76 {
77         lua_getglobal(L, "gamedata");
78
79         lua_getfield(L, -1, name.c_str());
80
81         if(lua_isnil(L, -1)) {
82                 valid = false;
83                 return false;
84                 }
85
86         valid = true;
87         return readParam<bool>(L, -1);
88 }
89
90 /******************************************************************************/
91 int ModApiMainMenu::l_update_formspec(lua_State *L)
92 {
93         GUIEngine* engine = getGuiEngine(L);
94         sanity_check(engine != NULL);
95
96         if (engine->m_startgame)
97                 return 0;
98
99         //read formspec
100         std::string formspec(luaL_checkstring(L, 1));
101
102         if (engine->m_formspecgui != 0) {
103                 engine->m_formspecgui->setForm(formspec);
104         }
105
106         return 0;
107 }
108
109 /******************************************************************************/
110 int ModApiMainMenu::l_set_formspec_prepend(lua_State *L)
111 {
112         GUIEngine *engine = getGuiEngine(L);
113         sanity_check(engine != NULL);
114
115         if (engine->m_startgame)
116                 return 0;
117
118         std::string formspec(luaL_checkstring(L, 1));
119         engine->setFormspecPrepend(formspec);
120
121         return 0;
122 }
123
124 /******************************************************************************/
125 int ModApiMainMenu::l_start(lua_State *L)
126 {
127         GUIEngine* engine = getGuiEngine(L);
128         sanity_check(engine != NULL);
129
130         //update c++ gamedata from lua table
131
132         bool valid = false;
133
134         MainMenuData *data = engine->m_data;
135
136         data->selected_world = getIntegerData(L, "selected_world",valid) -1;
137         data->simple_singleplayer_mode = getBoolData(L,"singleplayer",valid);
138         data->do_reconnect = getBoolData(L, "do_reconnect", valid);
139         if (!data->do_reconnect) {
140                 data->name     = getTextData(L,"playername");
141                 data->password = getTextData(L,"password");
142                 data->address  = getTextData(L,"address");
143                 data->port     = getTextData(L,"port");
144
145                 const auto val = getTextData(L, "allow_login_or_register");
146                 if (val == "login")
147                         data->allow_login_or_register = ELoginRegister::Login;
148                 else if (val == "register")
149                         data->allow_login_or_register = ELoginRegister::Register;
150                 else
151                         data->allow_login_or_register = ELoginRegister::Any;
152         }
153         data->serverdescription = getTextData(L,"serverdescription");
154         data->servername        = getTextData(L,"servername");
155
156         //close menu next time
157         engine->m_startgame = true;
158         return 0;
159 }
160
161 /******************************************************************************/
162 int ModApiMainMenu::l_close(lua_State *L)
163 {
164         GUIEngine* engine = getGuiEngine(L);
165         sanity_check(engine != NULL);
166
167         engine->m_kill = true;
168         return 0;
169 }
170
171 /******************************************************************************/
172 int ModApiMainMenu::l_set_background(lua_State *L)
173 {
174         GUIEngine* engine = getGuiEngine(L);
175         sanity_check(engine != NULL);
176
177         std::string backgroundlevel(luaL_checkstring(L, 1));
178         std::string texturename(luaL_checkstring(L, 2));
179
180         bool tile_image = false;
181         bool retval     = false;
182         unsigned int minsize = 16;
183
184         if (!lua_isnone(L, 3)) {
185                 tile_image = readParam<bool>(L, 3);
186         }
187
188         if (!lua_isnone(L, 4)) {
189                 minsize = lua_tonumber(L, 4);
190         }
191
192         if (backgroundlevel == "background") {
193                 retval |= engine->setTexture(TEX_LAYER_BACKGROUND, texturename,
194                                 tile_image, minsize);
195         }
196
197         if (backgroundlevel == "overlay") {
198                 retval |= engine->setTexture(TEX_LAYER_OVERLAY, texturename,
199                                 tile_image, minsize);
200         }
201
202         if (backgroundlevel == "header") {
203                 retval |= engine->setTexture(TEX_LAYER_HEADER,  texturename,
204                                 tile_image, minsize);
205         }
206
207         if (backgroundlevel == "footer") {
208                 retval |= engine->setTexture(TEX_LAYER_FOOTER, texturename,
209                                 tile_image, minsize);
210         }
211
212         lua_pushboolean(L,retval);
213         return 1;
214 }
215
216 /******************************************************************************/
217 int ModApiMainMenu::l_set_clouds(lua_State *L)
218 {
219         GUIEngine* engine = getGuiEngine(L);
220         sanity_check(engine != NULL);
221
222         bool value = readParam<bool>(L,1);
223
224         engine->m_clouds_enabled = value;
225
226         return 0;
227 }
228
229 /******************************************************************************/
230 int ModApiMainMenu::l_get_textlist_index(lua_State *L)
231 {
232         // get_table_index accepts both tables and textlists
233         return l_get_table_index(L);
234 }
235
236 /******************************************************************************/
237 int ModApiMainMenu::l_get_table_index(lua_State *L)
238 {
239         GUIEngine* engine = getGuiEngine(L);
240         sanity_check(engine != NULL);
241
242         std::string tablename(luaL_checkstring(L, 1));
243         GUITable *table = engine->m_menu->getTable(tablename);
244         s32 selection = table ? table->getSelected() : 0;
245
246         if (selection >= 1)
247                 lua_pushinteger(L, selection);
248         else
249                 lua_pushnil(L);
250         return 1;
251 }
252
253 /******************************************************************************/
254 int ModApiMainMenu::l_get_worlds(lua_State *L)
255 {
256         std::vector<WorldSpec> worlds = getAvailableWorlds();
257
258         lua_newtable(L);
259         int top = lua_gettop(L);
260         unsigned int index = 1;
261
262         for (const WorldSpec &world : worlds) {
263                 lua_pushnumber(L,index);
264
265                 lua_newtable(L);
266                 int top_lvl2 = lua_gettop(L);
267
268                 lua_pushstring(L,"path");
269                 lua_pushstring(L, world.path.c_str());
270                 lua_settable(L, top_lvl2);
271
272                 lua_pushstring(L,"name");
273                 lua_pushstring(L, world.name.c_str());
274                 lua_settable(L, top_lvl2);
275
276                 lua_pushstring(L,"gameid");
277                 lua_pushstring(L, world.gameid.c_str());
278                 lua_settable(L, top_lvl2);
279
280                 lua_settable(L, top);
281                 index++;
282         }
283         return 1;
284 }
285
286 /******************************************************************************/
287 int ModApiMainMenu::l_get_games(lua_State *L)
288 {
289         std::vector<SubgameSpec> games = getAvailableGames();
290
291         lua_newtable(L);
292         int top = lua_gettop(L);
293         unsigned int index = 1;
294
295         for (const SubgameSpec &game : games) {
296                 lua_pushnumber(L, index);
297                 lua_newtable(L);
298                 int top_lvl2 = lua_gettop(L);
299
300                 lua_pushstring(L,  "id");
301                 lua_pushstring(L,  game.id.c_str());
302                 lua_settable(L,    top_lvl2);
303
304                 lua_pushstring(L,  "path");
305                 lua_pushstring(L,  game.path.c_str());
306                 lua_settable(L,    top_lvl2);
307
308                 lua_pushstring(L,  "type");
309                 lua_pushstring(L,  "game");
310                 lua_settable(L,    top_lvl2);
311
312                 lua_pushstring(L,  "gamemods_path");
313                 lua_pushstring(L,  game.gamemods_path.c_str());
314                 lua_settable(L,    top_lvl2);
315
316                 lua_pushstring(L,  "name");
317                 lua_pushstring(L,  game.title.c_str());
318                 lua_settable(L,    top_lvl2);
319
320                 lua_pushstring(L,  "title");
321                 lua_pushstring(L,  game.title.c_str());
322                 lua_settable(L,    top_lvl2);
323
324                 lua_pushstring(L,  "author");
325                 lua_pushstring(L,  game.author.c_str());
326                 lua_settable(L,    top_lvl2);
327
328                 lua_pushstring(L,  "release");
329                 lua_pushinteger(L, game.release);
330                 lua_settable(L,    top_lvl2);
331
332                 lua_pushstring(L,  "menuicon_path");
333                 lua_pushstring(L,  game.menuicon_path.c_str());
334                 lua_settable(L,    top_lvl2);
335
336                 lua_pushstring(L, "addon_mods_paths");
337                 lua_newtable(L);
338                 int table2 = lua_gettop(L);
339                 int internal_index = 1;
340                 for (const auto &addon_mods_path : game.addon_mods_paths) {
341                         lua_pushnumber(L, internal_index);
342                         lua_pushstring(L, addon_mods_path.second.c_str());
343                         lua_settable(L,   table2);
344                         internal_index++;
345                 }
346                 lua_settable(L, top_lvl2);
347                 lua_settable(L, top);
348                 index++;
349         }
350         return 1;
351 }
352
353 /******************************************************************************/
354 int ModApiMainMenu::l_get_content_info(lua_State *L)
355 {
356         std::string path = luaL_checkstring(L, 1);
357
358         ContentSpec spec;
359         spec.path = path;
360         parseContentInfo(spec);
361
362         lua_newtable(L);
363
364         lua_pushstring(L, spec.name.c_str());
365         lua_setfield(L, -2, "name");
366
367         lua_pushstring(L, spec.type.c_str());
368         lua_setfield(L, -2, "type");
369
370         lua_pushstring(L, spec.author.c_str());
371         lua_setfield(L, -2, "author");
372
373         if (!spec.title.empty()) {
374                 lua_pushstring(L, spec.title.c_str());
375                 lua_setfield(L, -2, "title");
376         }
377
378         lua_pushinteger(L, spec.release);
379         lua_setfield(L, -2, "release");
380
381         lua_pushstring(L, spec.desc.c_str());
382         lua_setfield(L, -2, "description");
383
384         lua_pushstring(L, spec.path.c_str());
385         lua_setfield(L, -2, "path");
386
387         if (spec.type == "mod") {
388                 ModSpec spec;
389                 spec.path = path;
390                 parseModContents(spec);
391
392                 // Dependencies
393                 lua_newtable(L);
394                 int i = 1;
395                 for (const auto &dep : spec.depends) {
396                         lua_pushstring(L, dep.c_str());
397                         lua_rawseti(L, -2, i++);
398                 }
399                 lua_setfield(L, -2, "depends");
400
401                 // Optional Dependencies
402                 lua_newtable(L);
403                 i = 1;
404                 for (const auto &dep : spec.optdepends) {
405                         lua_pushstring(L, dep.c_str());
406                         lua_rawseti(L, -2, i++);
407                 }
408                 lua_setfield(L, -2, "optional_depends");
409         }
410
411         return 1;
412 }
413
414 /******************************************************************************/
415 int ModApiMainMenu::l_check_mod_configuration(lua_State *L)
416 {
417         std::string worldpath = luaL_checkstring(L, 1);
418
419         ModConfiguration modmgr;
420
421         // Add all game mods
422         SubgameSpec gamespec = findWorldSubgame(worldpath);
423         modmgr.addGameMods(gamespec);
424         modmgr.addModsInPath(worldpath + DIR_DELIM + "worldmods", "worldmods");
425
426         // Add user-configured mods
427         std::vector<ModSpec> modSpecs;
428
429         luaL_checktype(L, 2, LUA_TTABLE);
430
431         lua_pushnil(L);
432         while (lua_next(L, 2)) {
433                 // Ignore non-string keys
434                 if (lua_type(L, -2) != LUA_TSTRING) {
435                         throw LuaError(
436                                         "Unexpected non-string key in table passed to "
437                                         "core.check_mod_configuration");
438                 }
439
440                 std::string modpath = luaL_checkstring(L, -1);
441                 lua_pop(L, 1);
442                 std::string virtual_path = lua_tostring(L, -1);
443
444                 modSpecs.emplace_back();
445                 ModSpec &spec = modSpecs.back();
446                 spec.name = fs::GetFilenameFromPath(modpath.c_str());
447                 spec.path = modpath;
448                 spec.virtual_path = virtual_path;
449                 if (!parseModContents(spec)) {
450                         throw LuaError("Not a mod!");
451                 }
452         }
453
454         modmgr.addMods(modSpecs);
455         try {
456                 modmgr.checkConflictsAndDeps();
457         } catch (const ModError &err) {
458                 errorstream << err.what() << std::endl;
459
460                 lua_newtable(L);
461
462                 lua_pushboolean(L, false);
463                 lua_setfield(L, -2, "is_consistent");
464
465                 lua_newtable(L);
466                 lua_setfield(L, -2, "unsatisfied_mods");
467
468                 lua_newtable(L);
469                 lua_setfield(L, -2, "satisfied_mods");
470
471                 lua_pushstring(L, err.what());
472                 lua_setfield(L, -2, "error_message");
473                 return 1;
474         }
475
476
477         lua_newtable(L);
478
479         lua_pushboolean(L, modmgr.isConsistent());
480         lua_setfield(L, -2, "is_consistent");
481
482         lua_newtable(L);
483         int top = lua_gettop(L);
484         unsigned int index = 1;
485         for (const auto &spec : modmgr.getUnsatisfiedMods()) {
486                 lua_pushnumber(L, index);
487                 push_mod_spec(L, spec, true);
488                 lua_settable(L, top);
489                 index++;
490         }
491
492         lua_setfield(L, -2, "unsatisfied_mods");
493
494         lua_newtable(L);
495         top = lua_gettop(L);
496         index = 1;
497         for (const auto &spec : modmgr.getMods()) {
498                 lua_pushnumber(L, index);
499                 push_mod_spec(L, spec, false);
500                 lua_settable(L, top);
501                 index++;
502         }
503         lua_setfield(L, -2, "satisfied_mods");
504
505         return 1;
506 }
507
508 /******************************************************************************/
509 int ModApiMainMenu::l_show_keys_menu(lua_State *L)
510 {
511         GUIEngine* engine = getGuiEngine(L);
512         sanity_check(engine != NULL);
513
514         GUIKeyChangeMenu *kmenu = new GUIKeyChangeMenu(
515                         engine->m_rendering_engine->get_gui_env(),
516                         engine->m_parent,
517                         -1,
518                         engine->m_menumanager,
519                         engine->m_texture_source);
520         kmenu->drop();
521         return 0;
522 }
523
524 /******************************************************************************/
525 int ModApiMainMenu::l_create_world(lua_State *L)
526 {
527         const char *name   = luaL_checkstring(L, 1);
528         const char *gameid = luaL_checkstring(L, 2);
529
530         StringMap use_settings;
531         luaL_checktype(L, 3, LUA_TTABLE);
532         lua_pushnil(L);
533         while (lua_next(L, 3) != 0) {
534                 // key at index -2 and value at index -1
535                 use_settings[luaL_checkstring(L, -2)] = luaL_checkstring(L, -1);
536                 lua_pop(L, 1);
537         }
538         lua_pop(L, 1);
539
540         std::string path = porting::path_user + DIR_DELIM
541                         "worlds" + DIR_DELIM
542                         + sanitizeDirName(name, "world_");
543
544         std::vector<SubgameSpec> games = getAvailableGames();
545         auto game_it = std::find_if(games.begin(), games.end(), [gameid] (const SubgameSpec &spec) {
546                 return spec.id == gameid;
547         });
548         if (game_it == games.end()) {
549                 lua_pushstring(L, "Game ID not found");
550                 return 1;
551         }
552
553         // Set the settings for world creation
554         // this is a bad hack but the best we have right now..
555         StringMap backup;
556         for (auto it : use_settings) {
557                 if (g_settings->existsLocal(it.first))
558                         backup[it.first] = g_settings->get(it.first);
559                 g_settings->set(it.first, it.second);
560         }
561
562         // Create world if it doesn't exist
563         try {
564                 loadGameConfAndInitWorld(path, name, *game_it, true);
565                 lua_pushnil(L);
566         } catch (const BaseException &e) {
567                 auto err = std::string("Failed to initialize world: ") + e.what();
568                 lua_pushstring(L, err.c_str());
569         }
570
571         // Restore previous settings
572         for (auto it : use_settings) {
573                 auto it2 = backup.find(it.first);
574                 if (it2 == backup.end())
575                         g_settings->remove(it.first); // wasn't set before
576                 else
577                         g_settings->set(it.first, it2->second); // was set before
578         }
579
580         return 1;
581 }
582
583 /******************************************************************************/
584 int ModApiMainMenu::l_delete_world(lua_State *L)
585 {
586         int world_id = luaL_checkinteger(L, 1) - 1;
587         std::vector<WorldSpec> worlds = getAvailableWorlds();
588         if (world_id < 0 || world_id >= (int) worlds.size()) {
589                 lua_pushstring(L, "Invalid world index");
590                 return 1;
591         }
592         const WorldSpec &spec = worlds[world_id];
593         if (!fs::RecursiveDelete(spec.path)) {
594                 lua_pushstring(L, "Failed to delete world");
595                 return 1;
596         }
597         return 0;
598 }
599
600 /******************************************************************************/
601 int ModApiMainMenu::l_set_topleft_text(lua_State *L)
602 {
603         GUIEngine* engine = getGuiEngine(L);
604         sanity_check(engine != NULL);
605
606         std::string text;
607
608         if (!lua_isnone(L,1) && !lua_isnil(L,1))
609                 text = luaL_checkstring(L, 1);
610
611         engine->setTopleftText(text);
612         return 0;
613 }
614
615 /******************************************************************************/
616 int ModApiMainMenu::l_get_mapgen_names(lua_State *L)
617 {
618         std::vector<const char *> names;
619         bool include_hidden = lua_isboolean(L, 1) && readParam<bool>(L, 1);
620         Mapgen::getMapgenNames(&names, include_hidden);
621
622         lua_newtable(L);
623         for (size_t i = 0; i != names.size(); i++) {
624                 lua_pushstring(L, names[i]);
625                 lua_rawseti(L, -2, i + 1);
626         }
627
628         return 1;
629 }
630
631
632 /******************************************************************************/
633 int ModApiMainMenu::l_get_user_path(lua_State *L)
634 {
635         std::string path = fs::RemoveRelativePathComponents(porting::path_user);
636         lua_pushstring(L, path.c_str());
637         return 1;
638 }
639
640 /******************************************************************************/
641 int ModApiMainMenu::l_get_modpath(lua_State *L)
642 {
643         std::string modpath = fs::RemoveRelativePathComponents(
644                 porting::path_user + DIR_DELIM + "mods" + DIR_DELIM);
645         lua_pushstring(L, modpath.c_str());
646         return 1;
647 }
648
649 /******************************************************************************/
650 int ModApiMainMenu::l_get_modpaths(lua_State *L)
651 {
652         lua_newtable(L);
653
654         ModApiMainMenu::l_get_modpath(L);
655         lua_setfield(L, -2, "mods");
656
657         for (const std::string &component : getEnvModPaths()) {
658                 lua_pushstring(L, component.c_str());
659                 lua_setfield(L, -2, fs::AbsolutePath(component).c_str());
660         }
661         return 1;
662 }
663
664 /******************************************************************************/
665 int ModApiMainMenu::l_get_clientmodpath(lua_State *L)
666 {
667         std::string modpath = fs::RemoveRelativePathComponents(
668                 porting::path_user + DIR_DELIM + "clientmods" + DIR_DELIM);
669         lua_pushstring(L, modpath.c_str());
670         return 1;
671 }
672
673 /******************************************************************************/
674 int ModApiMainMenu::l_get_gamepath(lua_State *L)
675 {
676         std::string gamepath = fs::RemoveRelativePathComponents(
677                 porting::path_user + DIR_DELIM + "games" + DIR_DELIM);
678         lua_pushstring(L, gamepath.c_str());
679         return 1;
680 }
681
682 /******************************************************************************/
683 int ModApiMainMenu::l_get_texturepath(lua_State *L)
684 {
685         std::string gamepath = fs::RemoveRelativePathComponents(
686                 porting::path_user + DIR_DELIM + "textures");
687         lua_pushstring(L, gamepath.c_str());
688         return 1;
689 }
690
691 /******************************************************************************/
692 int ModApiMainMenu::l_get_texturepath_share(lua_State *L)
693 {
694         std::string gamepath = fs::RemoveRelativePathComponents(
695                 porting::path_share + DIR_DELIM + "textures");
696         lua_pushstring(L, gamepath.c_str());
697         return 1;
698 }
699
700 /******************************************************************************/
701 int ModApiMainMenu::l_get_cache_path(lua_State *L)
702 {
703         lua_pushstring(L, fs::RemoveRelativePathComponents(porting::path_cache).c_str());
704         return 1;
705 }
706
707 /******************************************************************************/
708 int ModApiMainMenu::l_get_temp_path(lua_State *L)
709 {
710         if (lua_isnoneornil(L, 1) || !lua_toboolean(L, 1))
711                 lua_pushstring(L, fs::TempPath().c_str());
712         else
713                 lua_pushstring(L, fs::CreateTempFile().c_str());
714         return 1;
715 }
716
717 /******************************************************************************/
718 int ModApiMainMenu::l_create_dir(lua_State *L) {
719         const char *path = luaL_checkstring(L, 1);
720
721         if (ModApiMainMenu::mayModifyPath(path)) {
722                 lua_pushboolean(L, fs::CreateAllDirs(path));
723                 return 1;
724         }
725
726         lua_pushboolean(L, false);
727         return 1;
728 }
729
730 /******************************************************************************/
731 int ModApiMainMenu::l_delete_dir(lua_State *L)
732 {
733         const char *path = luaL_checkstring(L, 1);
734
735         std::string absolute_path = fs::RemoveRelativePathComponents(path);
736
737         if (ModApiMainMenu::mayModifyPath(absolute_path)) {
738                 lua_pushboolean(L, fs::RecursiveDelete(absolute_path));
739                 return 1;
740         }
741
742         lua_pushboolean(L, false);
743         return 1;
744 }
745
746 /******************************************************************************/
747 int ModApiMainMenu::l_copy_dir(lua_State *L)
748 {
749         const char *source      = luaL_checkstring(L, 1);
750         const char *destination = luaL_checkstring(L, 2);
751
752         bool keep_source = true;
753         if (!lua_isnoneornil(L, 3))
754                 keep_source = readParam<bool>(L, 3);
755
756         std::string abs_destination = fs::RemoveRelativePathComponents(destination);
757         std::string abs_source = fs::RemoveRelativePathComponents(source);
758
759         if (!ModApiMainMenu::mayModifyPath(abs_destination) ||
760                 (!keep_source && !ModApiMainMenu::mayModifyPath(abs_source))) {
761                 lua_pushboolean(L, false);
762                 return 1;
763         }
764
765         bool retval;
766         if (keep_source)
767                 retval = fs::CopyDir(abs_source, abs_destination);
768         else
769                 retval = fs::MoveDir(abs_source, abs_destination);
770         lua_pushboolean(L, retval);
771         return 1;
772 }
773
774 /******************************************************************************/
775 int ModApiMainMenu::l_is_dir(lua_State *L)
776 {
777         const char *path = luaL_checkstring(L, 1);
778
779         lua_pushboolean(L, fs::IsDir(path));
780         return 1;
781 }
782
783 /******************************************************************************/
784 int ModApiMainMenu::l_extract_zip(lua_State *L)
785 {
786         const char *zipfile     = luaL_checkstring(L, 1);
787         const char *destination = luaL_checkstring(L, 2);
788
789         std::string absolute_destination = fs::RemoveRelativePathComponents(destination);
790
791         if (ModApiMainMenu::mayModifyPath(absolute_destination)) {
792                 auto fs = RenderingEngine::get_raw_device()->getFileSystem();
793                 bool ok = fs::extractZipFile(fs, zipfile, destination);
794                 lua_pushboolean(L, ok);
795                 return 1;
796         }
797
798         lua_pushboolean(L,false);
799         return 1;
800 }
801
802 /******************************************************************************/
803 int ModApiMainMenu::l_get_mainmenu_path(lua_State *L)
804 {
805         GUIEngine* engine = getGuiEngine(L);
806         sanity_check(engine != NULL);
807
808         lua_pushstring(L,engine->getScriptDir().c_str());
809         return 1;
810 }
811
812 /******************************************************************************/
813 bool ModApiMainMenu::mayModifyPath(std::string path)
814 {
815         path = fs::RemoveRelativePathComponents(path);
816
817         if (fs::PathStartsWith(path, fs::TempPath()))
818                 return true;
819
820         std::string path_user = fs::RemoveRelativePathComponents(porting::path_user);
821
822         if (fs::PathStartsWith(path, path_user + DIR_DELIM "client"))
823                 return true;
824         if (fs::PathStartsWith(path, path_user + DIR_DELIM "games"))
825                 return true;
826         if (fs::PathStartsWith(path, path_user + DIR_DELIM "mods"))
827                 return true;
828         if (fs::PathStartsWith(path, path_user + DIR_DELIM "textures"))
829                 return true;
830         if (fs::PathStartsWith(path, path_user + DIR_DELIM "worlds"))
831                 return true;
832
833         if (fs::PathStartsWith(path, fs::RemoveRelativePathComponents(porting::path_cache)))
834                 return true;
835
836         return false;
837 }
838
839
840 /******************************************************************************/
841 int ModApiMainMenu::l_may_modify_path(lua_State *L)
842 {
843         const char *target = luaL_checkstring(L, 1);
844         std::string absolute_destination = fs::RemoveRelativePathComponents(target);
845         lua_pushboolean(L, ModApiMainMenu::mayModifyPath(absolute_destination));
846         return 1;
847 }
848
849 /******************************************************************************/
850 int ModApiMainMenu::l_show_path_select_dialog(lua_State *L)
851 {
852         GUIEngine* engine = getGuiEngine(L);
853         sanity_check(engine != NULL);
854
855         const char *formname= luaL_checkstring(L, 1);
856         const char *title       = luaL_checkstring(L, 2);
857         bool is_file_select = readParam<bool>(L, 3);
858
859         GUIFileSelectMenu* fileOpenMenu =
860                 new GUIFileSelectMenu(engine->m_rendering_engine->get_gui_env(),
861                                 engine->m_parent,
862                                 -1,
863                                 engine->m_menumanager,
864                                 title,
865                                 formname,
866                                 is_file_select);
867         fileOpenMenu->setTextDest(engine->m_buttonhandler);
868         fileOpenMenu->drop();
869         return 0;
870 }
871
872 /******************************************************************************/
873 int ModApiMainMenu::l_download_file(lua_State *L)
874 {
875         const char *url    = luaL_checkstring(L, 1);
876         const char *target = luaL_checkstring(L, 2);
877
878         //check path
879         std::string absolute_destination = fs::RemoveRelativePathComponents(target);
880
881         if (ModApiMainMenu::mayModifyPath(absolute_destination)) {
882                 if (GUIEngine::downloadFile(url,absolute_destination)) {
883                         lua_pushboolean(L,true);
884                         return 1;
885                 }
886         } else {
887                 errorstream << "DOWNLOAD denied: " << absolute_destination
888                                 << " isn't an allowed path" << std::endl;
889         }
890         lua_pushboolean(L,false);
891         return 1;
892 }
893
894 /******************************************************************************/
895 int ModApiMainMenu::l_get_video_drivers(lua_State *L)
896 {
897         std::vector<irr::video::E_DRIVER_TYPE> drivers = RenderingEngine::getSupportedVideoDrivers();
898
899         lua_newtable(L);
900         for (u32 i = 0; i != drivers.size(); i++) {
901                 auto &info = RenderingEngine::getVideoDriverInfo(drivers[i]);
902
903                 lua_newtable(L);
904                 lua_pushstring(L, info.name.c_str());
905                 lua_setfield(L, -2, "name");
906                 lua_pushstring(L, info.friendly_name.c_str());
907                 lua_setfield(L, -2, "friendly_name");
908
909                 lua_rawseti(L, -2, i + 1);
910         }
911
912         return 1;
913 }
914
915 /******************************************************************************/
916 int ModApiMainMenu::l_gettext(lua_State *L)
917 {
918         const char *srctext = luaL_checkstring(L, 1);
919         const char *text = *srctext ? gettext(srctext) : "";
920         lua_pushstring(L, text);
921
922         return 1;
923 }
924
925 /******************************************************************************/
926 int ModApiMainMenu::l_get_window_info(lua_State *L)
927 {
928         lua_newtable(L);
929         int top = lua_gettop(L);
930
931         const v2u32 &window_size = RenderingEngine::getWindowSize();
932         f32 density = RenderingEngine::getDisplayDensity();
933         f32 gui_scaling = g_settings->getFloat("gui_scaling") * density;
934         f32 hud_scaling = g_settings->getFloat("hud_scaling") * density;
935
936         lua_pushstring(L, "size");
937         push_v2u32(L, window_size);
938         lua_settable(L, top);
939
940         lua_pushstring(L, "max_formspec_size");
941         push_v2f(L, ClientDynamicInfo::calculateMaxFSSize(window_size));
942         lua_settable(L, top);
943
944         lua_pushstring(L, "real_gui_scaling");
945         lua_pushnumber(L, gui_scaling);
946         lua_settable(L, top);
947
948         lua_pushstring(L, "real_hud_scaling");
949         lua_pushnumber(L, hud_scaling);
950         lua_settable(L, top);
951
952         return 1;
953 }
954
955 /******************************************************************************/
956
957 int ModApiMainMenu::l_get_active_renderer(lua_State *L)
958 {
959         lua_pushstring(L, wide_to_utf8(RenderingEngine::get_video_driver()->getName()).c_str());
960         return 1;
961 }
962
963 /******************************************************************************/
964 int ModApiMainMenu::l_get_min_supp_proto(lua_State *L)
965 {
966         lua_pushinteger(L, CLIENT_PROTOCOL_VERSION_MIN);
967         return 1;
968 }
969
970 int ModApiMainMenu::l_get_max_supp_proto(lua_State *L)
971 {
972         lua_pushinteger(L, CLIENT_PROTOCOL_VERSION_MAX);
973         return 1;
974 }
975
976 /******************************************************************************/
977 int ModApiMainMenu::l_open_url(lua_State *L)
978 {
979         std::string url = luaL_checkstring(L, 1);
980         lua_pushboolean(L, porting::open_url(url));
981         return 1;
982 }
983
984 /******************************************************************************/
985 int ModApiMainMenu::l_open_dir(lua_State *L)
986 {
987         std::string path = luaL_checkstring(L, 1);
988         lua_pushboolean(L, porting::open_directory(path));
989         return 1;
990 }
991
992 /******************************************************************************/
993 int ModApiMainMenu::l_share_file(lua_State *L)
994 {
995 #ifdef __ANDROID__
996         std::string path = luaL_checkstring(L, 1);
997         porting::shareFileAndroid(path);
998         lua_pushboolean(L, true);
999 #else
1000         lua_pushboolean(L, false);
1001 #endif
1002         return 1;
1003 }
1004
1005 /******************************************************************************/
1006 int ModApiMainMenu::l_do_async_callback(lua_State *L)
1007 {
1008         MainMenuScripting *script = getScriptApi<MainMenuScripting>(L);
1009
1010         size_t func_length, param_length;
1011         const char* serialized_func_raw = luaL_checklstring(L, 1, &func_length);
1012         const char* serialized_param_raw = luaL_checklstring(L, 2, &param_length);
1013
1014         sanity_check(serialized_func_raw != NULL);
1015         sanity_check(serialized_param_raw != NULL);
1016
1017         u32 jobId = script->queueAsync(
1018                 std::string(serialized_func_raw, func_length),
1019                 std::string(serialized_param_raw, param_length));
1020
1021         lua_pushinteger(L, jobId);
1022
1023         return 1;
1024 }
1025
1026 /******************************************************************************/
1027 // this is intentionally a global and not part of MainMenuScripting or such
1028 namespace {
1029         std::unordered_map<std::string, std::string> once_values;
1030         std::mutex once_mutex;
1031 }
1032
1033 int ModApiMainMenu::l_set_once(lua_State *L)
1034 {
1035         std::string key = readParam<std::string>(L, 1);
1036         if (lua_isnil(L, 2))
1037                 return 0;
1038         std::string value = readParam<std::string>(L, 2);
1039
1040         {
1041                 MutexAutoLock lock(once_mutex);
1042                 once_values[key] = value;
1043         }
1044
1045         return 0;
1046 }
1047
1048 int ModApiMainMenu::l_get_once(lua_State *L)
1049 {
1050         std::string key = readParam<std::string>(L, 1);
1051
1052         {
1053                 MutexAutoLock lock(once_mutex);
1054                 auto it = once_values.find(key);
1055                 if (it == once_values.end())
1056                         lua_pushnil(L);
1057                 else
1058                         lua_pushstring(L, it->second.c_str());
1059         }
1060
1061         return 1;
1062 }
1063
1064 /******************************************************************************/
1065 void ModApiMainMenu::Initialize(lua_State *L, int top)
1066 {
1067         API_FCT(update_formspec);
1068         API_FCT(set_formspec_prepend);
1069         API_FCT(set_clouds);
1070         API_FCT(get_textlist_index);
1071         API_FCT(get_table_index);
1072         API_FCT(get_worlds);
1073         API_FCT(get_games);
1074         API_FCT(get_content_info);
1075         API_FCT(check_mod_configuration);
1076         API_FCT(start);
1077         API_FCT(close);
1078         API_FCT(show_keys_menu);
1079         API_FCT(create_world);
1080         API_FCT(delete_world);
1081         API_FCT(set_background);
1082         API_FCT(set_topleft_text);
1083         API_FCT(get_mapgen_names);
1084         API_FCT(get_user_path);
1085         API_FCT(get_modpath);
1086         API_FCT(get_modpaths);
1087         API_FCT(get_clientmodpath);
1088         API_FCT(get_gamepath);
1089         API_FCT(get_texturepath);
1090         API_FCT(get_texturepath_share);
1091         API_FCT(get_cache_path);
1092         API_FCT(get_temp_path);
1093         API_FCT(create_dir);
1094         API_FCT(delete_dir);
1095         API_FCT(copy_dir);
1096         API_FCT(is_dir);
1097         API_FCT(extract_zip);
1098         API_FCT(may_modify_path);
1099         API_FCT(get_mainmenu_path);
1100         API_FCT(show_path_select_dialog);
1101         API_FCT(download_file);
1102         API_FCT(gettext);
1103         API_FCT(get_video_drivers);
1104         API_FCT(get_window_info);
1105         API_FCT(get_active_renderer);
1106         API_FCT(get_min_supp_proto);
1107         API_FCT(get_max_supp_proto);
1108         API_FCT(open_url);
1109         API_FCT(open_dir);
1110         API_FCT(share_file);
1111         API_FCT(do_async_callback);
1112         API_FCT(set_once);
1113         API_FCT(get_once);
1114 }
1115
1116 /******************************************************************************/
1117 void ModApiMainMenu::InitializeAsync(lua_State *L, int top)
1118 {
1119         API_FCT(get_worlds);
1120         API_FCT(get_games);
1121         API_FCT(get_mapgen_names);
1122         API_FCT(get_user_path);
1123         API_FCT(get_modpath);
1124         API_FCT(get_modpaths);
1125         API_FCT(get_clientmodpath);
1126         API_FCT(get_gamepath);
1127         API_FCT(get_texturepath);
1128         API_FCT(get_texturepath_share);
1129         API_FCT(get_cache_path);
1130         API_FCT(get_temp_path);
1131         API_FCT(create_dir);
1132         API_FCT(delete_dir);
1133         API_FCT(copy_dir);
1134         API_FCT(is_dir);
1135         API_FCT(extract_zip);
1136         API_FCT(may_modify_path);
1137         API_FCT(download_file);
1138         API_FCT(get_min_supp_proto);
1139         API_FCT(get_max_supp_proto);
1140         API_FCT(gettext);
1141 }