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