]> git.lizzy.rs Git - dragonfireclient.git/blob - builtin/mainmenu/pkgmgr.lua
Add more neighbors on mesh update (#6765)
[dragonfireclient.git] / builtin / mainmenu / pkgmgr.lua
1 --Minetest
2 --Copyright (C) 2013 sapier
3 --
4 --This program is free software; you can redistribute it and/or modify
5 --it under the terms of the GNU Lesser General Public License as published by
6 --the Free Software Foundation; either version 2.1 of the License, or
7 --(at your option) any later version.
8 --
9 --This program is distributed in the hope that it will be useful,
10 --but WITHOUT ANY WARRANTY; without even the implied warranty of
11 --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 --GNU Lesser General Public License for more details.
13 --
14 --You should have received a copy of the GNU Lesser General Public License along
15 --with this program; if not, write to the Free Software Foundation, Inc.,
16 --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18 --------------------------------------------------------------------------------
19 local function get_last_folder(text,count)
20         local parts = text:split(DIR_DELIM)
21
22         if count == nil then
23                 return parts[#parts]
24         end
25
26         local retval = ""
27         for i=1,count,1 do
28                 retval = retval .. parts[#parts - (count-i)] .. DIR_DELIM
29         end
30
31         return retval
32 end
33
34 local function cleanup_path(temppath)
35
36         local parts = temppath:split("-")
37         temppath = ""
38         for i=1,#parts,1 do
39                 if temppath ~= "" then
40                         temppath = temppath .. "_"
41                 end
42                 temppath = temppath .. parts[i]
43         end
44
45         parts = temppath:split(".")
46         temppath = ""
47         for i=1,#parts,1 do
48                 if temppath ~= "" then
49                         temppath = temppath .. "_"
50                 end
51                 temppath = temppath .. parts[i]
52         end
53
54         parts = temppath:split("'")
55         temppath = ""
56         for i=1,#parts,1 do
57                 if temppath ~= "" then
58                         temppath = temppath .. ""
59                 end
60                 temppath = temppath .. parts[i]
61         end
62
63         parts = temppath:split(" ")
64         temppath = ""
65         for i=1,#parts,1 do
66                 if temppath ~= "" then
67                         temppath = temppath
68                 end
69                 temppath = temppath .. parts[i]
70         end
71
72         return temppath
73 end
74
75 local function load_texture_packs(txtpath, retval)
76         local list = core.get_dir_list(txtpath, true)
77         local current_texture_path = core.settings:get("texture_path")
78
79         for _, item in ipairs(list) do
80                 if item ~= "base" then
81                         local name = item
82
83                         local path = txtpath .. DIR_DELIM .. item .. DIR_DELIM
84                         if path == current_texture_path then
85                                 name = fgettext("$1 (Enabled)", name)
86                         end
87
88                         local conf = Settings(path .. "texture_pack.conf")
89
90                         retval[#retval + 1] = {
91                                 name = item,
92                                 author = conf:get("author"),
93                                 release = tonumber(conf:get("release")) or 0,
94                                 list_name = name,
95                                 type = "txp",
96                                 path = path,
97                                 enabled = path == current_texture_path,
98                         }
99                 end
100         end
101 end
102
103 function get_mods(path,retval,modpack)
104         local mods = core.get_dir_list(path, true)
105
106         for _, name in ipairs(mods) do
107                 if name:sub(1, 1) ~= "." then
108                         local prefix = path .. DIR_DELIM .. name
109                         local toadd = {
110                                 dir_name = name,
111                                 parent_dir = path,
112                         }
113                         retval[#retval + 1] = toadd
114
115                         -- Get config file
116                         local mod_conf
117                         local modpack_conf = io.open(prefix .. DIR_DELIM .. "modpack.conf")
118                         if modpack_conf then
119                                 toadd.is_modpack = true
120                                 modpack_conf:close()
121
122                                 mod_conf = Settings(prefix .. DIR_DELIM .. "modpack.conf"):to_table()
123                                 if mod_conf.name then
124                                         name = mod_conf.name
125                                         toadd.is_name_explicit = true
126                                 end
127                         else
128                                 mod_conf = Settings(prefix .. DIR_DELIM .. "mod.conf"):to_table()
129                                 if mod_conf.name then
130                                         name = mod_conf.name
131                                         toadd.is_name_explicit = true
132                                 end
133                         end
134
135                         -- Read from config
136                         toadd.name = name
137                         toadd.author = mod_conf.author
138                         toadd.release = tonumber(mod_conf.release) or 0
139                         toadd.path = prefix
140                         toadd.type = "mod"
141
142                         -- Check modpack.txt
143                         -- Note: modpack.conf is already checked above
144                         local modpackfile = io.open(prefix .. DIR_DELIM .. "modpack.txt")
145                         if modpackfile then
146                                 modpackfile:close()
147                                 toadd.is_modpack = true
148                         end
149
150                         -- Deal with modpack contents
151                         if modpack and modpack ~= "" then
152                                 toadd.modpack = modpack
153                         elseif toadd.is_modpack then
154                                 toadd.type = "modpack"
155                                 toadd.is_modpack = true
156                                 get_mods(prefix, retval, name)
157                         end
158                 end
159         end
160 end
161
162 --modmanager implementation
163 pkgmgr = {}
164
165 function pkgmgr.get_texture_packs()
166         local txtpath = core.get_texturepath()
167         local txtpath_system = core.get_texturepath_share()
168         local retval = {}
169
170         load_texture_packs(txtpath, retval)
171         -- on portable versions these two paths coincide. It avoids loading the path twice
172         if txtpath ~= txtpath_system then
173                 load_texture_packs(txtpath_system, retval)
174         end
175
176         table.sort(retval, function(a, b)
177                 return a.name > b.name
178         end)
179
180         return retval
181 end
182
183 --------------------------------------------------------------------------------
184 function pkgmgr.get_folder_type(path)
185         local testfile = io.open(path .. DIR_DELIM .. "init.lua","r")
186         if testfile ~= nil then
187                 testfile:close()
188                 return { type = "mod", path = path }
189         end
190
191         testfile = io.open(path .. DIR_DELIM .. "modpack.conf","r")
192         if testfile ~= nil then
193                 testfile:close()
194                 return { type = "modpack", path = path }
195         end
196
197         testfile = io.open(path .. DIR_DELIM .. "modpack.txt","r")
198         if testfile ~= nil then
199                 testfile:close()
200                 return { type = "modpack", path = path }
201         end
202
203         testfile = io.open(path .. DIR_DELIM .. "game.conf","r")
204         if testfile ~= nil then
205                 testfile:close()
206                 return { type = "game", path = path }
207         end
208
209         testfile = io.open(path .. DIR_DELIM .. "texture_pack.conf","r")
210         if testfile ~= nil then
211                 testfile:close()
212                 return { type = "txp", path = path }
213         end
214
215         return nil
216 end
217
218 -------------------------------------------------------------------------------
219 function pkgmgr.get_base_folder(temppath)
220         if temppath == nil then
221                 return { type = "invalid", path = "" }
222         end
223
224         local ret = pkgmgr.get_folder_type(temppath)
225         if ret then
226                 return ret
227         end
228
229         local subdirs = core.get_dir_list(temppath, true)
230         if #subdirs == 1 then
231                 ret = pkgmgr.get_folder_type(temppath .. DIR_DELIM .. subdirs[1])
232                 if ret then
233                         return ret
234                 else
235                         return { type = "invalid", path = temppath .. DIR_DELIM .. subdirs[1] }
236                 end
237         end
238
239         return nil
240 end
241
242 --------------------------------------------------------------------------------
243 function pkgmgr.isValidModname(modpath)
244         if modpath:find("-") ~= nil then
245                 return false
246         end
247
248         return true
249 end
250
251 --------------------------------------------------------------------------------
252 function pkgmgr.parse_register_line(line)
253         local pos1 = line:find("\"")
254         local pos2 = nil
255         if pos1 ~= nil then
256                 pos2 = line:find("\"",pos1+1)
257         end
258
259         if pos1 ~= nil and pos2 ~= nil then
260                 local item = line:sub(pos1+1,pos2-1)
261
262                 if item ~= nil and
263                         item ~= "" then
264                         local pos3 = item:find(":")
265
266                         if pos3 ~= nil then
267                                 local retval = item:sub(1,pos3-1)
268                                 if retval ~= nil and
269                                         retval ~= "" then
270                                         return retval
271                                 end
272                         end
273                 end
274         end
275         return nil
276 end
277
278 --------------------------------------------------------------------------------
279 function pkgmgr.parse_dofile_line(modpath,line)
280         local pos1 = line:find("\"")
281         local pos2 = nil
282         if pos1 ~= nil then
283                 pos2 = line:find("\"",pos1+1)
284         end
285
286         if pos1 ~= nil and pos2 ~= nil then
287                 local filename = line:sub(pos1+1,pos2-1)
288
289                 if filename ~= nil and
290                         filename ~= "" and
291                         filename:find(".lua") then
292                         return pkgmgr.identify_modname(modpath,filename)
293                 end
294         end
295         return nil
296 end
297
298 --------------------------------------------------------------------------------
299 function pkgmgr.identify_modname(modpath,filename)
300         local testfile = io.open(modpath .. DIR_DELIM .. filename,"r")
301         if testfile ~= nil then
302                 local line = testfile:read()
303
304                 while line~= nil do
305                         local modname = nil
306
307                         if line:find("minetest.register_tool") then
308                                 modname = pkgmgr.parse_register_line(line)
309                         end
310
311                         if line:find("minetest.register_craftitem") then
312                                 modname = pkgmgr.parse_register_line(line)
313                         end
314
315
316                         if line:find("minetest.register_node") then
317                                 modname = pkgmgr.parse_register_line(line)
318                         end
319
320                         if line:find("dofile") then
321                                 modname = pkgmgr.parse_dofile_line(modpath,line)
322                         end
323
324                         if modname ~= nil then
325                                 testfile:close()
326                                 return modname
327                         end
328
329                         line = testfile:read()
330                 end
331                 testfile:close()
332         end
333
334         return nil
335 end
336 --------------------------------------------------------------------------------
337 function pkgmgr.render_packagelist(render_list)
338         if not render_list then
339                 if not pkgmgr.global_mods then
340                         pkgmgr.refresh_globals()
341                 end
342                 render_list = pkgmgr.global_mods
343         end
344
345         local list = render_list:get_list()
346         local retval = {}
347         for i, v in ipairs(list) do
348                 local color = ""
349                 if v.is_modpack then
350                         local rawlist = render_list:get_raw_list()
351                         color = mt_color_dark_green
352
353                         for j = 1, #rawlist, 1 do
354                                 if rawlist[j].modpack == list[i].name and
355                                                 not rawlist[j].enabled then
356                                         -- Modpack not entirely enabled so showing as grey
357                                         color = mt_color_grey
358                                         break
359                                 end
360                         end
361                 elseif v.is_game_content or v.type == "game" then
362                         color = mt_color_blue
363                 elseif v.enabled or v.type == "txp" then
364                         color = mt_color_green
365                 end
366
367                 retval[#retval + 1] = color
368                 if v.modpack ~= nil or v.loc == "game" then
369                         retval[#retval + 1] = "1"
370                 else
371                         retval[#retval + 1] = "0"
372                 end
373                 retval[#retval + 1] = core.formspec_escape(v.list_name or v.name)
374         end
375
376         return table.concat(retval, ",")
377 end
378
379 --------------------------------------------------------------------------------
380 function pkgmgr.get_dependencies(path)
381         if path == nil then
382                 return {}, {}
383         end
384
385         local info = core.get_content_info(path)
386         return info.depends or {}, info.optional_depends or {}
387 end
388
389 ----------- tests whether all of the mods in the modpack are enabled -----------
390 function pkgmgr.is_modpack_entirely_enabled(data, name)
391         local rawlist = data.list:get_raw_list()
392         for j = 1, #rawlist do
393                 if rawlist[j].modpack == name and not rawlist[j].enabled then
394                         return false
395                 end
396         end
397         return true
398 end
399
400 ---------- toggles or en/disables a mod or modpack and its dependencies --------
401 local function toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod)
402         if not mod.is_modpack then
403                 -- Toggle or en/disable the mod
404                 if toset == nil then
405                         toset = not mod.enabled
406                 end
407                 if mod.enabled ~= toset then
408                         mod.enabled = toset
409                         toggled_mods[#toggled_mods+1] = mod.name
410                 end
411                 if toset then
412                         -- Mark this mod for recursive dependency traversal
413                         enabled_mods[mod.name] = true
414                 end
415         else
416                 -- Toggle or en/disable every mod in the modpack,
417                 -- interleaved unsupported
418                 for i = 1, #list do
419                         if list[i].modpack == mod.name then
420                                 toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, list[i])
421                         end
422                 end
423         end
424 end
425
426 function pkgmgr.enable_mod(this, toset)
427         local list = this.data.list:get_list()
428         local mod = list[this.data.selected_mod]
429
430         -- Game mods can't be enabled or disabled
431         if mod.is_game_content then
432                 return
433         end
434
435         local toggled_mods = {}
436         local enabled_mods = {}
437         toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod)
438
439         if not toset then
440                 -- Mod(s) were disabled, so no dependencies need to be enabled
441                 table.sort(toggled_mods)
442                 core.log("info", "Following mods were disabled: " ..
443                         table.concat(toggled_mods, ", "))
444                 return
445         end
446
447         -- Enable mods' depends after activation
448
449         -- Make a list of mod ids indexed by their names
450         local mod_ids = {}
451         for id, mod2 in pairs(list) do
452                 if mod2.type == "mod" and not mod2.is_modpack then
453                         mod_ids[mod2.name] = id
454                 end
455         end
456
457         -- to_enable is used as a DFS stack with sp as stack pointer
458         local to_enable = {}
459         local sp = 0
460         for name in pairs(enabled_mods) do
461                 local depends = pkgmgr.get_dependencies(list[mod_ids[name]].path)
462                 for i = 1, #depends do
463                         local dependency_name = depends[i]
464                         if not enabled_mods[dependency_name] then
465                                 sp = sp+1
466                                 to_enable[sp] = dependency_name
467                         end
468                 end
469         end
470         -- If sp is 0, every dependency is already activated
471         while sp > 0 do
472                 local name = to_enable[sp]
473                 sp = sp-1
474
475                 if not enabled_mods[name] then
476                         enabled_mods[name] = true
477                         local mod_to_enable = list[mod_ids[name]]
478                         if not mod_to_enable then
479                                 core.log("warning", "Mod dependency \"" .. name ..
480                                         "\" not found!")
481                         else
482                                 if mod_to_enable.enabled == false then
483                                         mod_to_enable.enabled = true
484                                         toggled_mods[#toggled_mods+1] = mod_to_enable.name
485                                 end
486                                 -- Push the dependencies of the dependency onto the stack
487                                 local depends = pkgmgr.get_dependencies(mod_to_enable.path)
488                                 for i = 1, #depends do
489                                         if not enabled_mods[name] then
490                                                 sp = sp+1
491                                                 to_enable[sp] = depends[i]
492                                         end
493                                 end
494                         end
495                 end
496         end
497
498         -- Log the list of enabled mods
499         table.sort(toggled_mods)
500         core.log("info", "Following mods were enabled: " ..
501                 table.concat(toggled_mods, ", "))
502 end
503
504 --------------------------------------------------------------------------------
505 function pkgmgr.get_worldconfig(worldpath)
506         local filename = worldpath ..
507                                 DIR_DELIM .. "world.mt"
508
509         local worldfile = Settings(filename)
510
511         local worldconfig = {}
512         worldconfig.global_mods = {}
513         worldconfig.game_mods = {}
514
515         for key,value in pairs(worldfile:to_table()) do
516                 if key == "gameid" then
517                         worldconfig.id = value
518                 elseif key:sub(0, 9) == "load_mod_" then
519                         -- Compatibility: Check against "nil" which was erroneously used
520                         -- as value for fresh configured worlds
521                         worldconfig.global_mods[key] = value ~= "false" and value ~= "nil"
522                                 and value
523                 else
524                         worldconfig[key] = value
525                 end
526         end
527
528         --read gamemods
529         local gamespec = pkgmgr.find_by_gameid(worldconfig.id)
530         pkgmgr.get_game_mods(gamespec, worldconfig.game_mods)
531
532         return worldconfig
533 end
534
535 --------------------------------------------------------------------------------
536 function pkgmgr.install_dir(type, path, basename, targetpath)
537         local basefolder = pkgmgr.get_base_folder(path)
538
539         -- There's no good way to detect a texture pack, so let's just assume
540         -- it's correct for now.
541         if type == "txp" then
542                 if basefolder and basefolder.type ~= "invalid" and basefolder.type ~= "txp" then
543                         return nil, fgettext("Unable to install a $1 as a texture pack", basefolder.type)
544                 end
545
546                 local from = basefolder and basefolder.path or path
547                 if targetpath then
548                         core.delete_dir(targetpath)
549                 else
550                         targetpath = core.get_texturepath() .. DIR_DELIM .. basename
551                 end
552                 if not core.copy_dir(from, targetpath, false) then
553                         return nil,
554                                 fgettext("Failed to install $1 to $2", basename, targetpath)
555                 end
556                 return targetpath, nil
557
558         elseif not basefolder then
559                 return nil, fgettext("Unable to find a valid mod or modpack")
560         end
561
562         --
563         -- Get destination
564         --
565         if basefolder.type == "modpack" then
566                 if type ~= "mod" then
567                         return nil, fgettext("Unable to install a modpack as a $1", type)
568                 end
569
570                 -- Get destination name for modpack
571                 if targetpath then
572                         core.delete_dir(targetpath)
573                 else
574                         local clean_path = nil
575                         if basename ~= nil then
576                                 clean_path = basename
577                         end
578                         if not clean_path then
579                                 clean_path = get_last_folder(cleanup_path(basefolder.path))
580                         end
581                         if clean_path then
582                                 targetpath = core.get_modpath() .. DIR_DELIM .. clean_path
583                         else
584                                 return nil,
585                                         fgettext("Install Mod: Unable to find suitable folder name for modpack $1",
586                                         path)
587                         end
588                 end
589         elseif basefolder.type == "mod" then
590                 if type ~= "mod" then
591                         return nil, fgettext("Unable to install a mod as a $1", type)
592                 end
593
594                 if targetpath then
595                         core.delete_dir(targetpath)
596                 else
597                         local targetfolder = basename
598                         if targetfolder == nil then
599                                 targetfolder = pkgmgr.identify_modname(basefolder.path, "init.lua")
600                         end
601
602                         -- If heuristic failed try to use current foldername
603                         if targetfolder == nil then
604                                 targetfolder = get_last_folder(basefolder.path)
605                         end
606
607                         if targetfolder ~= nil and pkgmgr.isValidModname(targetfolder) then
608                                 targetpath = core.get_modpath() .. DIR_DELIM .. targetfolder
609                         else
610                                 return nil, fgettext("Install Mod: Unable to find real mod name for: $1", path)
611                         end
612                 end
613
614         elseif basefolder.type == "game" then
615                 if type ~= "game" then
616                         return nil, fgettext("Unable to install a game as a $1", type)
617                 end
618
619                 if targetpath then
620                         core.delete_dir(targetpath)
621                 else
622                         targetpath = core.get_gamepath() .. DIR_DELIM .. basename
623                 end
624         end
625
626         -- Copy it
627         if not core.copy_dir(basefolder.path, targetpath, false) then
628                 return nil,
629                         fgettext("Failed to install $1 to $2", basename, targetpath)
630         end
631
632         if basefolder.type == "game" then
633                 pkgmgr.update_gamelist()
634         else
635                 pkgmgr.refresh_globals()
636         end
637
638         return targetpath, nil
639 end
640
641 --------------------------------------------------------------------------------
642 function pkgmgr.preparemodlist(data)
643         local retval = {}
644
645         local global_mods = {}
646         local game_mods = {}
647
648         --read global mods
649         local modpaths = core.get_modpaths()
650         for _, modpath in ipairs(modpaths) do
651                 get_mods(modpath, global_mods)
652         end
653
654         for i=1,#global_mods,1 do
655                 global_mods[i].type = "mod"
656                 global_mods[i].loc = "global"
657                 retval[#retval + 1] = global_mods[i]
658         end
659
660         --read game mods
661         local gamespec = pkgmgr.find_by_gameid(data.gameid)
662         pkgmgr.get_game_mods(gamespec, game_mods)
663
664         if #game_mods > 0 then
665                 -- Add title
666                 retval[#retval + 1] = {
667                         type = "game",
668                         is_game_content = true,
669                         name = fgettext("$1 mods", gamespec.name),
670                         path = gamespec.path
671                 }
672         end
673
674         for i=1,#game_mods,1 do
675                 game_mods[i].type = "mod"
676                 game_mods[i].loc = "game"
677                 game_mods[i].is_game_content = true
678                 retval[#retval + 1] = game_mods[i]
679         end
680
681         if data.worldpath == nil then
682                 return retval
683         end
684
685         --read world mod configuration
686         local filename = data.worldpath ..
687                                 DIR_DELIM .. "world.mt"
688
689         local worldfile = Settings(filename)
690
691         for key,value in pairs(worldfile:to_table()) do
692                 if key:sub(1, 9) == "load_mod_" then
693                         key = key:sub(10)
694                         local element = nil
695                         for i=1,#retval,1 do
696                                 if retval[i].name == key and
697                                         not retval[i].is_modpack then
698                                         element = retval[i]
699                                         break
700                                 end
701                         end
702                         if element ~= nil then
703                                 element.enabled = value ~= "false" and value ~= "nil" and value
704                         else
705                                 core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
706                         end
707                 end
708         end
709
710         return retval
711 end
712
713 function pkgmgr.compare_package(a, b)
714         return a and b and a.name == b.name and a.path == b.path
715 end
716
717 --------------------------------------------------------------------------------
718 function pkgmgr.comparemod(elem1,elem2)
719         if elem1 == nil or elem2 == nil then
720                 return false
721         end
722         if elem1.name ~= elem2.name then
723                 return false
724         end
725         if elem1.is_modpack ~= elem2.is_modpack then
726                 return false
727         end
728         if elem1.type ~= elem2.type then
729                 return false
730         end
731         if elem1.modpack ~= elem2.modpack then
732                 return false
733         end
734
735         if elem1.path ~= elem2.path then
736                 return false
737         end
738
739         return true
740 end
741
742 --------------------------------------------------------------------------------
743 function pkgmgr.mod_exists(basename)
744
745         if pkgmgr.global_mods == nil then
746                 pkgmgr.refresh_globals()
747         end
748
749         if pkgmgr.global_mods:raw_index_by_uid(basename) > 0 then
750                 return true
751         end
752
753         return false
754 end
755
756 --------------------------------------------------------------------------------
757 function pkgmgr.get_global_mod(idx)
758
759         if pkgmgr.global_mods == nil then
760                 return nil
761         end
762
763         if idx == nil or idx < 1 or
764                 idx > pkgmgr.global_mods:size() then
765                 return nil
766         end
767
768         return pkgmgr.global_mods:get_list()[idx]
769 end
770
771 --------------------------------------------------------------------------------
772 function pkgmgr.refresh_globals()
773         local function is_equal(element,uid) --uid match
774                 if element.name == uid then
775                         return true
776                 end
777         end
778         pkgmgr.global_mods = filterlist.create(pkgmgr.preparemodlist,
779                         pkgmgr.comparemod, is_equal, nil, {})
780         pkgmgr.global_mods:add_sort_mechanism("alphabetic", sort_mod_list)
781         pkgmgr.global_mods:set_sortmode("alphabetic")
782 end
783
784 --------------------------------------------------------------------------------
785 function pkgmgr.find_by_gameid(gameid)
786         for i=1,#pkgmgr.games,1 do
787                 if pkgmgr.games[i].id == gameid then
788                         return pkgmgr.games[i], i
789                 end
790         end
791         return nil, nil
792 end
793
794 --------------------------------------------------------------------------------
795 function pkgmgr.get_game_mods(gamespec, retval)
796         if gamespec ~= nil and
797                 gamespec.gamemods_path ~= nil and
798                 gamespec.gamemods_path ~= "" then
799                 get_mods(gamespec.gamemods_path, retval)
800         end
801 end
802
803 --------------------------------------------------------------------------------
804 function pkgmgr.get_game_modlist(gamespec)
805         local retval = ""
806         local game_mods = {}
807         pkgmgr.get_game_mods(gamespec, game_mods)
808         for i=1,#game_mods,1 do
809                 if retval ~= "" then
810                         retval = retval..","
811                 end
812                 retval = retval .. game_mods[i].name
813         end
814         return retval
815 end
816
817 --------------------------------------------------------------------------------
818 function pkgmgr.get_game(index)
819         if index > 0 and index <= #pkgmgr.games then
820                 return pkgmgr.games[index]
821         end
822
823         return nil
824 end
825
826 --------------------------------------------------------------------------------
827 function pkgmgr.update_gamelist()
828         pkgmgr.games = core.get_games()
829 end
830
831 --------------------------------------------------------------------------------
832 function pkgmgr.gamelist()
833         local retval = ""
834         if #pkgmgr.games > 0 then
835                 retval = retval .. core.formspec_escape(pkgmgr.games[1].name)
836
837                 for i=2,#pkgmgr.games,1 do
838                         retval = retval .. "," .. core.formspec_escape(pkgmgr.games[i].name)
839                 end
840         end
841         return retval
842 end
843
844 --------------------------------------------------------------------------------
845 -- read initial data
846 --------------------------------------------------------------------------------
847 pkgmgr.update_gamelist()