modstore.lua 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  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. --modstore implementation
  19. modstore = {}
  20. --------------------------------------------------------------------------------
  21. -- @function [parent=#modstore] init
  22. function modstore.init()
  23. modstore.tabnames = {}
  24. table.insert(modstore.tabnames,"dialog_modstore_unsorted")
  25. table.insert(modstore.tabnames,"dialog_modstore_search")
  26. modstore.modsperpage = 5
  27. modstore.basetexturedir = engine.get_texturepath() .. DIR_DELIM .. "base" ..
  28. DIR_DELIM .. "pack" .. DIR_DELIM
  29. modstore.lastmodtitle = ""
  30. modstore.last_search = ""
  31. modstore.searchlist = filterlist.create(
  32. function()
  33. if modstore.modlist_unsorted ~= nil and
  34. modstore.modlist_unsorted.data ~= nil then
  35. return modstore.modlist_unsorted.data
  36. end
  37. return {}
  38. end,
  39. function(element,modid)
  40. if element.id == modid then
  41. return true
  42. end
  43. return false
  44. end, --compare fct
  45. nil, --uid match fct
  46. function(element,substring)
  47. if substring == nil or
  48. substring == "" then
  49. return false
  50. end
  51. substring = substring:upper()
  52. if element.title ~= nil and
  53. element.title:upper():find(substring) ~= nil then
  54. return true
  55. end
  56. if element.details ~= nil and
  57. element.details.author ~= nil and
  58. element.details.author:upper():find(substring) ~= nil then
  59. return true
  60. end
  61. if element.details ~= nil and
  62. element.details.description ~= nil and
  63. element.details.description:upper():find(substring) ~= nil then
  64. return true
  65. end
  66. return false
  67. end --filter fct
  68. )
  69. modstore.current_list = nil
  70. end
  71. --------------------------------------------------------------------------------
  72. -- @function [parent=#modstore] nametoindex
  73. function modstore.nametoindex(name)
  74. for i=1,#modstore.tabnames,1 do
  75. if modstore.tabnames[i] == name then
  76. return i
  77. end
  78. end
  79. return 1
  80. end
  81. --------------------------------------------------------------------------------
  82. -- @function [parent=#modstore] getsuccessfuldialog
  83. function modstore.getsuccessfuldialog()
  84. local retval = ""
  85. retval = retval .. "size[6,2,true]"
  86. if modstore.lastmodentry ~= nil then
  87. retval = retval .. "label[0,0.25;" .. fgettext("Successfully installed:") .. "]"
  88. retval = retval .. "label[3,0.25;" .. modstore.lastmodentry.moddetails.title .. "]"
  89. retval = retval .. "label[0,0.75;" .. fgettext("Shortname:") .. "]"
  90. retval = retval .. "label[3,0.75;" .. engine.formspec_escape(modstore.lastmodentry.moddetails.basename) .. "]"
  91. end
  92. retval = retval .. "button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;" .. fgettext("ok") .. "]"
  93. return retval
  94. end
  95. --------------------------------------------------------------------------------
  96. -- @function [parent=#modstore] gettab
  97. function modstore.gettab(tabname)
  98. local retval = ""
  99. local is_modstore_tab = false
  100. if tabname == "dialog_modstore_unsorted" then
  101. modstore.modsperpage = 5
  102. retval = modstore.getmodlist(modstore.modlist_unsorted)
  103. is_modstore_tab = true
  104. end
  105. if tabname == "dialog_modstore_search" then
  106. retval = modstore.getsearchpage()
  107. is_modstore_tab = true
  108. end
  109. if is_modstore_tab then
  110. return modstore.tabheader(tabname) .. retval
  111. end
  112. if tabname == "modstore_mod_installed" then
  113. return modstore.getsuccessfuldialog()
  114. end
  115. if tabname == "modstore_downloading" then
  116. return "size[6,2]label[0.25,0.75;" .. fgettext("Downloading") ..
  117. " " .. modstore.lastmodtitle .. " " ..
  118. fgettext("please wait...") .. "]"
  119. end
  120. return ""
  121. end
  122. --------------------------------------------------------------------------------
  123. -- @function [parent=#modstore] tabheader
  124. function modstore.tabheader(tabname)
  125. local retval = "size[12,10.25,true]"
  126. retval = retval .. "tabheader[-0.3,-0.99;modstore_tab;" ..
  127. "Unsorted,Search;" ..
  128. modstore.nametoindex(tabname) .. ";true;false]" ..
  129. "button[4,9.9;4,0.5;btn_modstore_close;" ..
  130. fgettext("Close modstore") .. "]"
  131. return retval
  132. end
  133. --------------------------------------------------------------------------------
  134. -- @function [parent=#modstore] handle_buttons
  135. function modstore.handle_buttons(current_tab,fields)
  136. if fields["modstore_tab"] then
  137. local index = tonumber(fields["modstore_tab"])
  138. if index > 0 and
  139. index <= #modstore.tabnames then
  140. if modstore.tabnames[index] == "dialog_modstore_search" then
  141. filterlist.set_filtercriteria(modstore.searchlist,modstore.last_search)
  142. filterlist.refresh(modstore.searchlist)
  143. modstore.modsperpage = 4
  144. modstore.currentlist = {
  145. page = 0,
  146. pagecount =
  147. math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage),
  148. data = filterlist.get_list(modstore.searchlist),
  149. }
  150. end
  151. return {
  152. current_tab = modstore.tabnames[index],
  153. is_dialog = true,
  154. show_buttons = false
  155. }
  156. end
  157. end
  158. if fields["btn_modstore_page_up"] then
  159. if modstore.current_list ~= nil and modstore.current_list.page > 0 then
  160. modstore.current_list.page = modstore.current_list.page - 1
  161. end
  162. end
  163. if fields["btn_modstore_page_down"] then
  164. if modstore.current_list ~= nil and
  165. modstore.current_list.page <modstore.current_list.pagecount-1 then
  166. modstore.current_list.page = modstore.current_list.page +1
  167. end
  168. end
  169. if fields["btn_hidden_close_download"] ~= nil then
  170. if fields["btn_hidden_close_download"].successfull then
  171. modstore.lastmodentry = fields["btn_hidden_close_download"]
  172. return {
  173. current_tab = "modstore_mod_installed",
  174. is_dialog = true,
  175. show_buttons = false
  176. }
  177. else
  178. modstore.lastmodtitle = ""
  179. return {
  180. current_tab = modstore.tabnames[1],
  181. is_dialog = true,
  182. show_buttons = false
  183. }
  184. end
  185. end
  186. if fields["btn_confirm_mod_successfull"] then
  187. modstore.lastmodentry = nil
  188. modstore.lastmodtitle = ""
  189. return {
  190. current_tab = modstore.tabnames[1],
  191. is_dialog = true,
  192. show_buttons = false
  193. }
  194. end
  195. if fields["btn_modstore_search"] or
  196. (fields["key_enter"] and fields["te_modstore_search"] ~= nil) then
  197. modstore.last_search = fields["te_modstore_search"]
  198. filterlist.set_filtercriteria(modstore.searchlist,fields["te_modstore_search"])
  199. filterlist.refresh(modstore.searchlist)
  200. modstore.currentlist = {
  201. page = 0,
  202. pagecount = math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage),
  203. data = filterlist.get_list(modstore.searchlist),
  204. }
  205. end
  206. if fields["btn_modstore_close"] then
  207. return {
  208. is_dialog = false,
  209. show_buttons = true,
  210. current_tab = engine.setting_get("main_menu_tab")
  211. }
  212. end
  213. for key,value in pairs(fields) do
  214. local foundat = key:find("btn_install_mod_")
  215. if ( foundat == 1) then
  216. local modid = tonumber(key:sub(17))
  217. for i=1,#modstore.modlist_unsorted.data,1 do
  218. if modstore.modlist_unsorted.data[i].id == modid then
  219. local moddetails = modstore.modlist_unsorted.data[i].details
  220. if modstore.lastmodtitle ~= "" then
  221. modstore.lastmodtitle = modstore.lastmodtitle .. ", "
  222. end
  223. modstore.lastmodtitle = modstore.lastmodtitle .. moddetails.title
  224. engine.handle_async(
  225. function(param)
  226. local fullurl = engine.setting_get("modstore_download_url") ..
  227. param.moddetails.download_url
  228. if param.version ~= nil then
  229. local found = false
  230. for i=1,#param.moddetails.versions, 1 do
  231. if param.moddetails.versions[i].date:sub(1,10) == param.version then
  232. fullurl = engine.setting_get("modstore_download_url") ..
  233. param.moddetails.versions[i].download_url
  234. found = true
  235. end
  236. end
  237. if not found then
  238. return {
  239. moddetails = param.moddetails,
  240. successfull = false
  241. }
  242. end
  243. end
  244. if engine.download_file(fullurl,param.filename) then
  245. return {
  246. texturename = param.texturename,
  247. moddetails = param.moddetails,
  248. filename = param.filename,
  249. successfull = true
  250. }
  251. else
  252. return {
  253. moddetails = param.moddetails,
  254. successfull = false
  255. }
  256. end
  257. end,
  258. {
  259. moddetails = moddetails,
  260. version = fields["dd_version" .. modid],
  261. filename = os.tempfolder() .. "_MODNAME_" .. moddetails.basename .. ".zip",
  262. texturename = modstore.modlist_unsorted.data[i].texturename
  263. },
  264. function(result)
  265. if result.successfull then
  266. modmgr.installmod(result.filename,result.moddetails.basename)
  267. os.remove(result.filename)
  268. else
  269. gamedata.errormessage = "Failed to download " .. result.moddetails.title
  270. end
  271. if gamedata.errormessage == nil then
  272. engine.button_handler({btn_hidden_close_download=result})
  273. else
  274. engine.button_handler({btn_hidden_close_download={successfull=false}})
  275. end
  276. end
  277. )
  278. return {
  279. current_tab = "modstore_downloading",
  280. is_dialog = true,
  281. show_buttons = false,
  282. ignore_menu_quit = true
  283. }
  284. end
  285. end
  286. break
  287. end
  288. end
  289. end
  290. --------------------------------------------------------------------------------
  291. -- @function [parent=#modstore] update_modlist
  292. function modstore.update_modlist()
  293. modstore.modlist_unsorted = {}
  294. modstore.modlist_unsorted.data = {}
  295. modstore.modlist_unsorted.pagecount = 1
  296. modstore.modlist_unsorted.page = 0
  297. engine.handle_async(
  298. function(param)
  299. return engine.get_modstore_list()
  300. end,
  301. nil,
  302. function(result)
  303. if result ~= nil then
  304. modstore.modlist_unsorted = {}
  305. modstore.modlist_unsorted.data = result
  306. if modstore.modlist_unsorted.data ~= nil then
  307. modstore.modlist_unsorted.pagecount =
  308. math.ceil((#modstore.modlist_unsorted.data / modstore.modsperpage))
  309. else
  310. modstore.modlist_unsorted.data = {}
  311. modstore.modlist_unsorted.pagecount = 1
  312. end
  313. modstore.modlist_unsorted.page = 0
  314. modstore.fetchdetails()
  315. engine.event_handler("Refresh")
  316. end
  317. end
  318. )
  319. end
  320. --------------------------------------------------------------------------------
  321. -- @function [parent=#modstore] fetchdetails
  322. function modstore.fetchdetails()
  323. for i=1,#modstore.modlist_unsorted.data,1 do
  324. engine.handle_async(
  325. function(param)
  326. param.details = engine.get_modstore_details(tostring(param.modid))
  327. return param
  328. end,
  329. {
  330. modid=modstore.modlist_unsorted.data[i].id,
  331. listindex=i
  332. },
  333. function(result)
  334. if result ~= nil and
  335. modstore.modlist_unsorted ~= nil
  336. and modstore.modlist_unsorted.data ~= nil and
  337. modstore.modlist_unsorted.data[result.listindex] ~= nil and
  338. modstore.modlist_unsorted.data[result.listindex].id ~= nil then
  339. modstore.modlist_unsorted.data[result.listindex].details = result.details
  340. engine.event_handler("Refresh")
  341. end
  342. end
  343. )
  344. end
  345. end
  346. --------------------------------------------------------------------------------
  347. -- @function [parent=#modstore] getscreenshot
  348. function modstore.getscreenshot(ypos,listentry)
  349. if listentry.details ~= nil and
  350. (listentry.details.screenshot_url == nil or
  351. listentry.details.screenshot_url == "") then
  352. if listentry.texturename == nil then
  353. listentry.texturename = modstore.basetexturedir .. "no_screenshot.png"
  354. end
  355. return "image[0,".. ypos .. ";3,2;" ..
  356. engine.formspec_escape(listentry.texturename) .. "]"
  357. end
  358. if listentry.details ~= nil and
  359. listentry.texturename == nil then
  360. --make sure we don't download multiple times
  361. listentry.texturename = "in progress"
  362. --prepare url and filename
  363. local fullurl = engine.setting_get("modstore_download_url") ..
  364. listentry.details.screenshot_url
  365. local filename = os.tempfolder() .. "_MID_" .. listentry.id
  366. --trigger download
  367. engine.handle_async(
  368. --first param is downloadfct
  369. function(param)
  370. param.successfull = engine.download_file(param.fullurl,param.filename)
  371. return param
  372. end,
  373. --second parameter is data passed to async job
  374. {
  375. fullurl = fullurl,
  376. filename = filename,
  377. modid = listentry.id
  378. },
  379. --integrate result to raw list
  380. function(result)
  381. if result.successfull then
  382. local found = false
  383. for i=1,#modstore.modlist_unsorted.data,1 do
  384. if modstore.modlist_unsorted.data[i].id == result.modid then
  385. found = true
  386. modstore.modlist_unsorted.data[i].texturename = result.filename
  387. break
  388. end
  389. end
  390. if found then
  391. engine.event_handler("Refresh")
  392. else
  393. engine.log("error","got screenshot but didn't find matching mod: " .. result.modid)
  394. end
  395. end
  396. end
  397. )
  398. end
  399. if listentry.texturename ~= nil and
  400. listentry.texturename ~= "in progress" then
  401. return "image[0,".. ypos .. ";3,2;" ..
  402. engine.formspec_escape(listentry.texturename) .. "]"
  403. end
  404. return ""
  405. end
  406. --------------------------------------------------------------------------------
  407. --@function [parent=#modstore] getshortmodinfo
  408. function modstore.getshortmodinfo(ypos,listentry,details)
  409. local retval = ""
  410. retval = retval .. "box[0," .. ypos .. ";11.4,1.75;#FFFFFF]"
  411. --screenshot
  412. retval = retval .. modstore.getscreenshot(ypos,listentry)
  413. --title + author
  414. retval = retval .."label[2.75," .. ypos .. ";" ..
  415. engine.formspec_escape(details.title) .. " (" .. details.author .. ")]"
  416. --description
  417. local descriptiony = ypos + 0.5
  418. retval = retval .. "textarea[3," .. descriptiony .. ";6.5,1.55;;" ..
  419. engine.formspec_escape(details.description) .. ";]"
  420. --rating
  421. local ratingy = ypos
  422. retval = retval .."label[7," .. ratingy .. ";" ..
  423. fgettext("Rating") .. ":]"
  424. retval = retval .. "label[8.7," .. ratingy .. ";" .. details.rating .."]"
  425. --versions (IMPORTANT has to be defined AFTER rating)
  426. if details.versions ~= nil and
  427. #details.versions > 1 then
  428. local versiony = ypos + 0.05
  429. retval = retval .. "dropdown[9.1," .. versiony .. ";2.48,0.25;dd_version" .. details.id .. ";"
  430. local versions = ""
  431. for i=1,#details.versions , 1 do
  432. if versions ~= "" then
  433. versions = versions .. ","
  434. end
  435. versions = versions .. details.versions[i].date:sub(1,10)
  436. end
  437. retval = retval .. versions .. ";1]"
  438. end
  439. if details.basename then
  440. --install button
  441. local buttony = ypos + 1.2
  442. retval = retval .."button[9.1," .. buttony .. ";2.5,0.5;btn_install_mod_" .. details.id .. ";"
  443. if modmgr.mod_exists(details.basename) then
  444. retval = retval .. fgettext("re-Install") .."]"
  445. else
  446. retval = retval .. fgettext("Install") .."]"
  447. end
  448. end
  449. return retval
  450. end
  451. --------------------------------------------------------------------------------
  452. --@function [parent=#modstore] getmodlist
  453. function modstore.getmodlist(list,yoffset)
  454. modstore.current_list = list
  455. if #list.data == 0 then
  456. return ""
  457. end
  458. if yoffset == nil then
  459. yoffset = 0
  460. end
  461. local scrollbar = ""
  462. scrollbar = scrollbar .. "label[0.1,9.5;"
  463. .. fgettext("Page $1 of $2", list.page+1, list.pagecount) .. "]"
  464. scrollbar = scrollbar .. "box[11.6," .. (yoffset + 0.35) .. ";0.28,"
  465. .. (8.6 - yoffset) .. ";#000000]"
  466. local scrollbarpos = (yoffset + 0.75) +
  467. ((7.7 -yoffset)/(list.pagecount-1)) * list.page
  468. scrollbar = scrollbar .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;#32CD32]"
  469. scrollbar = scrollbar .. "button[11.6," .. (yoffset + (0.3))
  470. .. ";0.5,0.5;btn_modstore_page_up;^]"
  471. scrollbar = scrollbar .. "button[11.6," .. 9.0
  472. .. ";0.5,0.5;btn_modstore_page_down;v]"
  473. local retval = ""
  474. local endmod = (list.page * modstore.modsperpage) + modstore.modsperpage
  475. if (endmod > #list.data) then
  476. endmod = #list.data
  477. end
  478. for i=(list.page * modstore.modsperpage) +1, endmod, 1 do
  479. --getmoddetails
  480. local details = list.data[i].details
  481. if details == nil then
  482. details = {}
  483. details.title = list.data[i].title
  484. details.author = ""
  485. details.rating = -1
  486. details.description = ""
  487. end
  488. if details ~= nil then
  489. local screenshot_ypos =
  490. yoffset +(i-1 - (list.page * modstore.modsperpage))*1.9 +0.2
  491. retval = retval .. modstore.getshortmodinfo(screenshot_ypos,
  492. list.data[i],
  493. details)
  494. end
  495. end
  496. return retval .. scrollbar
  497. end
  498. --------------------------------------------------------------------------------
  499. --@function [parent=#modstore] getsearchpage
  500. function modstore.getsearchpage()
  501. local retval = ""
  502. local search = ""
  503. if modstore.last_search ~= nil then
  504. search = modstore.last_search
  505. end
  506. retval = retval ..
  507. "button[9.5,0.2;2.5,0.5;btn_modstore_search;".. fgettext("Search") .. "]" ..
  508. "field[0.5,0.5;9,0.5;te_modstore_search;;" .. search .. "]"
  509. --show 4 mods only
  510. modstore.modsperpage = 4
  511. retval = retval ..
  512. modstore.getmodlist(
  513. modstore.currentlist,
  514. 1.75)
  515. return retval;
  516. end