mirror of
https://github.com/minetest/minetest.git
synced 2024-08-18 20:44:16 +02:00
Replace C++ mainmenu by formspec powered one
This commit is contained in:
parent
fe4ce03d52
commit
967121a34b
309
builtin/gamemgr.lua
Normal file
309
builtin/gamemgr.lua
Normal file
@ -0,0 +1,309 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2013 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
gamemgr = {}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.dialog_new_game()
|
||||
local retval =
|
||||
"label[2,2;Game Name]"..
|
||||
"field[4.5,2.4;6,0.5;te_game_name;;]" ..
|
||||
"button[5,4.2;2.6,0.5;new_game_confirm;Create]" ..
|
||||
"button[7.5,4.2;2.8,0.5;new_game_cancel;Cancel]"
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.handle_games_buttons(fields)
|
||||
if fields["gamelist"] ~= nil then
|
||||
local event = explode_textlist_event(fields["gamelist"])
|
||||
gamemgr.selected_game = event.index
|
||||
end
|
||||
|
||||
if fields["btn_game_mgr_edit_game"] ~= nil then
|
||||
return {
|
||||
is_dialog = true,
|
||||
show_buttons = false,
|
||||
current_tab = "dialog_edit_game"
|
||||
}
|
||||
end
|
||||
|
||||
if fields["btn_game_mgr_new_game"] ~= nil then
|
||||
return {
|
||||
is_dialog = true,
|
||||
show_buttons = false,
|
||||
current_tab = "dialog_new_game"
|
||||
}
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.handle_new_game_buttons(fields)
|
||||
|
||||
if fields["new_game_confirm"] and
|
||||
fields["te_game_name"] ~= nil and
|
||||
fields["te_game_name"] ~= "" then
|
||||
local gamepath = engine.get_gamepath()
|
||||
|
||||
if gamepath ~= nil and
|
||||
gamepath ~= "" then
|
||||
local gamefolder = cleanup_path(fields["te_game_name"])
|
||||
|
||||
--TODO check for already existing first
|
||||
engine.create_dir(gamepath .. DIR_DELIM .. gamefolder)
|
||||
engine.create_dir(gamepath .. DIR_DELIM .. gamefolder .. DIR_DELIM .. "mods")
|
||||
engine.create_dir(gamepath .. DIR_DELIM .. gamefolder .. DIR_DELIM .. "menu")
|
||||
|
||||
local gameconf =
|
||||
io.open(gamepath .. DIR_DELIM .. gamefolder .. DIR_DELIM .. "game.conf","w")
|
||||
|
||||
if gameconf then
|
||||
gameconf:write("name = " .. fields["te_game_name"])
|
||||
gameconf:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
is_dialog = false,
|
||||
show_buttons = true,
|
||||
current_tab = engine.setting_get("main_menu_tab")
|
||||
}
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.handle_edit_game_buttons(fields)
|
||||
local current_game = gamemgr.get_game(gamemgr.selected_game)
|
||||
|
||||
if fields["btn_close_edit_game"] ~= nil or
|
||||
current_game == nil then
|
||||
return {
|
||||
is_dialog = false,
|
||||
show_buttons = true,
|
||||
current_tab = engine.setting_get("main_menu_tab")
|
||||
}
|
||||
end
|
||||
|
||||
if fields["btn_remove_mod_from_game"] ~= nil then
|
||||
gamemgr.delete_mod(current_game,engine.get_textlist_index("mods_current"))
|
||||
end
|
||||
|
||||
if fields["btn_add_mod_to_game"] ~= nil then
|
||||
local modindex = engine.get_textlist_index("mods_available")
|
||||
|
||||
if modindex > 0 and
|
||||
modindex <= #modmgr.global_mods then
|
||||
|
||||
local sourcepath =
|
||||
engine.get_modpath() .. DIR_DELIM .. modmgr.global_mods[modindex]
|
||||
|
||||
gamemgr.add_mod(current_game,sourcepath)
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.add_mod(gamespec,sourcepath)
|
||||
if gamespec.gamemods_path ~= nil and
|
||||
gamespec.gamemods_path ~= "" then
|
||||
|
||||
local modname = get_last_folder(sourcepath)
|
||||
|
||||
engine.copy_dir(sourcepath,gamespec.gamemods_path .. DIR_DELIM .. modname);
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.delete_mod(gamespec,modindex)
|
||||
if gamespec.gamemods_path ~= nil and
|
||||
gamespec.gamemods_path ~= "" then
|
||||
local game_mods = {}
|
||||
get_mods(gamespec.gamemods_path,game_mods)
|
||||
|
||||
if modindex > 0 and
|
||||
#game_mods >= modindex then
|
||||
|
||||
local modname = game_mods[modindex]
|
||||
|
||||
if modname:find("<MODPACK>") ~= nil then
|
||||
modname = modname:sub(0,modname:find("<") -2)
|
||||
end
|
||||
|
||||
local modpath = gamespec.gamemods_path .. DIR_DELIM .. modname
|
||||
if modpath:sub(0,gamespec.gamemods_path:len()) == gamespec.gamemods_path then
|
||||
engine.delete_dir(modpath)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.get_game_mods(gamespec)
|
||||
|
||||
local retval = ""
|
||||
|
||||
if gamespec.gamemods_path ~= nil and
|
||||
gamespec.gamemods_path ~= "" then
|
||||
local game_mods = {}
|
||||
get_mods(gamespec.gamemods_path,game_mods)
|
||||
|
||||
for i=1,#game_mods,1 do
|
||||
if retval ~= "" then
|
||||
retval = retval..","
|
||||
end
|
||||
retval = retval .. game_mods[i]
|
||||
end
|
||||
end
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.gettab(name)
|
||||
local retval = ""
|
||||
|
||||
if name == "dialog_edit_game" then
|
||||
retval = retval .. gamemgr.dialog_edit_game()
|
||||
end
|
||||
|
||||
if name == "dialog_new_game" then
|
||||
retval = retval .. gamemgr.dialog_new_game()
|
||||
end
|
||||
|
||||
if name == "game_mgr" then
|
||||
retval = retval .. gamemgr.tab()
|
||||
end
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.tab()
|
||||
if gamemgr.selected_game == nil then
|
||||
gamemgr.selected_game = 1
|
||||
end
|
||||
|
||||
local retval =
|
||||
"vertlabel[0,-0.25;GAMES]" ..
|
||||
"label[1,-0.25;Games:]" ..
|
||||
"textlist[1,0.25;4.5,4.4;gamelist;" ..
|
||||
gamemgr.gamelist() ..
|
||||
";" .. gamemgr.selected_game .. "]"
|
||||
|
||||
local current_game = gamemgr.get_game(gamemgr.selected_game)
|
||||
|
||||
if current_game ~= nil then
|
||||
if current_game.menuicon_path ~= nil and
|
||||
current_game.menuicon_path ~= "" then
|
||||
retval = retval ..
|
||||
"image[5.8,-0.25;2,2;" .. current_game.menuicon_path .. "]"
|
||||
end
|
||||
|
||||
retval = retval ..
|
||||
"field[8,-0.25;6,2;;" .. current_game.name .. ";]"..
|
||||
"label[6,1.4;Mods:]" ..
|
||||
"button[9.7,1.5;2,0.2;btn_game_mgr_edit_game;edit game]" ..
|
||||
"textlist[6,2;5.5,3.3;game_mgr_modlist;"
|
||||
.. gamemgr.get_game_mods(current_game) ..";0]" ..
|
||||
"button[1,4.75;3.2,0.5;btn_game_mgr_new_game;new game]"
|
||||
end
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.dialog_edit_game()
|
||||
local current_game = gamemgr.get_game(gamemgr.selected_game)
|
||||
if current_game ~= nil then
|
||||
local retval =
|
||||
"vertlabel[0,-0.25;EDIT GAME]" ..
|
||||
"label[0,-0.25;" .. current_game.name .. "]" ..
|
||||
"button[11.55,-0.2;0.75,0.5;btn_close_edit_game;x]"
|
||||
|
||||
if current_game.menuicon_path ~= nil and
|
||||
current_game.menuicon_path ~= "" then
|
||||
retval = retval ..
|
||||
"image[5.25,0;2,2;" .. current_game.menuicon_path .. "]"
|
||||
end
|
||||
|
||||
retval = retval ..
|
||||
"textlist[0.5,0.5;4.5,4.3;mods_current;"
|
||||
.. gamemgr.get_game_mods(current_game) ..";0]"
|
||||
|
||||
|
||||
retval = retval ..
|
||||
"textlist[7,0.5;4.5,4.3;mods_available;"
|
||||
.. modmgr.get_mods_list() .. ";0]"
|
||||
|
||||
retval = retval ..
|
||||
"button[0.55,4.95;4.7,0.5;btn_remove_mod_from_game;Remove selected mod]"
|
||||
|
||||
retval = retval ..
|
||||
"button[7.05,4.95;4.7,0.5;btn_add_mod_to_game;<<-- Add mod]"
|
||||
|
||||
return retval
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.handle_buttons(tab,fields)
|
||||
local retval = nil
|
||||
|
||||
if tab == "dialog_edit_game" then
|
||||
retval = gamemgr.handle_edit_game_buttons(fields)
|
||||
end
|
||||
|
||||
if tab == "dialog_new_game" then
|
||||
retval = gamemgr.handle_new_game_buttons(fields)
|
||||
end
|
||||
|
||||
if tab == "game_mgr" then
|
||||
retval = gamemgr.handle_games_buttons(fields)
|
||||
end
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.get_game(index)
|
||||
if index > 0 and index <= #gamemgr.games then
|
||||
return gamemgr.games[index]
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.update_gamelist()
|
||||
gamemgr.games = engine.get_games()
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function gamemgr.gamelist()
|
||||
local retval = ""
|
||||
if #gamemgr.games > 0 then
|
||||
retval = retval .. gamemgr.games[1].id
|
||||
|
||||
for i=2,#gamemgr.games,1 do
|
||||
retval = retval .. "," .. gamemgr.games[i].name
|
||||
end
|
||||
end
|
||||
return retval
|
||||
end
|
1190
builtin/mainmenu.lua
Normal file
1190
builtin/mainmenu.lua
Normal file
File diff suppressed because it is too large
Load Diff
105
builtin/mainmenu_helper.lua
Normal file
105
builtin/mainmenu_helper.lua
Normal file
@ -0,0 +1,105 @@
|
||||
--------------------------------------------------------------------------------
|
||||
function dump(o, dumped)
|
||||
dumped = dumped or {}
|
||||
if type(o) == "number" then
|
||||
return tostring(o)
|
||||
elseif type(o) == "string" then
|
||||
return string.format("%q", o)
|
||||
elseif type(o) == "table" then
|
||||
if dumped[o] then
|
||||
return "<circular reference>"
|
||||
end
|
||||
dumped[o] = true
|
||||
local t = {}
|
||||
for k,v in pairs(o) do
|
||||
t[#t+1] = "" .. k .. " = " .. dump(v, dumped)
|
||||
end
|
||||
return "{" .. table.concat(t, ", ") .. "}"
|
||||
elseif type(o) == "boolean" then
|
||||
return tostring(o)
|
||||
elseif type(o) == "function" then
|
||||
return "<function>"
|
||||
elseif type(o) == "userdata" then
|
||||
return "<userdata>"
|
||||
elseif type(o) == "nil" then
|
||||
return "nil"
|
||||
else
|
||||
error("cannot dump a " .. type(o))
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function string:split(sep)
|
||||
local sep, fields = sep or ",", {}
|
||||
local pattern = string.format("([^%s]+)", sep)
|
||||
self:gsub(pattern, function(c) fields[#fields+1] = c end)
|
||||
return fields
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function string:trim()
|
||||
return (self:gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
engine.get_game = function(index)
|
||||
local games = game.get_games()
|
||||
|
||||
if index > 0 and index <= #games then
|
||||
return games[index]
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function fs_escape_string(text)
|
||||
if text ~= nil then
|
||||
while (text:find("\r\n") ~= nil) do
|
||||
local newtext = text:sub(1,text:find("\r\n")-1)
|
||||
newtext = newtext .. " " .. text:sub(text:find("\r\n")+3)
|
||||
|
||||
text = newtext
|
||||
end
|
||||
|
||||
while (text:find("\n") ~= nil) do
|
||||
local newtext = text:sub(1,text:find("\n")-1)
|
||||
newtext = newtext .. " " .. text:sub(text:find("\n")+1)
|
||||
|
||||
text = newtext
|
||||
end
|
||||
|
||||
while (text:find("\r") ~= nil) do
|
||||
local newtext = text:sub(1,text:find("\r")-1)
|
||||
newtext = newtext .. " " .. text:sub(text:find("\r")+1)
|
||||
|
||||
text = newtext
|
||||
end
|
||||
|
||||
text = text:gsub("%[","%[%[")
|
||||
text = text:gsub("]","]]")
|
||||
text = text:gsub(";"," ")
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function explode_textlist_event(text)
|
||||
|
||||
local retval = {}
|
||||
retval.typ = "INV"
|
||||
|
||||
local parts = text:split(":")
|
||||
|
||||
if #parts == 2 then
|
||||
retval.typ = parts[1]:trim()
|
||||
retval.index= tonumber(parts[2]:trim())
|
||||
|
||||
if type(retval.index) ~= "number" then
|
||||
retval.typ = "INV"
|
||||
end
|
||||
end
|
||||
|
||||
return retval
|
||||
end
|
881
builtin/modmgr.lua
Normal file
881
builtin/modmgr.lua
Normal file
@ -0,0 +1,881 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2013 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function get_mods(path,retval,basefolder)
|
||||
|
||||
local mods = engine.get_dirlist(path,true)
|
||||
|
||||
for i=1,#mods,1 do
|
||||
local filename = path .. DIR_DELIM .. mods[i] .. DIR_DELIM .. "modpack.txt"
|
||||
local modpackfile,error = io.open(filename,"r")
|
||||
|
||||
local name = mods[i]
|
||||
if basefolder ~= nil and
|
||||
basefolder ~= "" then
|
||||
name = basefolder .. DIR_DELIM .. mods[i]
|
||||
end
|
||||
|
||||
if modpackfile ~= nil then
|
||||
modpackfile:close()
|
||||
table.insert(retval,name .. " <MODPACK>")
|
||||
get_mods(path .. DIR_DELIM .. name,retval,name)
|
||||
else
|
||||
|
||||
table.insert(retval,name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--modmanager implementation
|
||||
modmgr = {}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.extract(modfile)
|
||||
if modfile.type == "zip" then
|
||||
local tempfolder = os.tempfolder()
|
||||
|
||||
if tempfolder ~= nil and
|
||||
tempfodler ~= "" then
|
||||
engine.create_dir(tempfolder)
|
||||
engine.extract_zip(modfile.name,tempfolder)
|
||||
return tempfolder
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
function modmgr.getbasefolder(temppath)
|
||||
|
||||
if temppath == nil then
|
||||
return {
|
||||
type = "invalid",
|
||||
path = ""
|
||||
}
|
||||
end
|
||||
|
||||
local testfile = io.open(temppath .. DIR_DELIM .. "init.lua","r")
|
||||
if testfile ~= nil then
|
||||
testfile:close()
|
||||
return {
|
||||
type="mod",
|
||||
path=temppath
|
||||
}
|
||||
end
|
||||
|
||||
testfile = io.open(temppath .. DIR_DELIM .. "modpack.txt","r")
|
||||
if testfile ~= nil then
|
||||
testfile:close()
|
||||
return {
|
||||
type="modpack",
|
||||
path=temppath
|
||||
}
|
||||
end
|
||||
|
||||
local subdirs = engine.get_dirlist(temppath,true)
|
||||
|
||||
--only single mod or modpack allowed
|
||||
if #subdirs ~= 1 then
|
||||
return {
|
||||
type = "invalid",
|
||||
path = ""
|
||||
}
|
||||
end
|
||||
|
||||
testfile =
|
||||
io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."init.lua","r")
|
||||
if testfile ~= nil then
|
||||
testfile:close()
|
||||
return {
|
||||
type="mod",
|
||||
path= temppath .. DIR_DELIM .. subdirs[1]
|
||||
}
|
||||
end
|
||||
|
||||
testfile =
|
||||
io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."modpack.txt","r")
|
||||
if testfile ~= nil then
|
||||
testfile:close()
|
||||
return {
|
||||
type="modpack",
|
||||
path=temppath .. DIR_DELIM .. subdirs[1]
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
type = "invalid",
|
||||
path = ""
|
||||
}
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.isValidModname(modpath)
|
||||
if modpath:find("-") ~= nil then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.parse_register_line(line)
|
||||
local pos1 = line:find("\"")
|
||||
local pos2 = nil
|
||||
if pos1 ~= nil then
|
||||
pos2 = line:find("\"",pos1+1)
|
||||
end
|
||||
|
||||
if pos1 ~= nil and pos2 ~= nil then
|
||||
local item = line:sub(pos1+1,pos2-1)
|
||||
|
||||
if item ~= nil and
|
||||
item ~= "" then
|
||||
local pos3 = item:find(":")
|
||||
|
||||
if pos3 ~= nil then
|
||||
return item:sub(1,pos3-1)
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.parse_dofile_line(modpath,line)
|
||||
local pos1 = line:find("\"")
|
||||
local pos2 = nil
|
||||
if pos1 ~= nil then
|
||||
pos2 = line:find("\"",pos1+1)
|
||||
end
|
||||
|
||||
if pos1 ~= nil and pos2 ~= nil then
|
||||
local filename = line:sub(pos1+1,pos2-1)
|
||||
|
||||
if filename ~= nil and
|
||||
filename ~= "" and
|
||||
filename:find(".lua") then
|
||||
return modmgr.identify_modname(modpath,filename)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.update_global_mods()
|
||||
local modpath = engine.get_modpath()
|
||||
modmgr.global_mods = {}
|
||||
if modpath ~= nil and
|
||||
modpath ~= "" then
|
||||
get_mods(modpath,modmgr.global_mods)
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.get_mods_list()
|
||||
local toadd = ""
|
||||
|
||||
modmgr.update_global_mods()
|
||||
|
||||
if modmgr.global_mods ~= nil then
|
||||
for i=1,#modmgr.global_mods,1 do
|
||||
if toadd ~= "" then
|
||||
toadd = toadd..","
|
||||
end
|
||||
toadd = toadd .. modmgr.global_mods[i]
|
||||
end
|
||||
end
|
||||
|
||||
return toadd
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.mod_exists(basename)
|
||||
modmgr.update_global_mods()
|
||||
|
||||
if modmgr.global_mods ~= nil then
|
||||
for i=1,#modmgr.global_mods,1 do
|
||||
if modmgr.global_mods[i] == basename then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.identify_modname(modpath,filename)
|
||||
local testfile = io.open(modpath .. DIR_DELIM .. filename,"r")
|
||||
if testfile ~= nil then
|
||||
local line = testfile:read()
|
||||
|
||||
while line~= nil do
|
||||
local modname = nil
|
||||
|
||||
if line:find("minetest.register_tool") then
|
||||
modname = modmgr.parse_register_line(line)
|
||||
end
|
||||
|
||||
if line:find("minetest.register_craftitem") then
|
||||
modname = modmgr.parse_register_line(line)
|
||||
end
|
||||
|
||||
|
||||
if line:find("minetest.register_node") then
|
||||
modname = modmgr.parse_register_line(line)
|
||||
end
|
||||
|
||||
if line:find("dofile") then
|
||||
modname = modmgr.parse_dofile_line(modpath,line)
|
||||
end
|
||||
|
||||
if modname ~= nil then
|
||||
testfile:close()
|
||||
return modname
|
||||
end
|
||||
|
||||
line = testfile:read()
|
||||
end
|
||||
testfile:close()
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.tab()
|
||||
if modmgr.selected_mod == nil then
|
||||
modmgr.selected_mod = 1
|
||||
end
|
||||
|
||||
local retval =
|
||||
"vertlabel[0,-0.25;MODS]" ..
|
||||
"label[0.8,-0.25;Installed Mods:]" ..
|
||||
"textlist[0.75,0.25;4.5,4.3;modlist;" ..
|
||||
modmgr.get_mods_list() ..
|
||||
";" .. modmgr.selected_mod .. "]"
|
||||
|
||||
retval = retval ..
|
||||
"button[1,4.85;2,0.5;btn_mod_mgr_install_local;Install]" ..
|
||||
"button[3,4.85;2,0.5;btn_mod_mgr_download;Download]"
|
||||
|
||||
if #modmgr.global_mods >= modmgr.selected_mod and
|
||||
modmgr.global_mods[modmgr.selected_mod]:find("<MODPACK>") then
|
||||
retval = retval .. "button[10,4.85;2,0.5;btn_mod_mgr_rename_modpack;Rename]"
|
||||
end
|
||||
|
||||
if #modmgr.global_mods >= modmgr.selected_mod then
|
||||
local modpath = engine.get_modpath()
|
||||
--show dependencys
|
||||
if modmgr.global_mods[modmgr.selected_mod]:find("<MODPACK>") == nil then
|
||||
retval = retval ..
|
||||
"label[6,1.9;Depends:]" ..
|
||||
"textlist[6,2.4;5.7,2;deplist;"
|
||||
|
||||
toadd = modmgr.get_dependencys(modpath .. DIR_DELIM ..
|
||||
modmgr.global_mods[modmgr.selected_mod])
|
||||
|
||||
retval = retval .. toadd .. ";0;true,false]"
|
||||
|
||||
--TODO read modinfo
|
||||
end
|
||||
--show delete button
|
||||
retval = retval .. "button[8,4.85;2,0.5;btn_mod_mgr_delete_mod;Delete]"
|
||||
end
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.dialog_rename_modpack()
|
||||
|
||||
local modname = modmgr.global_mods[modmgr.selected_mod]
|
||||
modname = modname:sub(0,modname:find("<") -2)
|
||||
|
||||
local retval =
|
||||
"label[1.75,1;Rename Modpack:]"..
|
||||
"field[4.5,1.4;6,0.5;te_modpack_name;;" ..
|
||||
modname ..
|
||||
"]" ..
|
||||
"button[5,4.2;2.6,0.5;dlg_rename_modpack_confirm;Accept]" ..
|
||||
"button[7.5,4.2;2.8,0.5;dlg_rename_modpack_cancel;Cancel]"
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.precheck()
|
||||
if modmgr.global_mods == nil then
|
||||
modmgr.update_global_mods()
|
||||
end
|
||||
|
||||
if modmgr.world_config_selected_world == nil then
|
||||
modmgr.world_config_selected_world = 1
|
||||
end
|
||||
|
||||
if modmgr.world_config_selected_mod == nil then
|
||||
modmgr.world_config_selected_mod = 1
|
||||
end
|
||||
|
||||
if modmgr.hide_gamemods == nil then
|
||||
modmgr.hide_gamemods = true
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.get_worldmod_idx()
|
||||
if not modmgr.hide_gamemods then
|
||||
return modmgr.world_config_selected_mod - #modmgr.worldconfig.game_mods
|
||||
else
|
||||
return modmgr.world_config_selected_mod
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.is_gamemod()
|
||||
if not modmgr.hide_gamemods then
|
||||
if modmgr.world_config_selected_mod <= #modmgr.worldconfig.game_mods then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.render_worldmodlist()
|
||||
local retval = ""
|
||||
|
||||
for i=1,#modmgr.global_mods,1 do
|
||||
local parts = modmgr.global_mods[i]:split(DIR_DELIM)
|
||||
local shortname = parts[#parts]
|
||||
if modmgr.worldconfig.global_mods[shortname] then
|
||||
retval = retval .. "#GRN" .. modmgr.global_mods[i] .. ","
|
||||
else
|
||||
retval = retval .. modmgr.global_mods[i] .. ","
|
||||
end
|
||||
end
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.render_gamemodlist()
|
||||
local retval = ""
|
||||
for i=1,#modmgr.worldconfig.game_mods,1 do
|
||||
retval = retval ..
|
||||
"#BLU" .. modmgr.worldconfig.game_mods[i] .. ","
|
||||
end
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.dialog_configure_world()
|
||||
modmgr.precheck()
|
||||
|
||||
local modpack_selected = false
|
||||
local gamemod_selected = modmgr.is_gamemod()
|
||||
local modname = ""
|
||||
local modfolder = ""
|
||||
local shortname = ""
|
||||
|
||||
if not gamemod_selected then
|
||||
local worldmodidx = modmgr.get_worldmod_idx()
|
||||
modname = modmgr.global_mods[worldmodidx]
|
||||
|
||||
if modname:find("<MODPACK>") ~= nil then
|
||||
modname = modname:sub(0,modname:find("<") -2)
|
||||
modpack_selected = true
|
||||
end
|
||||
|
||||
local parts = modmgr.global_mods[worldmodidx]:split(DIR_DELIM)
|
||||
shortname = parts[#parts]
|
||||
|
||||
modfolder = engine.get_modpath() .. DIR_DELIM .. modname
|
||||
end
|
||||
|
||||
local worldspec = engine.get_worlds()[modmgr.world_config_selected_world]
|
||||
|
||||
local retval =
|
||||
"size[11,6.5]" ..
|
||||
"label[1.5,-0.25;World: " .. worldspec.name .. "]"
|
||||
|
||||
if modmgr.hide_gamemods then
|
||||
retval = retval .. "checkbox[5.5,6.15;cb_hide_gamemods;Hide Game;true]"
|
||||
else
|
||||
retval = retval .. "checkbox[5.5,6.15;cb_hide_gamemods;Hide Game;false]"
|
||||
end
|
||||
retval = retval ..
|
||||
"button[9.25,6.35;2,0.5;btn_config_world_save;Save]" ..
|
||||
"button[7.4,6.35;2,0.5;btn_config_world_cancel;Cancel]" ..
|
||||
"textlist[5.5,-0.25;5.5,6.5;world_config_modlist;"
|
||||
|
||||
|
||||
if not modmgr.hide_gamemods then
|
||||
retval = retval .. modmgr.render_gamemodlist()
|
||||
end
|
||||
|
||||
retval = retval .. modmgr.render_worldmodlist()
|
||||
|
||||
retval = retval .. ";" .. modmgr.world_config_selected_mod .."]"
|
||||
|
||||
if not gamemod_selected then
|
||||
retval = retval ..
|
||||
"label[0,0.45;Mod:]" ..
|
||||
"label[0.75,0.45;" .. modname .. "]" ..
|
||||
"label[0,1.5;depends on:]" ..
|
||||
"textlist[0,2;5,2;world_config_depends;" ..
|
||||
modmgr.get_dependencys(modfolder) .. ";0]" ..
|
||||
"label[0,4;depends on:]" ..
|
||||
"textlist[0,4.5;5,2;world_config_is_required;;0]"
|
||||
|
||||
if modpack_selected then
|
||||
retval = retval ..
|
||||
"button[-0.05,1.05;2,0.5;btn_cfgw_enable_all;Enable All]" ..
|
||||
"button[3.25,1.05;2,0.5;btn_cfgw_disable_all;Disable All]"
|
||||
else
|
||||
retval = retval ..
|
||||
"checkbox[0,0.8;cb_mod_enabled;enabled;"
|
||||
|
||||
if modmgr.worldconfig.global_mods[shortname] then
|
||||
print("checkbox " .. shortname .. " enabled")
|
||||
retval = retval .. "true"
|
||||
else
|
||||
print("checkbox " .. shortname .. " disabled")
|
||||
retval = retval .. "false"
|
||||
end
|
||||
|
||||
retval = retval .. "]"
|
||||
end
|
||||
end
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.handle_buttons(tab,fields)
|
||||
|
||||
local retval = nil
|
||||
|
||||
if tab == "mod_mgr" then
|
||||
retval = modmgr.handle_modmgr_buttons(fields)
|
||||
end
|
||||
|
||||
if tab == "dialog_rename_modpack" then
|
||||
retval = modmgr.handle_rename_modpack_buttons(fields)
|
||||
end
|
||||
|
||||
if tab == "dialog_delete_mod" then
|
||||
retval = modmgr.handle_delete_mod_buttons(fields)
|
||||
end
|
||||
|
||||
if tab == "dialog_configure_world" then
|
||||
retval = modmgr.handle_configure_world_buttons(fields)
|
||||
end
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.get_dependencys(modfolder)
|
||||
local filename = modfolder ..
|
||||
DIR_DELIM .. "depends.txt"
|
||||
|
||||
local dependencyfile = io.open(filename,"r")
|
||||
|
||||
local toadd = ""
|
||||
if dependencyfile then
|
||||
local dependency = dependencyfile:read("*l")
|
||||
while dependency do
|
||||
if toadd ~= "" then
|
||||
toadd = toadd .. ","
|
||||
end
|
||||
toadd = toadd .. dependency
|
||||
dependency = dependencyfile:read()
|
||||
end
|
||||
dependencyfile:close()
|
||||
else
|
||||
print(filename .. " not found")
|
||||
end
|
||||
|
||||
return toadd
|
||||
end
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.get_worldconfig(worldpath)
|
||||
local filename = worldpath ..
|
||||
DIR_DELIM .. "world.mt"
|
||||
|
||||
local worldfile = io.open(filename,"r")
|
||||
|
||||
local worldconfig = {}
|
||||
worldconfig.global_mods = {}
|
||||
worldconfig.game_mods = {}
|
||||
|
||||
if worldfile then
|
||||
local dependency = worldfile:read("*l")
|
||||
while dependency do
|
||||
local parts = dependency:split("=")
|
||||
|
||||
local key = parts[1]:trim()
|
||||
|
||||
if key == "gameid" then
|
||||
worldconfig.id = parts[2]:trim()
|
||||
else
|
||||
local key = parts[1]:trim():sub(10)
|
||||
if parts[2]:trim() == "true" then
|
||||
print("found enabled mod: >" .. key .. "<")
|
||||
worldconfig.global_mods[key] = true
|
||||
else
|
||||
print("found disabled mod: >" .. key .. "<")
|
||||
worldconfig.global_mods[key] = false
|
||||
end
|
||||
end
|
||||
dependency = worldfile:read("*l")
|
||||
end
|
||||
worldfile:close()
|
||||
else
|
||||
print(filename .. " not found")
|
||||
end
|
||||
|
||||
--read gamemods
|
||||
local gamemodpath = engine.get_gamepath() .. DIR_DELIM .. worldconfig.id .. DIR_DELIM .. "mods"
|
||||
|
||||
print("reading game mods from: " .. dump(gamemodpath))
|
||||
get_mods(gamemodpath,worldconfig.game_mods)
|
||||
|
||||
return worldconfig
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.handle_modmgr_buttons(fields)
|
||||
local retval = {
|
||||
tab = nil,
|
||||
is_dialog = nil,
|
||||
show_buttons = nil,
|
||||
}
|
||||
|
||||
if fields["modlist"] ~= nil then
|
||||
local event = explode_textlist_event(fields["modlist"])
|
||||
modmgr.selected_mod = event.index
|
||||
end
|
||||
|
||||
if fields["btn_mod_mgr_install_local"] ~= nil then
|
||||
engine.show_file_open_dialog("mod_mgt_open_dlg","Select Mod File:")
|
||||
end
|
||||
|
||||
if fields["btn_mod_mgr_download"] ~= nil then
|
||||
retval.current_tab = "dialog_modstore_unsorted"
|
||||
retval.is_dialog = true
|
||||
retval.show_buttons = false
|
||||
return retval
|
||||
end
|
||||
|
||||
if fields["btn_mod_mgr_rename_modpack"] ~= nil then
|
||||
retval.current_tab = "dialog_rename_modpack"
|
||||
retval.is_dialog = true
|
||||
retval.show_buttons = false
|
||||
return retval
|
||||
end
|
||||
|
||||
if fields["btn_mod_mgr_delete_mod"] ~= nil then
|
||||
retval.current_tab = "dialog_delete_mod"
|
||||
retval.is_dialog = true
|
||||
retval.show_buttons = false
|
||||
return retval
|
||||
end
|
||||
|
||||
if fields["mod_mgt_open_dlg_accepted"] ~= nil and
|
||||
fields["mod_mgt_open_dlg_accepted"] ~= "" then
|
||||
modmgr.installmod(fields["mod_mgt_open_dlg_accepted"],nil)
|
||||
end
|
||||
|
||||
return nil;
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.installmod(modfilename,basename)
|
||||
local modfile = identify_filetype(modfilename)
|
||||
|
||||
local modpath = modmgr.extract(modfile)
|
||||
|
||||
if modpath == nil then
|
||||
gamedata.errormessage = "Install Mod: file: " .. modfile.name ..
|
||||
"\nInstall Mod: unsupported filetype \"" .. modfile.type .. "\""
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
local basefolder = modmgr.getbasefolder(modpath)
|
||||
|
||||
if basefolder.type == "modpack" then
|
||||
local clean_path = nil
|
||||
|
||||
if basename ~= nil then
|
||||
clean_path = "mp_" .. basename
|
||||
end
|
||||
|
||||
if clean_path == nil then
|
||||
clean_path = get_last_folder(cleanup_path(basefolder.path))
|
||||
end
|
||||
|
||||
if clean_path ~= nil then
|
||||
local targetpath = engine.get_modpath() .. DIR_DELIM .. clean_path
|
||||
engine.copy_dir(basefolder.path,targetpath)
|
||||
else
|
||||
gamedata.errormessage = "Install Mod: unable to find suitable foldername for modpack "
|
||||
.. modfilename
|
||||
end
|
||||
end
|
||||
|
||||
if basefolder.type == "mod" then
|
||||
local targetfolder = basename
|
||||
|
||||
if targetfolder == nil then
|
||||
targetfolder = modmgr.identify_modname(basefolder.path,"init.lua")
|
||||
end
|
||||
|
||||
--if heuristic failed try to use current foldername
|
||||
if targetfolder == nil then
|
||||
targetfolder = get_last_folder(basefolder.path)
|
||||
end
|
||||
|
||||
if targetfolder ~= nil and modmgr.isValidModname(targetfolder) then
|
||||
local targetpath = engine.get_modpath() .. DIR_DELIM .. targetfolder
|
||||
engine.copy_dir(basefolder.path,targetpath)
|
||||
else
|
||||
gamedata.errormessage = "Install Mod: unable to find real modname for: "
|
||||
.. modfilename
|
||||
end
|
||||
end
|
||||
|
||||
engine.delete_dir(modpath)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.handle_rename_modpack_buttons(fields)
|
||||
local oldname = modmgr.global_mods[modmgr.selected_mod]
|
||||
oldname = oldname:sub(0,oldname:find("<") -2)
|
||||
|
||||
if fields["dlg_rename_modpack_confirm"] ~= nil then
|
||||
local oldpath = engine.get_modpath() .. DIR_DELIM .. oldname
|
||||
local targetpath = engine.get_modpath() .. DIR_DELIM .. fields["te_modpack_name"]
|
||||
engine.copy_dir(oldpath,targetpath,false)
|
||||
end
|
||||
|
||||
return {
|
||||
is_dialog = false,
|
||||
show_buttons = true,
|
||||
current_tab = engine.setting_get("main_menu_tab")
|
||||
}
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.handle_configure_world_buttons(fields)
|
||||
if fields["world_config_modlist"] ~= nil then
|
||||
local event = explode_textlist_event(fields["world_config_modlist"])
|
||||
modmgr.world_config_selected_mod = event.index
|
||||
end
|
||||
|
||||
if fields["cb_mod_enabled"] ~= nil then
|
||||
local index = modmgr.get_worldmod_idx()
|
||||
local modname = modmgr.global_mods[index]
|
||||
|
||||
local parts = modmgr.global_mods[index]:split(DIR_DELIM)
|
||||
local shortname = parts[#parts]
|
||||
|
||||
if fields["cb_mod_enabled"] == "true" then
|
||||
modmgr.worldconfig.global_mods[shortname] = true
|
||||
else
|
||||
modmgr.worldconfig.global_mods[shortname] = false
|
||||
end
|
||||
end
|
||||
|
||||
if fields["cb_hide_gamemods"] ~= nil then
|
||||
if fields["cb_hide_gamemods"] == "true" then
|
||||
modmgr.hide_gamemods = true
|
||||
else
|
||||
modmgr.hide_gamemods = false
|
||||
end
|
||||
end
|
||||
|
||||
if fields["btn_config_world_save"] then
|
||||
local worldspec = engine.get_worlds()[modmgr.world_config_selected_world]
|
||||
|
||||
local filename = worldspec.path ..
|
||||
DIR_DELIM .. "world.mt"
|
||||
|
||||
local worldfile = io.open(filename,"w")
|
||||
|
||||
if worldfile then
|
||||
worldfile:write("gameid = " .. modmgr.worldconfig.id .. "\n")
|
||||
for key,value in pairs(modmgr.worldconfig.global_mods) do
|
||||
if value then
|
||||
worldfile:write("load_mod_" .. key .. " = true" .. "\n")
|
||||
else
|
||||
worldfile:write("load_mod_" .. key .. " = false" .. "\n")
|
||||
end
|
||||
end
|
||||
|
||||
worldfile:close()
|
||||
end
|
||||
|
||||
modmgr.worldconfig = nil
|
||||
|
||||
return {
|
||||
is_dialog = false,
|
||||
show_buttons = true,
|
||||
current_tab = engine.setting_get("main_menu_tab")
|
||||
}
|
||||
end
|
||||
|
||||
if fields["btn_config_world_cancel"] then
|
||||
|
||||
modmgr.worldconfig = nil
|
||||
|
||||
return {
|
||||
is_dialog = false,
|
||||
show_buttons = true,
|
||||
current_tab = engine.setting_get("main_menu_tab")
|
||||
}
|
||||
end
|
||||
|
||||
if fields["btn_cfgw_enable_all"] then
|
||||
local worldmodidx = modmgr.get_worldmod_idx()
|
||||
modname = modmgr.global_mods[worldmodidx]
|
||||
|
||||
modname = modname:sub(0,modname:find("<") -2)
|
||||
|
||||
for i=1,#modmgr.global_mods,1 do
|
||||
|
||||
if modmgr.global_mods[i]:find("<MODPACK>") == nil then
|
||||
local modpackpart = modmgr.global_mods[i]:sub(0,modname:len())
|
||||
|
||||
if modpackpart == modname then
|
||||
local parts = modmgr.global_mods[i]:split(DIR_DELIM)
|
||||
local shortname = parts[#parts]
|
||||
modmgr.worldconfig.global_mods[shortname] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if fields["btn_cfgw_disable_all"] then
|
||||
local worldmodidx = modmgr.get_worldmod_idx()
|
||||
modname = modmgr.global_mods[worldmodidx]
|
||||
|
||||
modname = modname:sub(0,modname:find("<") -2)
|
||||
|
||||
for i=1,#modmgr.global_mods,1 do
|
||||
local modpackpart = modmgr.global_mods[i]:sub(0,modname:len())
|
||||
|
||||
if modpackpart == modname then
|
||||
local parts = modmgr.global_mods[i]:split(DIR_DELIM)
|
||||
local shortname = parts[#parts]
|
||||
modmgr.worldconfig.global_mods[shortname] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.handle_delete_mod_buttons(fields)
|
||||
local modname = modmgr.global_mods[modmgr.selected_mod]
|
||||
|
||||
if modname:find("<MODPACK>") ~= nil then
|
||||
modname = modname:sub(0,modname:find("<") -2)
|
||||
end
|
||||
|
||||
if fields["dlg_delete_mod_confirm"] ~= nil then
|
||||
local oldpath = engine.get_modpath() .. DIR_DELIM .. modname
|
||||
|
||||
if oldpath ~= nil and
|
||||
oldpath ~= "" and
|
||||
oldpath ~= engine.get_modpath() then
|
||||
engine.delete_dir(oldpath)
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
is_dialog = false,
|
||||
show_buttons = true,
|
||||
current_tab = engine.setting_get("main_menu_tab")
|
||||
}
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.dialog_delete_mod()
|
||||
|
||||
local modname = modmgr.global_mods[modmgr.selected_mod]
|
||||
|
||||
if modname:find("<MODPACK>") ~= nil then
|
||||
modname = modname:sub(0,modname:find("<") -2)
|
||||
end
|
||||
|
||||
local retval =
|
||||
"field[1.75,1;10,3;;Are you sure you want to delete ".. modname .. "?;]"..
|
||||
"button[4,4.2;1,0.5;dlg_delete_mod_confirm;Yes]" ..
|
||||
"button[6.5,4.2;3,0.5;dlg_delete_mod_cancel;No of course not!]"
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.init_worldconfig()
|
||||
|
||||
local worldspec = engine.get_worlds()[modmgr.world_config_selected_world]
|
||||
|
||||
if worldspec ~= nil then
|
||||
--read worldconfig
|
||||
modmgr.worldconfig = modmgr.get_worldconfig(worldspec.path)
|
||||
|
||||
if modmgr.worldconfig.id == nil or
|
||||
modmgr.worldconfig.id == "" then
|
||||
modmgr.worldconfig = nil
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modmgr.gettab(name)
|
||||
local retval = ""
|
||||
|
||||
if name == "mod_mgr" then
|
||||
retval = retval .. modmgr.tab()
|
||||
end
|
||||
|
||||
if name == "dialog_rename_modpack" then
|
||||
retval = retval .. modmgr.dialog_rename_modpack()
|
||||
end
|
||||
|
||||
if name == "dialog_delete_mod" then
|
||||
retval = retval .. modmgr.dialog_delete_mod()
|
||||
end
|
||||
|
||||
if name == "dialog_configure_world" then
|
||||
retval = retval .. modmgr.dialog_configure_world()
|
||||
end
|
||||
|
||||
return retval
|
||||
end
|
275
builtin/modstore.lua
Normal file
275
builtin/modstore.lua
Normal file
@ -0,0 +1,275 @@
|
||||
--Minetest
|
||||
--Copyright (C) 2013 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
--modstore implementation
|
||||
modstore = {}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modstore.init()
|
||||
modstore.tabnames = {}
|
||||
|
||||
table.insert(modstore.tabnames,"dialog_modstore_unsorted")
|
||||
table.insert(modstore.tabnames,"dialog_modstore_search")
|
||||
|
||||
modstore.modsperpage = 5
|
||||
|
||||
modstore.basetexturedir = engine.get_gamepath() .. DIR_DELIM .. ".." ..
|
||||
DIR_DELIM .. "textures" .. DIR_DELIM .. "base" ..
|
||||
DIR_DELIM .. "pack" .. DIR_DELIM
|
||||
modstore.update_modlist()
|
||||
|
||||
modstore.current_list = nil
|
||||
|
||||
modstore.details_cache = {}
|
||||
end
|
||||
--------------------------------------------------------------------------------
|
||||
function modstore.nametoindex(name)
|
||||
|
||||
for i=1,#modstore.tabnames,1 do
|
||||
if modstore.tabnames[i] == name then
|
||||
return i
|
||||
end
|
||||
end
|
||||
|
||||
return 1
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modstore.gettab(tabname)
|
||||
local retval = ""
|
||||
|
||||
local is_modstore_tab = false
|
||||
|
||||
if tabname == "dialog_modstore_unsorted" then
|
||||
retval = modstore.getmodlist(modstore.modlist_unsorted)
|
||||
is_modstore_tab = true
|
||||
end
|
||||
|
||||
if tabname == "dialog_modstore_search" then
|
||||
|
||||
|
||||
is_modstore_tab = true
|
||||
end
|
||||
|
||||
if is_modstore_tab then
|
||||
return modstore.tabheader(tabname) .. retval
|
||||
end
|
||||
|
||||
if tabname == "modstore_mod_installed" then
|
||||
return "size[6,2]label[0.25,0.25;Mod: " .. modstore.lastmodtitle ..
|
||||
" installed successfully]" ..
|
||||
"button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;ok]"
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modstore.tabheader(tabname)
|
||||
local retval = "size[12,9.25]"
|
||||
retval = retval .. "tabheader[-0.3,-0.99;modstore_tab;" ..
|
||||
"Unsorted,Search;" ..
|
||||
modstore.nametoindex(tabname) .. ";true;false]"
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modstore.handle_buttons(current_tab,fields)
|
||||
|
||||
modstore.lastmodtitle = ""
|
||||
|
||||
if fields["modstore_tab"] then
|
||||
local index = tonumber(fields["modstore_tab"])
|
||||
|
||||
if index > 0 and
|
||||
index <= #modstore.tabnames then
|
||||
return {
|
||||
current_tab = modstore.tabnames[index],
|
||||
is_dialog = true,
|
||||
show_buttons = false
|
||||
}
|
||||
end
|
||||
|
||||
modstore.modlist_page = 0
|
||||
end
|
||||
|
||||
if fields["btn_modstore_page_up"] then
|
||||
if modstore.current_list ~= nil and modstore.current_list.page > 0 then
|
||||
modstore.current_list.page = modstore.current_list.page - 1
|
||||
end
|
||||
end
|
||||
|
||||
if fields["btn_modstore_page_down"] then
|
||||
if modstore.current_list ~= nil and
|
||||
modstore.current_list.page <modstore.current_list.pagecount then
|
||||
modstore.current_list.page = modstore.current_list.page +1
|
||||
end
|
||||
end
|
||||
|
||||
if fields["btn_confirm_mod_successfull"] then
|
||||
return {
|
||||
current_tab = modstore.tabnames[1],
|
||||
is_dialog = true,
|
||||
show_buttons = false
|
||||
}
|
||||
end
|
||||
|
||||
for i=1, modstore.modsperpage, 1 do
|
||||
local installbtn = "btn_install_mod_" .. i
|
||||
|
||||
if fields[installbtn] then
|
||||
local modlistentry =
|
||||
modstore.current_list.page * modstore.modsperpage + i
|
||||
|
||||
local moddetails = modstore.get_details(modstore.current_list.data[modlistentry].id)
|
||||
|
||||
local fullurl = engine.setting_get("modstore_download_url") ..
|
||||
moddetails.download_url
|
||||
local modfilename = os.tempfolder() .. ".zip"
|
||||
print("Downloading mod from: " .. fullurl .. " to ".. modfilename)
|
||||
|
||||
if engine.download_file(fullurl,modfilename) then
|
||||
|
||||
modmgr.installmod(modfilename,moddetails.basename)
|
||||
|
||||
os.remove(modfilename)
|
||||
modstore.lastmodtitle = modstore.current_list.data[modlistentry].title
|
||||
|
||||
return {
|
||||
current_tab = "modstore_mod_installed",
|
||||
is_dialog = true,
|
||||
show_buttons = false
|
||||
}
|
||||
else
|
||||
gamedata.errormessage = "Unable to download " ..
|
||||
moddetails.download_url .. " (internet connection?)"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modstore.update_modlist()
|
||||
modstore.modlist_unsorted = {}
|
||||
modstore.modlist_unsorted.data = engine.get_modstore_list()
|
||||
|
||||
if modstore.modlist_unsorted.data ~= nil then
|
||||
modstore.modlist_unsorted.pagecount =
|
||||
math.floor((#modstore.modlist_unsorted.data / modstore.modsperpage))
|
||||
else
|
||||
modstore.modlist_unsorted.data = {}
|
||||
modstore.modlist_unsorted.pagecount = 0
|
||||
end
|
||||
modstore.modlist_unsorted.page = 0
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modstore.getmodlist(list)
|
||||
local retval = ""
|
||||
retval = retval .. "label[10,-0.4;Page " .. (list.page +1) ..
|
||||
" of " .. (list.pagecount +1) .. "]"
|
||||
|
||||
retval = retval .. "button[11.6,-0.1;0.5,0.5;btn_modstore_page_up;^]"
|
||||
retval = retval .. "box[11.6,0.35;0.28,8.6;BLK]"
|
||||
local scrollbarpos = 0.35 + (8.1/list.pagecount) * list.page
|
||||
retval = retval .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;LIM]"
|
||||
retval = retval .. "button[11.6,9.0;0.5,0.5;btn_modstore_page_down;v]"
|
||||
|
||||
|
||||
if #list.data < (list.page * modstore.modsperpage) then
|
||||
return retval
|
||||
end
|
||||
|
||||
local endmod = (list.page * modstore.modsperpage) + modstore.modsperpage
|
||||
|
||||
if (endmod > #list.data) then
|
||||
endmod = #list.data
|
||||
end
|
||||
|
||||
for i=(list.page * modstore.modsperpage) +1, endmod, 1 do
|
||||
--getmoddetails
|
||||
local details = modstore.get_details(list.data[i].id)
|
||||
|
||||
if details ~= nil then
|
||||
local screenshot_ypos = (i-1 - (list.page * modstore.modsperpage))*1.9 +0.2
|
||||
|
||||
retval = retval .. "box[0," .. screenshot_ypos .. ";11.4,1.75;WHT]"
|
||||
|
||||
--screenshot
|
||||
if details.screenshot_url ~= nil and
|
||||
details.screenshot_url ~= "" then
|
||||
if list.data[i].texturename == nil then
|
||||
print("downloading screenshot: " .. details.screenshot_url)
|
||||
local filename = os.tempfolder()
|
||||
|
||||
if engine.download_file(details.screenshot_url,filename) then
|
||||
list.data[i].texturename = filename
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if list.data[i].texturename == nil then
|
||||
list.data[i].texturename = modstore.basetexturedir .. "no_screenshot.png"
|
||||
end
|
||||
|
||||
retval = retval .. "image[0,".. screenshot_ypos .. ";3,2;" ..
|
||||
list.data[i].texturename .. "]"
|
||||
|
||||
--title + author
|
||||
retval = retval .."label[2.75," .. screenshot_ypos .. ";" ..
|
||||
fs_escape_string(details.title) .. " (" .. details.author .. ")]"
|
||||
|
||||
--description
|
||||
local descriptiony = screenshot_ypos + 0.5
|
||||
retval = retval .. "textarea[3," .. descriptiony .. ";6.5,1.6;;" ..
|
||||
fs_escape_string(details.description) .. ";]"
|
||||
--rating
|
||||
local ratingy = screenshot_ypos + 0.6
|
||||
retval = retval .."label[10.1," .. ratingy .. ";Rating: " .. details.rating .."]"
|
||||
|
||||
--install button
|
||||
local buttony = screenshot_ypos + 1.2
|
||||
local buttonnumber = (i - (list.page * modstore.modsperpage))
|
||||
retval = retval .."button[9.6," .. buttony .. ";2,0.5;btn_install_mod_" .. buttonnumber .. ";"
|
||||
|
||||
if modmgr.mod_exists(details.basename) then
|
||||
retval = retval .. "re-Install]"
|
||||
else
|
||||
retval = retval .. "Install]"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
modstore.current_list = list
|
||||
|
||||
return retval
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function modstore.get_details(modid)
|
||||
|
||||
if modstore.details_cache[modid] ~= nil then
|
||||
return modstore.details_cache[modid]
|
||||
end
|
||||
|
||||
local retval = engine.get_modstore_details(tostring(modid))
|
||||
modstore.details_cache[modid] = retval
|
||||
return retval
|
||||
end
|
@ -883,6 +883,15 @@ background[<X>,<Y>;<W>,<H>;<texture name>]
|
||||
^ Position and size units are inventory slots
|
||||
^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px
|
||||
|
||||
pwdfield[<X>,<Y>;<W>,<H>;<name>;<label>]
|
||||
^ Textual password style field; will be sent to server when a button is clicked
|
||||
^ x and y position the field relative to the top left of the menu
|
||||
^ w and h are the size of the field
|
||||
^ fields are a set height, but will be vertically centred on h
|
||||
^ Position and size units are inventory slots
|
||||
^ name is the name of the field as returned in fields to on_receive_fields
|
||||
^ label, if not blank, will be text printed on the top left above the field
|
||||
|
||||
field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]
|
||||
^ Textual field; will be sent to server when a button is clicked
|
||||
^ x and y position the field relative to the top left of the menu
|
||||
@ -910,6 +919,12 @@ label[<X>,<Y>;<label>]
|
||||
^ label is the text on the label
|
||||
^ Position and size units are inventory slots
|
||||
|
||||
vertlabel[<X>,<Y>;<label>]
|
||||
^ Textual label drawn verticaly
|
||||
^ x and y work as per field
|
||||
^ label is the text on the label
|
||||
^ Position and size units are inventory slots
|
||||
|
||||
button[<X>,<Y>;<W>,<H>;<name>;<label>]
|
||||
^ Clickable button. When clicked, fields will be sent.
|
||||
^ x, y and name work as per field
|
||||
@ -922,6 +937,13 @@ image_button[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
|
||||
^ image is the filename of an image
|
||||
^ Position and size units are inventory slots
|
||||
|
||||
image_button[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>;<noclip>;<drawborder>]
|
||||
^ x, y, w, h, and name work as per button
|
||||
^ image is the filename of an image
|
||||
^ Position and size units are inventory slots
|
||||
^ noclip true meand imagebutton doesn't need to be within specified formsize
|
||||
^ drawborder draw button bodrer or not
|
||||
|
||||
item_image_button[<X>,<Y>;<W>,<H>;<item name>;<name>;<label>]
|
||||
^ x, y, w, h, name and label work as per button
|
||||
^ item name is the registered name of an item/node,
|
||||
@ -934,6 +956,42 @@ button_exit[<X>,<Y>;<W>,<H>;<name>;<label>]
|
||||
image_button_exit[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
|
||||
^ When clicked, fields will be sent and the form will quit.
|
||||
|
||||
textlist[<X>,<Y>;<W>,<H>;<name>;<listelem 1>,<listelem 2>,...,<listelem n>]
|
||||
^Scrollabel itemlist showing arbitrary text elements
|
||||
^ x and y position the itemlist relative to the top left of the menu
|
||||
^ w and h are the size of the itemlist
|
||||
^ listelements can be prepended by #colorkey (see colorkeys),
|
||||
^ if you want a listelement to start with # write ##
|
||||
^ name fieldname sent to server on doubleclick value is current selected element
|
||||
|
||||
tabheader[<X>,<Y>;<name>;<caption 1>,<caption 2>;<current_tab>;<transparent>;<draw_border>]
|
||||
^ show a tabHEADER at specific position (ignores formsize)
|
||||
^ x and y position the itemlist relative to the top left of the menu
|
||||
^ name fieldname data is transfered to lua
|
||||
^ caption 1... name shown on top of tab
|
||||
^ current_tab index of selected tab 1...
|
||||
^ transparent (optional) show transparent
|
||||
^ draw_border (optional) draw border
|
||||
|
||||
box[<X>,<Y>;<W>,<H>;<colorkey>]
|
||||
^ simple colored semitransparent box
|
||||
^ x and y position the box relative to the top left of the menu
|
||||
^ w and h are the size of box
|
||||
^ colorkey (see colorkeys)
|
||||
|
||||
Available colorkeys:
|
||||
- YLW yellow
|
||||
- GRN green
|
||||
- LIM lime
|
||||
- ORN orange
|
||||
- RED red
|
||||
- BLU blue
|
||||
- CYN cyan
|
||||
- BLK black
|
||||
- BRN brown
|
||||
- WHT white
|
||||
- GRY grey
|
||||
|
||||
Inventory location:
|
||||
|
||||
- "context": Selected node metadata (deprecated: "current_name")
|
||||
|
163
doc/menu_lua_api.txt
Normal file
163
doc/menu_lua_api.txt
Normal file
@ -0,0 +1,163 @@
|
||||
Minetest Lua Mainmenu API Reference 0.4.6
|
||||
========================================
|
||||
|
||||
Introduction
|
||||
-------------
|
||||
The main menu is defined as a formspec by Lua in builtin/mainmenu.lua
|
||||
Description of formspec language to show your menu is in lua_api.txt
|
||||
|
||||
Callbacks
|
||||
---------
|
||||
engine.buttonhandler(fields): called when a button is pressed.
|
||||
^ fields = {name1 = value1, name2 = value2, ...}
|
||||
engine.event_handler(event)
|
||||
^ event: "MenuQuit", "KeyEnter", "ExitButton" or "EditBoxEnter"
|
||||
|
||||
Gamedata
|
||||
--------
|
||||
The "gamedata" table is read when calling engine.start(). It should contain:
|
||||
{
|
||||
playername = <name>,
|
||||
password = <password>,
|
||||
address = <IP/adress>,
|
||||
port = <port>,
|
||||
selected_world = <index>, -- 0 for client mode
|
||||
singleplayer = <true/false>,
|
||||
}
|
||||
|
||||
Functions
|
||||
---------
|
||||
engine.start()
|
||||
engine.close()
|
||||
|
||||
Filesystem:
|
||||
engine.get_scriptdir()
|
||||
^ returns directory of script
|
||||
engine.get_modpath()
|
||||
^ returns path to global modpath
|
||||
engine.get_modstore_details(modid)
|
||||
^ modid numeric id of mod in modstore
|
||||
^ returns {
|
||||
id = <numeric id of mod in modstore>,
|
||||
title = <human readable title>,
|
||||
basename = <basename for mod>,
|
||||
description = <description of mod>,
|
||||
author = <author of mod>,
|
||||
download_url= <best match download url>,
|
||||
license = <short description of license>,
|
||||
rating = <float value of current rating>
|
||||
}
|
||||
engine.get_modstore_list()
|
||||
^ returns {
|
||||
[1] = {
|
||||
id = <numeric id of mod in modstore>,
|
||||
title = <human readable title>,
|
||||
basename = <basename for mod>
|
||||
}
|
||||
}
|
||||
engine.get_gamepath()
|
||||
^ returns path to global gamepath
|
||||
engine.get_dirlist(path,onlydirs)
|
||||
^ path to get subdirs from
|
||||
^ onlydirs should result contain only dirs?
|
||||
^ returns list of folders within path
|
||||
engine.create_dir(absolute_path)
|
||||
^ absolute_path to directory to create (needs to be absolute)
|
||||
^ returns true/false
|
||||
engine.delete_dir(absolute_path)
|
||||
^ absolute_path to directory to delete (needs to be absolute)
|
||||
^ returns true/false
|
||||
engine.copy_dir(source,destination,keep_soure)
|
||||
^ source folder
|
||||
^ destination folder
|
||||
^ keep_source DEFAULT true --> if set to false source is deleted after copying
|
||||
^ returns true/false
|
||||
engine.extract_zip(zipfile,destination) [unzip within path required]
|
||||
^ zipfile to extract
|
||||
^ destination folder to extract to
|
||||
^ returns true/false
|
||||
engine.download_file(url,target)
|
||||
^ url to download
|
||||
^ target to store to
|
||||
^ returns true/false
|
||||
engine.get_version()
|
||||
^ returns current minetest version
|
||||
|
||||
GUI:
|
||||
engine.update_formspec(formspec)
|
||||
- engine.set_background(type, texturepath)
|
||||
^ type: "background", "overlay", "header" or "footer"
|
||||
engine.set_clouds(<true/false>)
|
||||
engine.set_topleft_text(text)
|
||||
|
||||
Games:
|
||||
engine.get_game(index)
|
||||
^ returns {
|
||||
id = <id>,
|
||||
path = <full path to game>,
|
||||
gamemods_path = <path>,
|
||||
name = <name of game>,
|
||||
menuicon_path = <full path to menuicon>,
|
||||
DEPRECATED:
|
||||
addon_mods_paths = {[1] = <path>,},
|
||||
}
|
||||
engine.get_games() -> table of all games in upper format
|
||||
|
||||
Favorites:
|
||||
engine.get_favorites(location) -> list of favorites
|
||||
^ location: "local" or "online"
|
||||
^ returns {
|
||||
[1] = {
|
||||
clients = <number of clients/nil>,
|
||||
clients_max = <maximum number of clients/nil>,
|
||||
version = <server version/nil>,
|
||||
password = <true/nil>,
|
||||
creative = <true/nil>,
|
||||
damage = <true/nil>,
|
||||
pvp = <true/nil>,
|
||||
description = <server description/nil>,
|
||||
name = <server name/nil>,
|
||||
address = <address of server/nil>,
|
||||
port = <port>
|
||||
},
|
||||
}
|
||||
engine.delete_favorite(id, location) -> success
|
||||
|
||||
Settings:
|
||||
engine.setting_set(name, value)
|
||||
engine.setting_get(name) -> string or nil
|
||||
engine.setting_setbool(name, value)
|
||||
engine.setting_getbool(name) -> bool or nil
|
||||
|
||||
Worlds:
|
||||
engine.get_worlds() -> list of worlds
|
||||
^ returns {
|
||||
[1] = {
|
||||
path = <full path to world>,
|
||||
name = <name of world>,
|
||||
gameid = <gameid of world>,
|
||||
},
|
||||
}
|
||||
engine.create_world(worldname, gameid)
|
||||
engine.delete_world(index)
|
||||
|
||||
|
||||
UI:
|
||||
engine.get_textlist_index(textlistname) -> index
|
||||
engine.show_keys_menu()
|
||||
engine.file_open_dialog(formname,caption)
|
||||
^ shows a file open dialog
|
||||
^ formname is base name of dialog response returned in fields
|
||||
^ -if dialog was accepted "_accepted"
|
||||
^^ will be added to fieldname containing the path
|
||||
^ -if dialog was canceled "_cancelled"
|
||||
^ will be added to fieldname value is set to formname itself
|
||||
^ returns nil or selected file/folder
|
||||
|
||||
Helpers:
|
||||
dump(obj, dumped={})
|
||||
^ Return object serialized as a string
|
||||
string:split(separator)
|
||||
^ eg. string:split("a,b", ",") == {"a","b"}
|
||||
string:trim()
|
||||
^ eg. string.trim("\n \t\tfoo bar\t ") == "foo bar"
|
@ -373,3 +373,9 @@
|
||||
# Enable/disable running an IPv6 server. An IPv6 server may be restricted
|
||||
# to IPv6 clients, depending on system configuration.
|
||||
#ipv6_server = false
|
||||
|
||||
#main_menu_game_mgr = 0
|
||||
#main_menu_mod_mgr = 0
|
||||
#modstore_download_url = http://forum.minetest.net/media/
|
||||
#modstore_listmods_url = http://forum.minetest.net/mmdb/mods/
|
||||
#modstore_details_url = http://forum.minetest.net/mmdb/mod/*/
|
||||
|
@ -271,6 +271,7 @@ set(common_SRCS
|
||||
staticobject.cpp
|
||||
serverlist.cpp
|
||||
pathfinder.cpp
|
||||
convert_json.cpp
|
||||
${SCRIPT_SRCS}
|
||||
${UTIL_SRCS}
|
||||
)
|
||||
@ -313,7 +314,6 @@ set(minetest_SRCS
|
||||
clientobject.cpp
|
||||
chat.cpp
|
||||
hud.cpp
|
||||
guiMainMenu.cpp
|
||||
guiKeyChangeMenu.cpp
|
||||
guiMessageMenu.cpp
|
||||
guiTextInputMenu.cpp
|
||||
@ -323,15 +323,16 @@ set(minetest_SRCS
|
||||
guiVolumeChange.cpp
|
||||
guiDeathScreen.cpp
|
||||
guiChatConsole.cpp
|
||||
guiCreateWorld.cpp
|
||||
guiConfigureWorld.cpp
|
||||
guiConfirmMenu.cpp
|
||||
client.cpp
|
||||
filecache.cpp
|
||||
tile.cpp
|
||||
shader.cpp
|
||||
game.cpp
|
||||
main.cpp
|
||||
guiEngine.cpp
|
||||
guiLuaApi.cpp
|
||||
guiFileSelectMenu.cpp
|
||||
convert_json.cpp
|
||||
)
|
||||
|
||||
if(USE_FREETYPE)
|
||||
@ -488,7 +489,7 @@ else()
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG ${RELEASE_WARNING_FLAGS} ${WARNING_FLAGS} ${OTHER_FLAGS} -O3 -ffast-math -Wall -fomit-frame-pointer -pipe -funroll-loops")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -O1 -Wall ${WARNING_FLAGS} ${OTHER_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall ${WARNING_FLAGS} ${OTHER_FLAGS}")
|
||||
|
||||
if(USE_GPROF)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg")
|
||||
|
367
src/convert_json.cpp
Normal file
367
src/convert_json.cpp
Normal file
@ -0,0 +1,367 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "convert_json.h"
|
||||
#include "mods.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
|
||||
#if USE_CURL
|
||||
#include <curl/curl.h>
|
||||
|
||||
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Json::Value fetchJsonValue(const std::string url,
|
||||
struct curl_slist *chunk) {
|
||||
#if USE_CURL
|
||||
std::string liststring;
|
||||
CURL *curl;
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl)
|
||||
{
|
||||
CURLcode res;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &liststring);
|
||||
|
||||
if (chunk != 0)
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
|
||||
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK)
|
||||
errorstream<<"Jsonreader: "<< url <<" not found (internet connection?)"<<std::endl;
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
std::istringstream stream(liststring);
|
||||
if (!liststring.size()) {
|
||||
return Json::Value();
|
||||
}
|
||||
|
||||
if (!reader.parse( stream, root ) )
|
||||
{
|
||||
errorstream << "URL: " << url << std::endl;
|
||||
errorstream << "Failed to parse json data " << reader.getFormattedErrorMessages();
|
||||
errorstream << "data: \"" << liststring << "\"" << std::endl;
|
||||
return Json::Value();
|
||||
}
|
||||
|
||||
if (root.isArray()) {
|
||||
return root;
|
||||
}
|
||||
if ((root["list"].isArray())) {
|
||||
return root["list"];
|
||||
}
|
||||
else {
|
||||
return root;
|
||||
}
|
||||
#endif
|
||||
return Json::Value();
|
||||
}
|
||||
|
||||
std::vector<ModStoreMod> readModStoreList(Json::Value& modlist) {
|
||||
std::vector<ModStoreMod> retval;
|
||||
|
||||
if (modlist.isArray()) {
|
||||
for (unsigned int i = 0; i < modlist.size(); i++)
|
||||
{
|
||||
ModStoreMod toadd;
|
||||
toadd.valid = true;
|
||||
|
||||
//id
|
||||
if (modlist[i]["id"].asString().size()) {
|
||||
const char* id_raw = modlist[i]["id"].asString().c_str();
|
||||
char* endptr = 0;
|
||||
int numbervalue = strtol(id_raw,&endptr,10);
|
||||
|
||||
if ((*id_raw != 0) && (*endptr == 0)) {
|
||||
toadd.id = numbervalue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
toadd.valid = false;
|
||||
}
|
||||
|
||||
//title
|
||||
if (modlist[i]["title"].asString().size()) {
|
||||
toadd.title = modlist[i]["title"].asString();
|
||||
}
|
||||
else {
|
||||
toadd.valid = false;
|
||||
}
|
||||
|
||||
//basename
|
||||
if (modlist[i]["basename"].asString().size()) {
|
||||
toadd.basename = modlist[i]["basename"].asString();
|
||||
}
|
||||
else {
|
||||
toadd.valid = false;
|
||||
}
|
||||
|
||||
//author
|
||||
|
||||
//rating
|
||||
|
||||
//version
|
||||
|
||||
if (toadd.valid) {
|
||||
retval.push_back(toadd);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
ModStoreModDetails readModStoreModDetails(Json::Value& details) {
|
||||
|
||||
ModStoreModDetails retval;
|
||||
|
||||
retval.valid = true;
|
||||
|
||||
//version set
|
||||
if (details["version_set"].isArray()) {
|
||||
for (unsigned int i = 0; i < details["version_set"].size(); i++)
|
||||
{
|
||||
ModStoreVersionEntry toadd;
|
||||
|
||||
if (details["version_set"][i]["id"].asString().size()) {
|
||||
const char* id_raw = details["version_set"][i]["id"].asString().c_str();
|
||||
char* endptr = 0;
|
||||
int numbervalue = strtol(id_raw,&endptr,10);
|
||||
|
||||
if ((*id_raw != 0) && (*endptr == 0)) {
|
||||
toadd.id = numbervalue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
|
||||
//date
|
||||
if (details["version_set"][i]["date"].asString().size()) {
|
||||
toadd.date = details["version_set"][i]["date"].asString();
|
||||
}
|
||||
|
||||
//file
|
||||
if (details["version_set"][i]["file"].asString().size()) {
|
||||
toadd.file = details["version_set"][i]["file"].asString();
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
|
||||
//approved
|
||||
|
||||
//mtversion
|
||||
|
||||
if( retval.valid ) {
|
||||
retval.versions.push_back(toadd);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (retval.versions.size() < 1) {
|
||||
retval.valid = false;
|
||||
}
|
||||
|
||||
//categories
|
||||
if (details["categories"].isObject()) {
|
||||
for (unsigned int i = 0; i < details["categories"].size(); i++) {
|
||||
ModStoreCategoryInfo toadd;
|
||||
|
||||
if (details["categories"][i]["id"].asString().size()) {
|
||||
|
||||
const char* id_raw = details["categories"][i]["id"].asString().c_str();
|
||||
char* endptr = 0;
|
||||
int numbervalue = strtol(id_raw,&endptr,10);
|
||||
|
||||
if ((*id_raw != 0) && (*endptr == 0)) {
|
||||
toadd.id = numbervalue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
if (details["categories"][i]["title"].asString().size()) {
|
||||
toadd.name = details["categories"][i]["title"].asString();
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
|
||||
if( retval.valid ) {
|
||||
retval.categories.push_back(toadd);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//author
|
||||
if (details["author"].isObject()) {
|
||||
if (details["author"]["id"].asString().size()) {
|
||||
|
||||
const char* id_raw = details["author"]["id"].asString().c_str();
|
||||
char* endptr = 0;
|
||||
int numbervalue = strtol(id_raw,&endptr,10);
|
||||
|
||||
if ((*id_raw != 0) && (*endptr == 0)) {
|
||||
retval.author.id = numbervalue;
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
|
||||
if (details["author"]["username"].asString().size()) {
|
||||
retval.author.username = details["author"]["username"].asString();
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
|
||||
//license
|
||||
if (details["license"].isObject()) {
|
||||
if (details["license"]["id"].asString().size()) {
|
||||
|
||||
const char* id_raw = details["license"]["id"].asString().c_str();
|
||||
char* endptr = 0;
|
||||
int numbervalue = strtol(id_raw,&endptr,10);
|
||||
|
||||
if ((*id_raw != 0) && (*endptr == 0)) {
|
||||
retval.license.id = numbervalue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
|
||||
if (details["license"]["short"].asString().size()) {
|
||||
retval.license.shortinfo = details["license"]["short"].asString();
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
|
||||
if (details["license"]["link"].asString().size()) {
|
||||
retval.license.url = details["license"]["link"].asString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//id
|
||||
if (details["id"].asString().size()) {
|
||||
|
||||
const char* id_raw = details["id"].asString().c_str();
|
||||
char* endptr = 0;
|
||||
int numbervalue = strtol(id_raw,&endptr,10);
|
||||
|
||||
if ((*id_raw != 0) && (*endptr == 0)) {
|
||||
retval.id = numbervalue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
|
||||
//title
|
||||
if (details["title"].asString().size()) {
|
||||
retval.title = details["title"].asString();
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
|
||||
//basename
|
||||
if (details["basename"].asString().size()) {
|
||||
retval.basename = details["basename"].asString();
|
||||
}
|
||||
else {
|
||||
retval.valid = false;
|
||||
}
|
||||
|
||||
//description
|
||||
if (details["desc"].asString().size()) {
|
||||
retval.description = details["desc"].asString();
|
||||
}
|
||||
|
||||
//repository
|
||||
if (details["replink"].asString().size()) {
|
||||
retval.repository = details["replink"].asString();
|
||||
}
|
||||
|
||||
//value
|
||||
if (details["rating"].asString().size()) {
|
||||
|
||||
const char* id_raw = details["rating"].asString().c_str();
|
||||
char* endptr = 0;
|
||||
float numbervalue = strtof(id_raw,&endptr);
|
||||
|
||||
if ((*id_raw != 0) && (*endptr == 0)) {
|
||||
retval.rating = numbervalue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
retval.rating = 0.0;
|
||||
}
|
||||
|
||||
//depends
|
||||
if (details["depends"].isArray()) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
//softdepends
|
||||
if (details["softdep"].isArray()) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
//screenshot url
|
||||
if (details["screenshot_url"].asString().size()) {
|
||||
retval.screenshot_url = details["screenshot_url"].asString();
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
@ -17,40 +17,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef GUICONFIRMMENU_HEADER
|
||||
#define GUICONFIRMMENU_HEADER
|
||||
#ifndef __CONVERT_JSON_H__
|
||||
#define __CONVERT_JSON_H__
|
||||
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
#include <string>
|
||||
#include "json/json.h"
|
||||
|
||||
struct ConfirmDest
|
||||
{
|
||||
virtual void answer(bool answer) = 0;
|
||||
virtual ~ConfirmDest() {};
|
||||
};
|
||||
struct ModStoreMod;
|
||||
struct ModStoreModDetails;
|
||||
|
||||
class GUIConfirmMenu : public GUIModalMenu
|
||||
{
|
||||
public:
|
||||
GUIConfirmMenu(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
ConfirmDest *dest,
|
||||
std::wstring message_text);
|
||||
~GUIConfirmMenu();
|
||||
|
||||
void removeChildren();
|
||||
// Remove and re-add (or reposition) stuff
|
||||
void regenerateGui(v2u32 screensize);
|
||||
void drawMenu();
|
||||
void acceptInput(bool answer);
|
||||
bool OnEvent(const SEvent& event);
|
||||
|
||||
private:
|
||||
ConfirmDest *m_dest;
|
||||
std::wstring m_message_text;
|
||||
};
|
||||
std::vector<ModStoreMod> readModStoreList(Json::Value& modlist);
|
||||
ModStoreModDetails readModStoreModDetails(Json::Value& details);
|
||||
|
||||
Json::Value fetchJsonValue(const std::string url,
|
||||
struct curl_slist *chunk);
|
||||
|
||||
#endif
|
||||
|
@ -256,6 +256,11 @@ void set_default_settings(Settings *settings)
|
||||
// IPv6
|
||||
settings->setDefault("enable_ipv6", "true");
|
||||
settings->setDefault("ipv6_server", "false");
|
||||
|
||||
settings->setDefault("modstore_download_url", "http://forum.minetest.net/media/");
|
||||
settings->setDefault("modstore_listmods_url", "http://forum.minetest.net/mmdb/mods/");
|
||||
settings->setDefault("modstore_details_url", "http://forum.minetest.net/mmdb/mod/*/");
|
||||
|
||||
}
|
||||
|
||||
void override_default_settings(Settings *settings, Settings *from)
|
||||
|
267
src/filesys.cpp
267
src/filesys.cpp
@ -20,7 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "filesys.h"
|
||||
#include "strfnd.h"
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "log.h"
|
||||
|
||||
namespace fs
|
||||
@ -30,11 +32,9 @@ namespace fs
|
||||
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
#include <tchar.h>
|
||||
#include <wchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define BUFSIZE MAX_PATH
|
||||
|
||||
@ -145,6 +145,11 @@ bool IsDir(std::string path)
|
||||
(attr & FILE_ATTRIBUTE_DIRECTORY));
|
||||
}
|
||||
|
||||
bool IsDirDelimiter(char c)
|
||||
{
|
||||
return c == '/' || c == '\\';
|
||||
}
|
||||
|
||||
bool RecursiveDelete(std::string path)
|
||||
{
|
||||
infostream<<"Recursively deleting \""<<path<<"\""<<std::endl;
|
||||
@ -207,11 +212,26 @@ bool DeleteSingleFileOrEmptyDirectory(std::string path)
|
||||
}
|
||||
}
|
||||
|
||||
std::string TempPath()
|
||||
{
|
||||
DWORD bufsize = GetTempPath(0, "");
|
||||
if(bufsize == 0){
|
||||
errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
|
||||
return "";
|
||||
}
|
||||
std::vector<char> buf(bufsize);
|
||||
DWORD len = GetTempPath(bufsize, &buf[0]);
|
||||
if(len == 0 || len > bufsize){
|
||||
errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
|
||||
return "";
|
||||
}
|
||||
return std::string(buf.begin(), buf.begin() + len);
|
||||
}
|
||||
|
||||
#else // POSIX
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
@ -301,6 +321,11 @@ bool IsDir(std::string path)
|
||||
return ((statbuf.st_mode & S_IFDIR) == S_IFDIR);
|
||||
}
|
||||
|
||||
bool IsDirDelimiter(char c)
|
||||
{
|
||||
return c == '/';
|
||||
}
|
||||
|
||||
bool RecursiveDelete(std::string path)
|
||||
{
|
||||
/*
|
||||
@ -364,6 +389,20 @@ bool DeleteSingleFileOrEmptyDirectory(std::string path)
|
||||
}
|
||||
}
|
||||
|
||||
std::string TempPath()
|
||||
{
|
||||
/*
|
||||
Should the environment variables TMPDIR, TMP and TEMP
|
||||
and the macro P_tmpdir (if defined by stdio.h) be checked
|
||||
before falling back on /tmp?
|
||||
|
||||
Probably not, because this function is intended to be
|
||||
compatible with lua's os.tmpname which under the default
|
||||
configuration hardcodes mkstemp("/tmp/lua_XXXXXX").
|
||||
*/
|
||||
return std::string(DIR_DELIM) + "tmp";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void GetRecursiveSubPaths(std::string path, std::vector<std::string> &dst)
|
||||
@ -414,16 +453,14 @@ bool RecursiveDeleteContent(std::string path)
|
||||
bool CreateAllDirs(std::string path)
|
||||
{
|
||||
|
||||
size_t pos;
|
||||
std::vector<std::string> tocreate;
|
||||
std::string basepath = path;
|
||||
while(!PathExists(basepath))
|
||||
{
|
||||
tocreate.push_back(basepath);
|
||||
pos = basepath.rfind(DIR_DELIM_C);
|
||||
if(pos == std::string::npos)
|
||||
basepath = RemoveLastPathComponent(basepath);
|
||||
if(basepath.empty())
|
||||
break;
|
||||
basepath = basepath.substr(0,pos);
|
||||
}
|
||||
for(int i=tocreate.size()-1;i>=0;i--)
|
||||
if(!CreateDir(tocreate[i]))
|
||||
@ -431,5 +468,221 @@ bool CreateAllDirs(std::string path)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CopyFileContents(std::string source, std::string target)
|
||||
{
|
||||
FILE *sourcefile = fopen(source.c_str(), "rb");
|
||||
if(sourcefile == NULL){
|
||||
errorstream<<source<<": can't open for reading: "
|
||||
<<strerror(errno)<<std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE *targetfile = fopen(target.c_str(), "wb");
|
||||
if(targetfile == NULL){
|
||||
errorstream<<target<<": can't open for writing: "
|
||||
<<strerror(errno)<<std::endl;
|
||||
fclose(sourcefile);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t total = 0;
|
||||
bool retval = true;
|
||||
bool done = false;
|
||||
char readbuffer[BUFSIZ];
|
||||
while(!done){
|
||||
size_t readbytes = fread(readbuffer, 1,
|
||||
sizeof(readbuffer), sourcefile);
|
||||
total += readbytes;
|
||||
if(ferror(sourcefile)){
|
||||
errorstream<<source<<": IO error: "
|
||||
<<strerror(errno)<<std::endl;
|
||||
retval = false;
|
||||
done = true;
|
||||
}
|
||||
if(readbytes > 0){
|
||||
fwrite(readbuffer, 1, readbytes, targetfile);
|
||||
}
|
||||
if(feof(sourcefile) || ferror(sourcefile)){
|
||||
// flush destination file to catch write errors
|
||||
// (e.g. disk full)
|
||||
fflush(targetfile);
|
||||
done = true;
|
||||
}
|
||||
if(ferror(targetfile)){
|
||||
errorstream<<target<<": IO error: "
|
||||
<<strerror(errno)<<std::endl;
|
||||
retval = false;
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
infostream<<"copied "<<total<<" bytes from "
|
||||
<<source<<" to "<<target<<std::endl;
|
||||
fclose(sourcefile);
|
||||
fclose(targetfile);
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool CopyDir(std::string source, std::string target)
|
||||
{
|
||||
if(PathExists(source)){
|
||||
if(!PathExists(target)){
|
||||
fs::CreateAllDirs(target);
|
||||
}
|
||||
bool retval = true;
|
||||
std::vector<DirListNode> content = fs::GetDirListing(source);
|
||||
|
||||
for(unsigned int i=0; i < content.size(); i++){
|
||||
std::string sourcechild = source + DIR_DELIM + content[i].name;
|
||||
std::string targetchild = target + DIR_DELIM + content[i].name;
|
||||
if(content[i].dir){
|
||||
if(!fs::CopyDir(sourcechild, targetchild)){
|
||||
retval = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!fs::CopyFileContents(sourcechild, targetchild)){
|
||||
retval = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PathStartsWith(std::string path, std::string prefix)
|
||||
{
|
||||
size_t pathsize = path.size();
|
||||
size_t pathpos = 0;
|
||||
size_t prefixsize = prefix.size();
|
||||
size_t prefixpos = 0;
|
||||
for(;;){
|
||||
bool delim1 = pathpos == pathsize
|
||||
|| IsDirDelimiter(path[pathpos]);
|
||||
bool delim2 = prefixpos == prefixsize
|
||||
|| IsDirDelimiter(prefix[prefixpos]);
|
||||
|
||||
if(delim1 != delim2)
|
||||
return false;
|
||||
|
||||
if(delim1){
|
||||
while(pathpos < pathsize &&
|
||||
IsDirDelimiter(path[pathpos]))
|
||||
++pathpos;
|
||||
while(prefixpos < prefixsize &&
|
||||
IsDirDelimiter(prefix[prefixpos]))
|
||||
++prefixpos;
|
||||
if(prefixpos == prefixsize)
|
||||
return true;
|
||||
if(pathpos == pathsize)
|
||||
return false;
|
||||
}
|
||||
else{
|
||||
size_t len = 0;
|
||||
do{
|
||||
char pathchar = path[pathpos+len];
|
||||
char prefixchar = prefix[prefixpos+len];
|
||||
if(FILESYS_CASE_INSENSITIVE){
|
||||
pathchar = tolower(pathchar);
|
||||
prefixchar = tolower(prefixchar);
|
||||
}
|
||||
if(pathchar != prefixchar)
|
||||
return false;
|
||||
++len;
|
||||
} while(pathpos+len < pathsize
|
||||
&& !IsDirDelimiter(path[pathpos+len])
|
||||
&& prefixpos+len < prefixsize
|
||||
&& !IsDirDelimiter(
|
||||
prefix[prefixsize+len]));
|
||||
pathpos += len;
|
||||
prefixpos += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string RemoveLastPathComponent(std::string path,
|
||||
std::string *removed, int count)
|
||||
{
|
||||
if(removed)
|
||||
*removed = "";
|
||||
|
||||
size_t remaining = path.size();
|
||||
|
||||
for(int i = 0; i < count; ++i){
|
||||
// strip a dir delimiter
|
||||
while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
|
||||
remaining--;
|
||||
// strip a path component
|
||||
size_t component_end = remaining;
|
||||
while(remaining != 0 && !IsDirDelimiter(path[remaining-1]))
|
||||
remaining--;
|
||||
size_t component_start = remaining;
|
||||
// strip a dir delimiter
|
||||
while(remaining != 0 && IsDirDelimiter(path[remaining-1]))
|
||||
remaining--;
|
||||
if(removed){
|
||||
std::string component = path.substr(component_start,
|
||||
component_end - component_start);
|
||||
if(i)
|
||||
*removed = component + DIR_DELIM + *removed;
|
||||
else
|
||||
*removed = component;
|
||||
}
|
||||
}
|
||||
return path.substr(0, remaining);
|
||||
}
|
||||
|
||||
std::string RemoveRelativePathComponents(std::string path)
|
||||
{
|
||||
size_t pos = path.size();
|
||||
size_t dotdot_count = 0;
|
||||
while(pos != 0){
|
||||
size_t component_with_delim_end = pos;
|
||||
// skip a dir delimiter
|
||||
while(pos != 0 && IsDirDelimiter(path[pos-1]))
|
||||
pos--;
|
||||
// strip a path component
|
||||
size_t component_end = pos;
|
||||
while(pos != 0 && !IsDirDelimiter(path[pos-1]))
|
||||
pos--;
|
||||
size_t component_start = pos;
|
||||
|
||||
std::string component = path.substr(component_start,
|
||||
component_end - component_start);
|
||||
bool remove_this_component = false;
|
||||
if(component == "."){
|
||||
remove_this_component = true;
|
||||
}
|
||||
else if(component == ".."){
|
||||
remove_this_component = true;
|
||||
dotdot_count += 1;
|
||||
}
|
||||
else if(dotdot_count != 0){
|
||||
remove_this_component = true;
|
||||
dotdot_count -= 1;
|
||||
}
|
||||
|
||||
if(remove_this_component){
|
||||
while(pos != 0 && IsDirDelimiter(path[pos-1]))
|
||||
pos--;
|
||||
path = path.substr(0, pos) + DIR_DELIM +
|
||||
path.substr(component_with_delim_end,
|
||||
std::string::npos);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
if(dotdot_count > 0)
|
||||
return "";
|
||||
|
||||
// remove trailing dir delimiters
|
||||
pos = path.size();
|
||||
while(pos != 0 && IsDirDelimiter(path[pos-1]))
|
||||
pos--;
|
||||
return path.substr(0, pos);
|
||||
}
|
||||
|
||||
} // namespace fs
|
||||
|
||||
|
@ -26,10 +26,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
#ifdef _WIN32 // WINDOWS
|
||||
#define DIR_DELIM "\\"
|
||||
#define DIR_DELIM_C '\\'
|
||||
#define FILESYS_CASE_INSENSITIVE 1
|
||||
#else // POSIX
|
||||
#define DIR_DELIM "/"
|
||||
#define DIR_DELIM_C '/'
|
||||
#define FILESYS_CASE_INSENSITIVE 0
|
||||
#endif
|
||||
|
||||
namespace fs
|
||||
@ -49,12 +49,17 @@ bool PathExists(std::string path);
|
||||
|
||||
bool IsDir(std::string path);
|
||||
|
||||
bool IsDirDelimiter(char c);
|
||||
|
||||
// Only pass full paths to this one. True on success.
|
||||
// NOTE: The WIN32 version returns always true.
|
||||
bool RecursiveDelete(std::string path);
|
||||
|
||||
bool DeleteSingleFileOrEmptyDirectory(std::string path);
|
||||
|
||||
// Returns path to temp directory, can return "" on error
|
||||
std::string TempPath();
|
||||
|
||||
/* Multiplatform */
|
||||
|
||||
// The path itself not included
|
||||
@ -69,6 +74,30 @@ bool RecursiveDeleteContent(std::string path);
|
||||
// Create all directories on the given path that don't already exist.
|
||||
bool CreateAllDirs(std::string path);
|
||||
|
||||
// Copy a regular file
|
||||
bool CopyFileContents(std::string source, std::string target);
|
||||
|
||||
// Copy directory and all subdirectories
|
||||
// Omits files and subdirectories that start with a period
|
||||
bool CopyDir(std::string source, std::string target);
|
||||
|
||||
// Check if one path is prefix of another
|
||||
// For example, "/tmp" is a prefix of "/tmp" and "/tmp/file" but not "/tmp2"
|
||||
// Ignores case differences and '/' vs. '\\' on Windows
|
||||
bool PathStartsWith(std::string path, std::string prefix);
|
||||
|
||||
// Remove last path component and the dir delimiter before and/or after it,
|
||||
// returns "" if there is only one path component.
|
||||
// removed: If non-NULL, receives the removed component(s).
|
||||
// count: Number of components to remove
|
||||
std::string RemoveLastPathComponent(std::string path,
|
||||
std::string *removed = NULL, int count = 1);
|
||||
|
||||
// Remove "." and ".." path components and for every ".." removed, remove
|
||||
// the last normal path component before it. Unlike AbsolutePath,
|
||||
// this does not resolve symlinks and check for existence of directories.
|
||||
std::string RemoveRelativePathComponents(std::string path);
|
||||
|
||||
}//fs
|
||||
|
||||
#endif
|
||||
|
27
src/game.cpp
27
src/game.cpp
@ -208,33 +208,6 @@ public:
|
||||
Client *m_client;
|
||||
};
|
||||
|
||||
class FormspecFormSource: public IFormSource
|
||||
{
|
||||
public:
|
||||
FormspecFormSource(std::string formspec,FormspecFormSource** game_formspec)
|
||||
{
|
||||
m_formspec = formspec;
|
||||
m_game_formspec = game_formspec;
|
||||
}
|
||||
|
||||
~FormspecFormSource()
|
||||
{
|
||||
*m_game_formspec = 0;
|
||||
}
|
||||
|
||||
void setForm(std::string formspec) {
|
||||
m_formspec = formspec;
|
||||
}
|
||||
|
||||
std::string getForm()
|
||||
{
|
||||
return m_formspec;
|
||||
}
|
||||
|
||||
std::string m_formspec;
|
||||
FormspecFormSource** m_game_formspec;
|
||||
};
|
||||
|
||||
/*
|
||||
Check if a node is pointable
|
||||
*/
|
||||
|
@ -1,693 +0,0 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "guiConfigureWorld.h"
|
||||
#include "guiMessageMenu.h"
|
||||
#include <IGUIButton.h>
|
||||
#include <IGUICheckBox.h>
|
||||
#include <IGUIListBox.h>
|
||||
#include <IGUIStaticText.h>
|
||||
#include <IGUITreeView.h>
|
||||
#include "gettext.h"
|
||||
#include "util/string.h"
|
||||
#include "settings.h"
|
||||
#include "filesys.h"
|
||||
|
||||
enum
|
||||
{
|
||||
GUI_ID_MOD_TREEVIEW = 101,
|
||||
GUI_ID_ENABLED_CHECKBOX,
|
||||
GUI_ID_ENABLEALL,
|
||||
GUI_ID_DISABLEALL,
|
||||
GUI_ID_DEPENDS_LISTBOX,
|
||||
GUI_ID_RDEPENDS_LISTBOX,
|
||||
GUI_ID_CANCEL,
|
||||
GUI_ID_SAVE
|
||||
};
|
||||
|
||||
#define QUESTIONMARK_STR L"?"
|
||||
#define CHECKMARK_STR L"\411"
|
||||
#define CROSS_STR L"\403"
|
||||
|
||||
GUIConfigureWorld::GUIConfigureWorld(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr, WorldSpec wspec):
|
||||
GUIModalMenu(env, parent, id, menumgr),
|
||||
m_wspec(wspec),
|
||||
m_gspec(findWorldSubgame(m_wspec.path)),
|
||||
m_menumgr(menumgr)
|
||||
{
|
||||
//will be initialized in regenerateGUI()
|
||||
m_treeview=NULL;
|
||||
|
||||
// game mods
|
||||
m_gamemods = flattenModTree(getModsInPath(m_gspec.gamemods_path));
|
||||
|
||||
// world mods
|
||||
std::string worldmods_path = wspec.path + DIR_DELIM + "worldmods";
|
||||
m_worldmods = flattenModTree(getModsInPath(worldmods_path));
|
||||
|
||||
// fill m_addontree with add-on mods
|
||||
std::set<std::string> paths = m_gspec.addon_mods_paths;
|
||||
for(std::set<std::string>::iterator it=paths.begin();
|
||||
it != paths.end(); ++it)
|
||||
{
|
||||
std::map<std::string,ModSpec> mods = getModsInPath(*it);
|
||||
m_addontree.insert(mods.begin(), mods.end());
|
||||
}
|
||||
|
||||
// expand modpacks
|
||||
m_addonmods = flattenModTree(m_addontree);
|
||||
|
||||
// collect reverse dependencies
|
||||
for(std::map<std::string, ModSpec>::iterator it = m_addonmods.begin();
|
||||
it != m_addonmods.end(); ++it)
|
||||
{
|
||||
std::string modname = (*it).first;
|
||||
ModSpec mod = (*it).second;
|
||||
for(std::set<std::string>::iterator dep_it = mod.depends.begin();
|
||||
dep_it != mod.depends.end(); ++dep_it)
|
||||
{
|
||||
m_reverse_depends.insert(std::make_pair((*dep_it),modname));
|
||||
}
|
||||
}
|
||||
|
||||
m_settings.readConfigFile((m_wspec.path + DIR_DELIM + "world.mt").c_str());
|
||||
std::vector<std::string> names = m_settings.getNames();
|
||||
|
||||
// mod_names contains the names of mods mentioned in the world.mt file
|
||||
std::set<std::string> mod_names;
|
||||
for(std::vector<std::string>::iterator it = names.begin();
|
||||
it != names.end(); ++it)
|
||||
{
|
||||
std::string name = *it;
|
||||
if (name.compare(0,9,"load_mod_")==0)
|
||||
mod_names.insert(name.substr(9));
|
||||
}
|
||||
|
||||
// find new mods (installed but not mentioned in world.mt)
|
||||
for(std::map<std::string, ModSpec>::iterator it = m_addonmods.begin();
|
||||
it != m_addonmods.end(); ++it)
|
||||
{
|
||||
std::string modname = (*it).first;
|
||||
ModSpec mod = (*it).second;
|
||||
// a mod is new if it is not a modpack, and does not occur in
|
||||
// mod_names
|
||||
if(!mod.is_modpack &&
|
||||
mod_names.count(modname) == 0)
|
||||
m_settings.setBool("load_mod_"+modname, false);
|
||||
}
|
||||
// find missing mods (mentioned in world.mt, but not installed)
|
||||
for(std::set<std::string>::iterator it = mod_names.begin();
|
||||
it != mod_names.end(); ++it)
|
||||
{
|
||||
std::string modname = *it;
|
||||
if(m_addonmods.count(modname) == 0)
|
||||
m_settings.remove("load_mod_"+modname);
|
||||
}
|
||||
std::string worldmtfile = m_wspec.path+DIR_DELIM+"world.mt";
|
||||
m_settings.updateConfigFile(worldmtfile.c_str());
|
||||
}
|
||||
|
||||
void GUIConfigureWorld::drawMenu()
|
||||
{
|
||||
gui::IGUISkin* skin = Environment->getSkin();
|
||||
if (!skin)
|
||||
return;
|
||||
video::IVideoDriver* driver = Environment->getVideoDriver();
|
||||
|
||||
video::SColor bgcolor(140,0,0,0);
|
||||
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
|
||||
|
||||
gui::IGUIElement::draw();
|
||||
}
|
||||
|
||||
|
||||
void GUIConfigureWorld::regenerateGui(v2u32 screensize)
|
||||
{
|
||||
|
||||
/*
|
||||
Remove stuff
|
||||
*/
|
||||
removeChildren();
|
||||
|
||||
/*
|
||||
Calculate new sizes and positions
|
||||
*/
|
||||
core::rect<s32> rect(
|
||||
screensize.X/2 - 580/2,
|
||||
screensize.Y/2 - 300/2,
|
||||
screensize.X/2 + 580/2,
|
||||
screensize.Y/2 + 300/2
|
||||
);
|
||||
|
||||
DesiredRect = rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
|
||||
v2s32 topleft = v2s32(10, 10);
|
||||
|
||||
/*
|
||||
Add stuff
|
||||
*/
|
||||
changeCtype("");
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 200, 20);
|
||||
rect += topleft;
|
||||
//proper text is set below, when a mod is selected
|
||||
m_modname_text = Environment->addStaticText(L"Mod: N/A", rect, false,
|
||||
false, this, -1);
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 200, 20);
|
||||
rect += v2s32(0, 25) + topleft;
|
||||
wchar_t* text = wgettext("enabled");
|
||||
m_enabled_checkbox =
|
||||
Environment->addCheckBox(false, rect, this, GUI_ID_ENABLED_CHECKBOX,
|
||||
text);
|
||||
delete[] text;
|
||||
m_enabled_checkbox->setVisible(false);
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 85, 30);
|
||||
rect = rect + v2s32(0, 25) + topleft;
|
||||
wchar_t* text = wgettext("Enable All");
|
||||
m_enableall = Environment->addButton(rect, this, GUI_ID_ENABLEALL,
|
||||
text);
|
||||
delete[] text;
|
||||
m_enableall->setVisible(false);
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 85, 30);
|
||||
rect = rect + v2s32(115, 25) + topleft;
|
||||
wchar_t* text = wgettext("Disable All");
|
||||
m_disableall = Environment->addButton(rect, this, GUI_ID_DISABLEALL, text );
|
||||
delete[] text;
|
||||
m_disableall->setVisible(false);
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 200, 20);
|
||||
rect += v2s32(0, 60) + topleft;
|
||||
wchar_t* text = wgettext("depends on:");
|
||||
Environment->addStaticText(text, rect, false, false, this, -1);
|
||||
delete[] text;
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 200, 85);
|
||||
rect += v2s32(0, 80) + topleft;
|
||||
m_dependencies_listbox =
|
||||
Environment->addListBox(rect, this, GUI_ID_DEPENDS_LISTBOX, true);
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 200, 20);
|
||||
rect += v2s32(0, 175) + topleft;
|
||||
wchar_t* text = wgettext("is required by:");
|
||||
Environment->addStaticText( text, rect, false, false, this, -1);
|
||||
delete[] text;
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 200, 85);
|
||||
rect += v2s32(0, 195) + topleft;
|
||||
m_rdependencies_listbox =
|
||||
Environment->addListBox(rect,this, GUI_ID_RDEPENDS_LISTBOX,true);
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 340, 250);
|
||||
rect += v2s32(220, 0) + topleft;
|
||||
m_treeview = Environment->addTreeView(rect, this,
|
||||
GUI_ID_MOD_TREEVIEW,true);
|
||||
gui::IGUITreeViewNode* node
|
||||
= m_treeview->getRoot()->addChildBack(L"Add-Ons");
|
||||
buildTreeView(m_addontree, node);
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 120, 30);
|
||||
rect = rect + v2s32(330, 270) - topleft;
|
||||
wchar_t* text = wgettext("Cancel");
|
||||
Environment->addButton(rect, this, GUI_ID_CANCEL, text);
|
||||
delete[] text;
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 120, 30);
|
||||
rect = rect + v2s32(460, 270) - topleft;
|
||||
wchar_t* text = wgettext("Save");
|
||||
Environment->addButton(rect, this, GUI_ID_SAVE, text);
|
||||
delete[] text;
|
||||
}
|
||||
changeCtype("C");
|
||||
|
||||
// at start, none of the treeview nodes is selected, so we select
|
||||
// the first element in the treeview of mods manually here.
|
||||
if(m_treeview->getRoot()->hasChilds())
|
||||
{
|
||||
m_treeview->getRoot()->getFirstChild()->setExpanded(true);
|
||||
m_treeview->getRoot()->getFirstChild()->setSelected(true);
|
||||
// Because a manual ->setSelected() doesn't cause an event, we
|
||||
// have to do this here:
|
||||
adjustSidebar();
|
||||
}
|
||||
}
|
||||
|
||||
bool GUIConfigureWorld::OnEvent(const SEvent& event)
|
||||
{
|
||||
|
||||
gui::IGUITreeViewNode* selected_node = NULL;
|
||||
if(m_treeview != NULL)
|
||||
selected_node = m_treeview->getSelected();
|
||||
|
||||
if(event.EventType==EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
|
||||
{
|
||||
switch (event.KeyInput.Key) {
|
||||
case KEY_ESCAPE: {
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
// irrlicht's built-in TreeView gui has no keyboard control,
|
||||
// so we do it here: up/down to select prev/next node,
|
||||
// left/right to collapse/expand nodes, space to toggle
|
||||
// enabled/disabled.
|
||||
case KEY_DOWN: {
|
||||
if(selected_node != NULL)
|
||||
{
|
||||
gui::IGUITreeViewNode* node = selected_node->getNextVisible();
|
||||
if(node != NULL)
|
||||
{
|
||||
node->setSelected(true);
|
||||
adjustSidebar();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case KEY_UP: {
|
||||
if(selected_node != NULL)
|
||||
{
|
||||
gui::IGUITreeViewNode* node = selected_node->getPrevSibling();
|
||||
if(node!=NULL)
|
||||
{
|
||||
node->setSelected(true);
|
||||
adjustSidebar();
|
||||
}
|
||||
else
|
||||
{
|
||||
gui::IGUITreeViewNode* parent = selected_node->getParent();
|
||||
if(selected_node == parent->getFirstChild() &&
|
||||
parent != m_treeview->getRoot())
|
||||
{
|
||||
parent->setSelected(true);
|
||||
adjustSidebar();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case KEY_RIGHT: {
|
||||
if(selected_node != NULL && selected_node->hasChilds())
|
||||
selected_node->setExpanded(true);
|
||||
return true;
|
||||
}
|
||||
case KEY_LEFT: {
|
||||
if(selected_node != NULL && selected_node->hasChilds())
|
||||
selected_node->setExpanded(false);
|
||||
return true;
|
||||
}
|
||||
case KEY_SPACE: {
|
||||
if(selected_node != NULL && !selected_node->hasChilds() &&
|
||||
selected_node->getText() != NULL)
|
||||
{
|
||||
std::string modname = wide_to_narrow(selected_node->getText());
|
||||
bool checked = m_enabled_checkbox->isChecked();
|
||||
m_enabled_checkbox->setChecked(!checked);
|
||||
setEnabled(modname,!checked);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default: {}
|
||||
}
|
||||
}
|
||||
if(event.EventType==EET_GUI_EVENT)
|
||||
{
|
||||
if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
|
||||
&& isVisible())
|
||||
{
|
||||
if(!canTakeFocus(event.GUIEvent.Element))
|
||||
{
|
||||
dstream<<"GUIConfigureWorld: Not allowing focus change."
|
||||
<<std::endl;
|
||||
// Returning true disables focus change
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED){
|
||||
switch(event.GUIEvent.Caller->getID()){
|
||||
case GUI_ID_CANCEL: {
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
case GUI_ID_SAVE: {
|
||||
std::string worldmtfile = m_wspec.path+DIR_DELIM+"world.mt";
|
||||
m_settings.updateConfigFile(worldmtfile.c_str());
|
||||
|
||||
// The trailing spaces are because there seems to be a
|
||||
// bug in the text-size calculation. if the trailing
|
||||
// spaces are removed from the message text, the
|
||||
// message gets wrapped and parts of it are cut off:
|
||||
wchar_t* text = wgettext("Configuration saved. ");
|
||||
GUIMessageMenu *menu =
|
||||
new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
|
||||
text );
|
||||
delete[] text;
|
||||
menu->drop();
|
||||
|
||||
try
|
||||
{
|
||||
ModConfiguration modconf(m_wspec.path);
|
||||
if(!modconf.isConsistent())
|
||||
{
|
||||
wchar_t* text = wgettext("Warning: Configuration not consistent. ");
|
||||
GUIMessageMenu *menu =
|
||||
new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
|
||||
text );
|
||||
delete[] text;
|
||||
menu->drop();
|
||||
}
|
||||
}
|
||||
catch(ModError &err)
|
||||
{
|
||||
errorstream<<err.what()<<std::endl;
|
||||
std::wstring text = narrow_to_wide(err.what()) + wgettext("\nCheck debug.txt for details.");
|
||||
GUIMessageMenu *menu =
|
||||
new GUIMessageMenu(Environment, Parent, -1, m_menumgr,
|
||||
text );
|
||||
menu->drop();
|
||||
}
|
||||
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
case GUI_ID_ENABLEALL: {
|
||||
if(selected_node != NULL && selected_node->getParent() == m_treeview->getRoot())
|
||||
{
|
||||
enableAllMods(m_addonmods,true);
|
||||
}
|
||||
else if(selected_node != NULL && selected_node->getText() != NULL)
|
||||
{
|
||||
std::string modname = wide_to_narrow(selected_node->getText());
|
||||
std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
|
||||
if(mod_it != m_addonmods.end())
|
||||
enableAllMods(mod_it->second.modpack_content,true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case GUI_ID_DISABLEALL: {
|
||||
if(selected_node != NULL && selected_node->getParent() == m_treeview->getRoot())
|
||||
{
|
||||
enableAllMods(m_addonmods,false);
|
||||
}
|
||||
if(selected_node != NULL && selected_node->getText() != NULL)
|
||||
{
|
||||
std::string modname = wide_to_narrow(selected_node->getText());
|
||||
std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
|
||||
if(mod_it != m_addonmods.end())
|
||||
enableAllMods(mod_it->second.modpack_content,false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(event.GUIEvent.EventType==gui::EGET_CHECKBOX_CHANGED &&
|
||||
event.GUIEvent.Caller->getID() == GUI_ID_ENABLED_CHECKBOX)
|
||||
{
|
||||
if(selected_node != NULL && !selected_node->hasChilds() &&
|
||||
selected_node->getText() != NULL)
|
||||
{
|
||||
std::string modname = wide_to_narrow(selected_node->getText());
|
||||
setEnabled(modname, m_enabled_checkbox->isChecked());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if(event.GUIEvent.EventType==gui::EGET_TREEVIEW_NODE_SELECT &&
|
||||
event.GUIEvent.Caller->getID() == GUI_ID_MOD_TREEVIEW)
|
||||
{
|
||||
selecting_dep = -1;
|
||||
selecting_rdep = -1;
|
||||
adjustSidebar();
|
||||
return true;
|
||||
}
|
||||
if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED &&
|
||||
event.GUIEvent.Caller->getID() == GUI_ID_DEPENDS_LISTBOX)
|
||||
{
|
||||
selecting_dep = m_dependencies_listbox->getSelected();
|
||||
selecting_rdep = -1;
|
||||
return true;
|
||||
}
|
||||
if(event.GUIEvent.EventType==gui::EGET_LISTBOX_CHANGED &&
|
||||
event.GUIEvent.Caller->getID() == GUI_ID_RDEPENDS_LISTBOX)
|
||||
{
|
||||
selecting_dep = -1;
|
||||
selecting_rdep = m_rdependencies_listbox->getSelected();
|
||||
return true;
|
||||
}
|
||||
|
||||
//double click in a dependency listbox: find corresponding
|
||||
//treeviewnode and select it:
|
||||
if(event.GUIEvent.EventType==gui::EGET_LISTBOX_SELECTED_AGAIN)
|
||||
{
|
||||
gui::IGUIListBox* box = NULL;
|
||||
if(event.GUIEvent.Caller->getID() == GUI_ID_DEPENDS_LISTBOX)
|
||||
{
|
||||
box = m_dependencies_listbox;
|
||||
if(box->getSelected() != selecting_dep)
|
||||
return true;
|
||||
}
|
||||
if(event.GUIEvent.Caller->getID() == GUI_ID_RDEPENDS_LISTBOX)
|
||||
{
|
||||
box = m_rdependencies_listbox;
|
||||
if(box->getSelected() != selecting_rdep)
|
||||
return true;
|
||||
}
|
||||
if(box != NULL && box->getSelected() != -1 &&
|
||||
box->getListItem(box->getSelected()) != NULL)
|
||||
{
|
||||
std::string modname =
|
||||
wide_to_narrow(box->getListItem(box->getSelected()));
|
||||
std::map<std::string, gui::IGUITreeViewNode*>::iterator it =
|
||||
m_nodes.find(modname);
|
||||
if(it != m_nodes.end())
|
||||
{
|
||||
// select node and make sure node is visible by
|
||||
// expanding all parents
|
||||
gui::IGUITreeViewNode* node = (*it).second;
|
||||
node->setSelected(true);
|
||||
while(!node->isVisible() &&
|
||||
node->getParent() != m_treeview->getRoot())
|
||||
{
|
||||
node = node->getParent();
|
||||
node->setExpanded(true);
|
||||
}
|
||||
adjustSidebar();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Parent ? Parent->OnEvent(event) : false;
|
||||
}
|
||||
|
||||
void GUIConfigureWorld::buildTreeView(std::map<std::string, ModSpec> mods,
|
||||
gui::IGUITreeViewNode* node)
|
||||
{
|
||||
for(std::map<std::string,ModSpec>::iterator it = mods.begin();
|
||||
it != mods.end(); ++it)
|
||||
{
|
||||
std::string modname = (*it).first;
|
||||
ModSpec mod = (*it).second;
|
||||
gui::IGUITreeViewNode* new_node =
|
||||
node->addChildBack(narrow_to_wide(modname).c_str());
|
||||
m_nodes.insert(std::make_pair(modname, new_node));
|
||||
if(mod.is_modpack)
|
||||
buildTreeView(mod.modpack_content, new_node);
|
||||
else
|
||||
{
|
||||
// set icon for node: x for disabled mods, checkmark for enabled mods
|
||||
bool mod_enabled = false;
|
||||
if(m_settings.exists("load_mod_"+modname))
|
||||
mod_enabled = m_settings.getBool("load_mod_"+modname);
|
||||
if(mod_enabled)
|
||||
new_node->setIcon(CHECKMARK_STR);
|
||||
else
|
||||
new_node->setIcon(CROSS_STR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GUIConfigureWorld::adjustSidebar()
|
||||
{
|
||||
gui::IGUITreeViewNode* node = m_treeview->getSelected();
|
||||
std::wstring modname_w;
|
||||
if(node->getText() != NULL)
|
||||
modname_w = node->getText();
|
||||
else
|
||||
modname_w = L"N/A";
|
||||
std::string modname = wide_to_narrow(modname_w);
|
||||
|
||||
ModSpec mspec;
|
||||
std::map<std::string, ModSpec>::iterator it = m_addonmods.find(modname);
|
||||
if(it != m_addonmods.end())
|
||||
mspec = it->second;
|
||||
|
||||
m_dependencies_listbox->clear();
|
||||
m_rdependencies_listbox->clear();
|
||||
|
||||
// if no mods installed, there is nothing to enable/disable, so we
|
||||
// don't show buttons or checkbox on the sidebar
|
||||
if(node->getParent() == m_treeview->getRoot() && !node->hasChilds())
|
||||
{
|
||||
m_disableall->setVisible(false);
|
||||
m_enableall->setVisible(false);
|
||||
m_enabled_checkbox->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// a modpack is not enabled/disabled by itself, only its cotnents
|
||||
// are. so we show show enable/disable all buttons, but hide the
|
||||
// checkbox
|
||||
if(node->getParent() == m_treeview->getRoot() ||
|
||||
mspec.is_modpack)
|
||||
{
|
||||
m_enabled_checkbox->setVisible(false);
|
||||
m_disableall->setVisible(true);
|
||||
m_enableall->setVisible(true);
|
||||
m_modname_text->setText((L"Modpack: "+modname_w).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// for a normal mod, we hide the enable/disable all buttons, but show the checkbox.
|
||||
m_disableall->setVisible(false);
|
||||
m_enableall->setVisible(false);
|
||||
m_enabled_checkbox->setVisible(true);
|
||||
m_modname_text->setText((L"Mod: "+modname_w).c_str());
|
||||
|
||||
// the mod is enabled unless it is disabled in the world.mt settings.
|
||||
bool mod_enabled = true;
|
||||
if(m_settings.exists("load_mod_"+modname))
|
||||
mod_enabled = m_settings.getBool("load_mod_"+modname);
|
||||
m_enabled_checkbox->setChecked(mod_enabled);
|
||||
|
||||
for(std::set<std::string>::iterator it=mspec.depends.begin();
|
||||
it != mspec.depends.end(); ++it)
|
||||
{
|
||||
// check if it is an add-on mod or a game/world mod. We only
|
||||
// want to show add-ons
|
||||
std::string dependency = (*it);
|
||||
if(m_gamemods.count(dependency) > 0)
|
||||
dependency += " (" + m_gspec.id + ")";
|
||||
else if(m_worldmods.count(dependency) > 0)
|
||||
dependency += " (" + m_wspec.name + ")";
|
||||
else if(m_addonmods.count(dependency) == 0)
|
||||
dependency += " (missing)";
|
||||
m_dependencies_listbox->addItem(narrow_to_wide(dependency).c_str());
|
||||
}
|
||||
|
||||
// reverse dependencies of this mod:
|
||||
std::pair< std::multimap<std::string, std::string>::iterator,
|
||||
std::multimap<std::string, std::string>::iterator > rdep =
|
||||
m_reverse_depends.equal_range(modname);
|
||||
for(std::multimap<std::string,std::string>::iterator it = rdep.first;
|
||||
it != rdep.second; ++it)
|
||||
{
|
||||
// check if it is an add-on mod or a game/world mod. We only
|
||||
// want to show add-ons
|
||||
std::string rdependency = (*it).second;
|
||||
if(m_addonmods.count(rdependency) > 0)
|
||||
m_rdependencies_listbox->addItem(narrow_to_wide(rdependency).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void GUIConfigureWorld::enableAllMods(std::map<std::string, ModSpec> mods,bool enable)
|
||||
{
|
||||
for(std::map<std::string, ModSpec>::iterator it = mods.begin();
|
||||
it != mods.end(); ++it)
|
||||
{
|
||||
ModSpec mod = (*it).second;
|
||||
if(mod.is_modpack)
|
||||
// a modpack, recursively enable all mods in it
|
||||
enableAllMods(mod.modpack_content,enable);
|
||||
else // not a modpack
|
||||
setEnabled(mod.name, enable);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void GUIConfigureWorld::enableMod(std::string modname)
|
||||
{
|
||||
std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
|
||||
if(mod_it == m_addonmods.end()){
|
||||
errorstream << "enableMod() called with invalid mod name \"" << modname << "\"" << std::endl;
|
||||
return;
|
||||
}
|
||||
ModSpec mspec = mod_it->second;
|
||||
m_settings.setBool("load_mod_"+modname,true);
|
||||
std::map<std::string,gui::IGUITreeViewNode*>::iterator it =
|
||||
m_nodes.find(modname);
|
||||
if(it != m_nodes.end())
|
||||
(*it).second->setIcon(CHECKMARK_STR);
|
||||
//also enable all dependencies
|
||||
for(std::set<std::string>::iterator it=mspec.depends.begin();
|
||||
it != mspec.depends.end(); ++it)
|
||||
{
|
||||
std::string dependency = *it;
|
||||
// only enable it if it is an add-on mod
|
||||
if(m_addonmods.count(dependency) > 0)
|
||||
enableMod(dependency);
|
||||
}
|
||||
}
|
||||
|
||||
void GUIConfigureWorld::disableMod(std::string modname)
|
||||
{
|
||||
std::map<std::string, ModSpec>::iterator mod_it = m_addonmods.find(modname);
|
||||
if(mod_it == m_addonmods.end()){
|
||||
errorstream << "disableMod() called with invalid mod name \"" << modname << "\"" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
m_settings.setBool("load_mod_"+modname,false);
|
||||
std::map<std::string,gui::IGUITreeViewNode*>::iterator it =
|
||||
m_nodes.find(modname);
|
||||
if(it != m_nodes.end())
|
||||
(*it).second->setIcon(CROSS_STR);
|
||||
//also disable all mods that depend on this one
|
||||
std::pair<std::multimap<std::string, std::string>::iterator,
|
||||
std::multimap<std::string, std::string>::iterator > rdep =
|
||||
m_reverse_depends.equal_range(modname);
|
||||
for(std::multimap<std::string,std::string>::iterator it = rdep.first;
|
||||
it != rdep.second; ++it)
|
||||
{
|
||||
std::string rdependency = (*it).second;
|
||||
// only disable it if it is an add-on mod
|
||||
if(m_addonmods.count(rdependency) > 0)
|
||||
disableMod(rdependency);
|
||||
}
|
||||
}
|
||||
|
@ -1,107 +0,0 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef GUICONFIGUREWORLD_HEADER
|
||||
#define GUICONFIGUREWORLD_HEADER
|
||||
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
#include "mods.h"
|
||||
#include "subgame.h"
|
||||
#include "settings.h"
|
||||
|
||||
|
||||
namespace irr{
|
||||
namespace gui{
|
||||
class IGUITreeViewNode;
|
||||
}
|
||||
}
|
||||
|
||||
class GUIConfigureWorld : public GUIModalMenu
|
||||
{
|
||||
public:
|
||||
GUIConfigureWorld(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr, WorldSpec wspec);
|
||||
|
||||
void regenerateGui(v2u32 screensize);
|
||||
|
||||
void drawMenu();
|
||||
|
||||
bool OnEvent(const SEvent& event);
|
||||
|
||||
private:
|
||||
WorldSpec m_wspec;
|
||||
SubgameSpec m_gspec;
|
||||
|
||||
// tree of installed add-on mods. key is the mod name, modpacks
|
||||
// are not expanded.
|
||||
std::map<std::string, ModSpec> m_addontree;
|
||||
|
||||
// like m_addontree, but modpacks are expanded.
|
||||
std::map<std::string, ModSpec> m_addonmods;
|
||||
|
||||
// list of game mods (flattened)
|
||||
std::map<std::string, ModSpec> m_gamemods;
|
||||
|
||||
// list of world mods (flattened)
|
||||
std::map<std::string, ModSpec> m_worldmods;
|
||||
|
||||
// for each mod, the set of mods depending on it
|
||||
std::multimap<std::string, std::string> m_reverse_depends;
|
||||
|
||||
// the settings in the world.mt file
|
||||
Settings m_settings;
|
||||
|
||||
// maps modnames to nodes in m_treeview
|
||||
std::map<std::string,gui::IGUITreeViewNode*> m_nodes;
|
||||
|
||||
gui::IGUIStaticText* m_modname_text;
|
||||
gui::IGUITreeView* m_treeview;
|
||||
gui::IGUIButton* m_enableall;
|
||||
gui::IGUIButton* m_disableall;
|
||||
gui::IGUICheckBox* m_enabled_checkbox;
|
||||
gui::IGUIListBox* m_dependencies_listbox;
|
||||
gui::IGUIListBox* m_rdependencies_listbox;
|
||||
void buildTreeView(std::map<std::string,ModSpec> mods,
|
||||
gui::IGUITreeViewNode* node);
|
||||
void adjustSidebar();
|
||||
void enableAllMods(std::map<std::string,ModSpec> mods, bool enable);
|
||||
void setEnabled(std::string modname, bool enable)
|
||||
{
|
||||
if(enable)
|
||||
enableMod(modname);
|
||||
else
|
||||
disableMod(modname);
|
||||
};
|
||||
|
||||
void enableMod(std::string modname);
|
||||
void disableMod(std::string modname);
|
||||
|
||||
// hack to work around wonky handling of double-click in
|
||||
// irrlicht. store selected index of listbox items here so event
|
||||
// handling can check whether it was a real double click on the
|
||||
// same item. (irrlicht also reports a double click if you rapidly
|
||||
// select two different items.)
|
||||
int selecting_dep;
|
||||
int selecting_rdep;
|
||||
|
||||
IMenuManager* m_menumgr;
|
||||
};
|
||||
#endif
|
@ -1,204 +0,0 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "guiConfirmMenu.h"
|
||||
#include "debug.h"
|
||||
#include "serialization.h"
|
||||
#include <string>
|
||||
#include <IGUICheckBox.h>
|
||||
#include <IGUIEditBox.h>
|
||||
#include <IGUIButton.h>
|
||||
#include <IGUIStaticText.h>
|
||||
#include <IGUIFont.h>
|
||||
|
||||
#include "gettext.h"
|
||||
|
||||
enum
|
||||
{
|
||||
GUI_ID_YES = 101,
|
||||
GUI_ID_NO,
|
||||
};
|
||||
|
||||
GUIConfirmMenu::GUIConfirmMenu(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
ConfirmDest *dest,
|
||||
std::wstring message_text
|
||||
):
|
||||
GUIModalMenu(env, parent, id, menumgr),
|
||||
m_dest(dest),
|
||||
m_message_text(message_text)
|
||||
{
|
||||
}
|
||||
|
||||
GUIConfirmMenu::~GUIConfirmMenu()
|
||||
{
|
||||
removeChildren();
|
||||
if(m_dest)
|
||||
delete m_dest;
|
||||
}
|
||||
|
||||
void GUIConfirmMenu::removeChildren()
|
||||
{
|
||||
const core::list<gui::IGUIElement*> &children = getChildren();
|
||||
core::list<gui::IGUIElement*> children_copy;
|
||||
for(core::list<gui::IGUIElement*>::ConstIterator
|
||||
i = children.begin(); i != children.end(); i++)
|
||||
{
|
||||
children_copy.push_back(*i);
|
||||
}
|
||||
for(core::list<gui::IGUIElement*>::Iterator
|
||||
i = children_copy.begin();
|
||||
i != children_copy.end(); i++)
|
||||
{
|
||||
(*i)->remove();
|
||||
}
|
||||
}
|
||||
|
||||
void GUIConfirmMenu::regenerateGui(v2u32 screensize)
|
||||
{
|
||||
/*
|
||||
Remove stuff
|
||||
*/
|
||||
removeChildren();
|
||||
|
||||
/*
|
||||
Calculate new sizes and positions
|
||||
*/
|
||||
core::rect<s32> rect(
|
||||
screensize.X/2 - 580/2,
|
||||
screensize.Y/2 - 300/2,
|
||||
screensize.X/2 + 580/2,
|
||||
screensize.Y/2 + 300/2
|
||||
);
|
||||
|
||||
DesiredRect = rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
|
||||
v2s32 size = rect.getSize();
|
||||
|
||||
gui::IGUISkin *skin = Environment->getSkin();
|
||||
gui::IGUIFont *font = skin->getFont();
|
||||
s32 msg_h = font->getDimension(m_message_text.c_str()).Height;
|
||||
s32 msg_w = font->getDimension(m_message_text.c_str()).Width;
|
||||
if(msg_h > 200)
|
||||
msg_h = 200;
|
||||
if(msg_w > 540)
|
||||
msg_w = 540;
|
||||
|
||||
/*
|
||||
Add stuff
|
||||
*/
|
||||
{
|
||||
core::rect<s32> rect(0, 0, msg_w, msg_h);
|
||||
rect += v2s32(size.X/2-msg_w/2, size.Y/2-30/2 - msg_h/2);
|
||||
Environment->addStaticText(m_message_text.c_str(),
|
||||
rect, false, true, this, -1);
|
||||
}
|
||||
changeCtype("");
|
||||
int bw = 100;
|
||||
{
|
||||
core::rect<s32> rect(0, 0, bw, 30);
|
||||
rect = rect + v2s32(size.X/2-bw/2-(bw/2+5), size.Y/2-30/2+5 + msg_h/2);
|
||||
wchar_t* text = wgettext("Yes");
|
||||
Environment->addButton(rect, this, GUI_ID_YES,
|
||||
text);
|
||||
delete[] text;
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, bw, 30);
|
||||
rect = rect + v2s32(size.X/2-bw/2+(bw/2+5), size.Y/2-30/2+5 + msg_h/2);
|
||||
wchar_t* text = wgettext("No");
|
||||
Environment->addButton(rect, this, GUI_ID_NO,
|
||||
text);
|
||||
delete[] text;
|
||||
}
|
||||
changeCtype("C");
|
||||
}
|
||||
|
||||
void GUIConfirmMenu::drawMenu()
|
||||
{
|
||||
gui::IGUISkin* skin = Environment->getSkin();
|
||||
if (!skin)
|
||||
return;
|
||||
video::IVideoDriver* driver = Environment->getVideoDriver();
|
||||
|
||||
video::SColor bgcolor(140,0,0,0);
|
||||
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
|
||||
|
||||
gui::IGUIElement::draw();
|
||||
}
|
||||
|
||||
void GUIConfirmMenu::acceptInput(bool answer)
|
||||
{
|
||||
if(m_dest)
|
||||
m_dest->answer(answer);
|
||||
}
|
||||
|
||||
bool GUIConfirmMenu::OnEvent(const SEvent& event)
|
||||
{
|
||||
if(event.EventType==EET_KEY_INPUT_EVENT)
|
||||
{
|
||||
if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
|
||||
{
|
||||
acceptInput(false);
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
|
||||
{
|
||||
acceptInput(true);
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(event.EventType==EET_GUI_EVENT)
|
||||
{
|
||||
if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
|
||||
&& isVisible())
|
||||
{
|
||||
if(!canTakeFocus(event.GUIEvent.Element))
|
||||
{
|
||||
dstream<<"GUIConfirmMenu: Not allowing focus change."
|
||||
<<std::endl;
|
||||
// Returning true disables focus change
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
|
||||
{
|
||||
switch(event.GUIEvent.Caller->getID())
|
||||
{
|
||||
case GUI_ID_YES:
|
||||
acceptInput(true);
|
||||
quitMenu();
|
||||
// quitMenu deallocates menu
|
||||
return true;
|
||||
case GUI_ID_NO:
|
||||
acceptInput(false);
|
||||
quitMenu();
|
||||
// quitMenu deallocates menu
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Parent ? Parent->OnEvent(event) : false;
|
||||
}
|
||||
|
@ -1,280 +0,0 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "guiCreateWorld.h"
|
||||
#include "debug.h"
|
||||
#include "serialization.h"
|
||||
#include <string>
|
||||
#include <IGUICheckBox.h>
|
||||
#include <IGUIEditBox.h>
|
||||
#include <IGUIButton.h>
|
||||
#include <IGUIStaticText.h>
|
||||
#include <IGUIFont.h>
|
||||
#include <IGUIListBox.h>
|
||||
#include "gettext.h"
|
||||
#include "util/string.h"
|
||||
|
||||
enum
|
||||
{
|
||||
GUI_ID_NAME_INPUT = 101,
|
||||
GUI_ID_GAME_LISTBOX,
|
||||
GUI_ID_CREATE,
|
||||
GUI_ID_CANCEL
|
||||
};
|
||||
|
||||
GUICreateWorld::GUICreateWorld(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
CreateWorldDest *dest,
|
||||
const std::vector<SubgameSpec> &games,
|
||||
const std::string &initial_game
|
||||
):
|
||||
GUIModalMenu(env, parent, id, menumgr),
|
||||
m_dest(dest),
|
||||
m_games(games),
|
||||
m_initial_game_i(0)
|
||||
{
|
||||
assert(games.size() > 0);
|
||||
|
||||
for(size_t i=0; i<games.size(); i++){
|
||||
if(games[i].id == initial_game){
|
||||
m_initial_game_i = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GUICreateWorld::~GUICreateWorld()
|
||||
{
|
||||
removeChildren();
|
||||
if(m_dest)
|
||||
delete m_dest;
|
||||
}
|
||||
|
||||
void GUICreateWorld::removeChildren()
|
||||
{
|
||||
const core::list<gui::IGUIElement*> &children = getChildren();
|
||||
core::list<gui::IGUIElement*> children_copy;
|
||||
for(core::list<gui::IGUIElement*>::ConstIterator
|
||||
i = children.begin(); i != children.end(); i++)
|
||||
{
|
||||
children_copy.push_back(*i);
|
||||
}
|
||||
for(core::list<gui::IGUIElement*>::Iterator
|
||||
i = children_copy.begin();
|
||||
i != children_copy.end(); i++)
|
||||
{
|
||||
(*i)->remove();
|
||||
}
|
||||
}
|
||||
|
||||
void GUICreateWorld::regenerateGui(v2u32 screensize)
|
||||
{
|
||||
std::wstring name = L"";
|
||||
|
||||
{
|
||||
gui::IGUIElement *e = getElementFromId(GUI_ID_NAME_INPUT);
|
||||
if(e != NULL)
|
||||
name = e->getText();
|
||||
}
|
||||
|
||||
/*
|
||||
Remove stuff
|
||||
*/
|
||||
removeChildren();
|
||||
|
||||
/*
|
||||
Calculate new sizes and positions
|
||||
*/
|
||||
core::rect<s32> rect(
|
||||
screensize.X/2 - 580/2,
|
||||
screensize.Y/2 - 300/2,
|
||||
screensize.X/2 + 580/2,
|
||||
screensize.Y/2 + 300/2
|
||||
);
|
||||
|
||||
DesiredRect = rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
|
||||
v2s32 topleft = v2s32(10+80, 10+70);
|
||||
|
||||
/*
|
||||
Add stuff
|
||||
*/
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 100, 20);
|
||||
rect += v2s32(0, 5) + topleft;
|
||||
wchar_t* text = wgettext("World name");
|
||||
Environment->addStaticText(text, rect, false, true, this, -1);
|
||||
delete[] text;
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 300, 30);
|
||||
rect = rect + v2s32(100, 0) + topleft;
|
||||
gui::IGUIElement *e =
|
||||
Environment->addEditBox(name.c_str(), rect, true, this, GUI_ID_NAME_INPUT);
|
||||
Environment->setFocus(e);
|
||||
|
||||
irr::SEvent evt;
|
||||
evt.EventType = EET_KEY_INPUT_EVENT;
|
||||
evt.KeyInput.Key = KEY_END;
|
||||
evt.KeyInput.PressedDown = true;
|
||||
evt.KeyInput.Char = 0;
|
||||
evt.KeyInput.Control = 0;
|
||||
evt.KeyInput.Shift = 0;
|
||||
e->OnEvent(evt);
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 100, 20);
|
||||
rect += v2s32(0, 40+5) + topleft;
|
||||
wchar_t* text = wgettext("Game");
|
||||
Environment->addStaticText(text, rect, false, true, this, -1);
|
||||
delete[] text;
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 300, 80);
|
||||
rect += v2s32(100, 40) + topleft;
|
||||
gui::IGUIListBox *e = Environment->addListBox(rect, this,
|
||||
GUI_ID_GAME_LISTBOX);
|
||||
e->setDrawBackground(true);
|
||||
for(u32 i=0; i<m_games.size(); i++){
|
||||
std::wostringstream os(std::ios::binary);
|
||||
os<<narrow_to_wide(m_games[i].name).c_str();
|
||||
os<<L" [";
|
||||
os<<narrow_to_wide(m_games[i].id).c_str();
|
||||
os<<L"]";
|
||||
e->addItem(os.str().c_str());
|
||||
}
|
||||
e->setSelected(m_initial_game_i);
|
||||
}
|
||||
changeCtype("");
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 120, 30);
|
||||
rect = rect + v2s32(170, 140) + topleft;
|
||||
wchar_t* text = wgettext("Create");
|
||||
Environment->addButton(rect, this, GUI_ID_CREATE,
|
||||
text);
|
||||
delete[] text;
|
||||
}
|
||||
{
|
||||
core::rect<s32> rect(0, 0, 120, 30);
|
||||
rect = rect + v2s32(300, 140) + topleft;
|
||||
wchar_t* text = wgettext("Cancel");
|
||||
Environment->addButton(rect, this, GUI_ID_CANCEL,
|
||||
text);
|
||||
delete [] text;
|
||||
}
|
||||
changeCtype("C");
|
||||
}
|
||||
|
||||
void GUICreateWorld::drawMenu()
|
||||
{
|
||||
gui::IGUISkin* skin = Environment->getSkin();
|
||||
if (!skin)
|
||||
return;
|
||||
video::IVideoDriver* driver = Environment->getVideoDriver();
|
||||
|
||||
video::SColor bgcolor(140,0,0,0);
|
||||
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
|
||||
|
||||
gui::IGUIElement::draw();
|
||||
}
|
||||
|
||||
void GUICreateWorld::acceptInput()
|
||||
{
|
||||
if(m_dest)
|
||||
{
|
||||
int selected = -1;
|
||||
{
|
||||
gui::IGUIElement *e = getElementFromId(GUI_ID_GAME_LISTBOX);
|
||||
if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX)
|
||||
selected = ((gui::IGUIListBox*)e)->getSelected();
|
||||
}
|
||||
std::wstring name;
|
||||
{
|
||||
gui::IGUIElement *e = getElementFromId(GUI_ID_NAME_INPUT);
|
||||
if(e != NULL)
|
||||
name = e->getText();
|
||||
}
|
||||
if(selected != -1 && name != L"")
|
||||
m_dest->accepted(name, m_games[selected].id);
|
||||
delete m_dest;
|
||||
m_dest = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool GUICreateWorld::OnEvent(const SEvent& event)
|
||||
{
|
||||
if(event.EventType==EET_KEY_INPUT_EVENT)
|
||||
{
|
||||
if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
|
||||
{
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
|
||||
{
|
||||
acceptInput();
|
||||
quitMenu();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(event.EventType==EET_GUI_EVENT)
|
||||
{
|
||||
if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
|
||||
&& isVisible())
|
||||
{
|
||||
if(!canTakeFocus(event.GUIEvent.Element))
|
||||
{
|
||||
dstream<<"GUICreateWorld: Not allowing focus change."
|
||||
<<std::endl;
|
||||
// Returning true disables focus change
|
||||
return true;
|
||||
}
|
||||
}
|
||||
bool accept_input = false;
|
||||
if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED){
|
||||
switch(event.GUIEvent.Caller->getID()){
|
||||
case GUI_ID_CANCEL:
|
||||
quitMenu();
|
||||
return true;
|
||||
break;
|
||||
case GUI_ID_CREATE:
|
||||
accept_input = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER){
|
||||
switch(event.GUIEvent.Caller->getID()){
|
||||
case GUI_ID_NAME_INPUT:
|
||||
accept_input = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(accept_input){
|
||||
acceptInput();
|
||||
quitMenu();
|
||||
// quitMenu deallocates menu
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Parent ? Parent->OnEvent(event) : false;
|
||||
}
|
||||
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef GUICREATEWORLD_HEADER
|
||||
#define GUICREATEWORLD_HEADER
|
||||
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "modalMenu.h"
|
||||
#include <string>
|
||||
#include "subgame.h"
|
||||
|
||||
struct CreateWorldDest
|
||||
{
|
||||
virtual void accepted(std::wstring name, std::string gameid) = 0;
|
||||
virtual ~CreateWorldDest() {};
|
||||
};
|
||||
|
||||
class GUICreateWorld : public GUIModalMenu
|
||||
{
|
||||
public:
|
||||
GUICreateWorld(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
CreateWorldDest *dest,
|
||||
const std::vector<SubgameSpec> &games,
|
||||
const std::string &initial_game);
|
||||
~GUICreateWorld();
|
||||
|
||||
void removeChildren();
|
||||
/*
|
||||
Remove and re-add (or reposition) stuff
|
||||
*/
|
||||
void regenerateGui(v2u32 screensize);
|
||||
|
||||
void drawMenu();
|
||||
|
||||
void acceptInput();
|
||||
|
||||
bool OnEvent(const SEvent& event);
|
||||
|
||||
private:
|
||||
CreateWorldDest *m_dest;
|
||||
std::vector<SubgameSpec> m_games;
|
||||
int m_initial_game_i;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
570
src/guiEngine.cpp
Normal file
570
src/guiEngine.cpp
Normal file
@ -0,0 +1,570 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 sapier
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
}
|
||||
|
||||
#include "irrlicht.h"
|
||||
|
||||
#include "porting.h"
|
||||
#include "filesys.h"
|
||||
#include "main.h"
|
||||
#include "settings.h"
|
||||
#include "guiMainMenu.h"
|
||||
|
||||
#include "guiEngine.h"
|
||||
|
||||
#if USE_CURL
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
int menuscript_ErrorHandler(lua_State *L) {
|
||||
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return 1;
|
||||
}
|
||||
lua_getfield(L, -1, "traceback");
|
||||
if (!lua_isfunction(L, -1)) {
|
||||
lua_pop(L, 2);
|
||||
return 1;
|
||||
}
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushinteger(L, 2);
|
||||
lua_call(L, 2, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
TextDestGuiEngine::TextDestGuiEngine(GUIEngine* engine)
|
||||
{
|
||||
m_engine = engine;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void TextDestGuiEngine::gotText(std::map<std::string, std::string> fields)
|
||||
{
|
||||
m_engine->handleButtons(fields);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void TextDestGuiEngine::gotText(std::wstring text)
|
||||
{
|
||||
m_engine->handleEvent(wide_to_narrow(text));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
GUIEngine::GUIEngine( irr::IrrlichtDevice* dev,
|
||||
gui::IGUIElement* parent,
|
||||
IMenuManager *menumgr,
|
||||
scene::ISceneManager* smgr,
|
||||
MainMenuData* data) :
|
||||
m_device(dev),
|
||||
m_parent(parent),
|
||||
m_menumanager(menumgr),
|
||||
m_smgr(smgr),
|
||||
m_data(data),
|
||||
m_formspecgui(0),
|
||||
m_buttonhandler(0),
|
||||
m_menu(0),
|
||||
m_startgame(false),
|
||||
m_engineluastack(0),
|
||||
m_luaerrorhandler(-1),
|
||||
m_scriptdir(""),
|
||||
m_irr_toplefttext(0),
|
||||
m_clouds_enabled(true),
|
||||
m_cloud()
|
||||
{
|
||||
//initialize texture pointers
|
||||
for (unsigned int i = 0; i < TEX_LAYER_MAX; i++) {
|
||||
m_textures[i] = 0;
|
||||
}
|
||||
// is deleted by guiformspec!
|
||||
m_buttonhandler = new TextDestGuiEngine(this);
|
||||
|
||||
//create luastack
|
||||
m_engineluastack = luaL_newstate();
|
||||
|
||||
//load basic lua modules
|
||||
luaL_openlibs(m_engineluastack);
|
||||
|
||||
//init
|
||||
guiLuaApi::initialize(m_engineluastack,this);
|
||||
|
||||
//push errorstring
|
||||
if (m_data->errormessage != "")
|
||||
{
|
||||
lua_getglobal(m_engineluastack, "gamedata");
|
||||
int gamedata_idx = lua_gettop(m_engineluastack);
|
||||
lua_pushstring(m_engineluastack, "errormessage");
|
||||
lua_pushstring(m_engineluastack,m_data->errormessage.c_str());
|
||||
lua_settable(m_engineluastack, gamedata_idx);
|
||||
m_data->errormessage = "";
|
||||
}
|
||||
|
||||
//create topleft header
|
||||
core::rect<s32> rect(0, 0, 500, 40);
|
||||
rect += v2s32(4, 0);
|
||||
std::string t = "Minetest " VERSION_STRING;
|
||||
|
||||
m_irr_toplefttext =
|
||||
m_device->getGUIEnvironment()->addStaticText(narrow_to_wide(t).c_str(),
|
||||
rect,false,true,0,-1);
|
||||
|
||||
//create formspecsource
|
||||
m_formspecgui = new FormspecFormSource("",&m_formspecgui);
|
||||
|
||||
/* Create menu */
|
||||
m_menu =
|
||||
new GUIFormSpecMenu( m_device,
|
||||
m_parent,
|
||||
-1,
|
||||
m_menumanager,
|
||||
0 /* &client */,
|
||||
0 /* gamedef */);
|
||||
|
||||
m_menu->allowClose(false);
|
||||
m_menu->lockSize(true,v2u32(800,600));
|
||||
m_menu->setFormSource(m_formspecgui);
|
||||
m_menu->setTextDest(m_buttonhandler);
|
||||
m_menu->useGettext(true);
|
||||
|
||||
std::string builtin_helpers
|
||||
= porting::path_share + DIR_DELIM + "builtin"
|
||||
+ DIR_DELIM + "mainmenu_helper.lua";
|
||||
|
||||
if (!runScript(builtin_helpers)) {
|
||||
errorstream
|
||||
<< "GUIEngine::GUIEngine unable to load builtin helper script"
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string menuscript = "";
|
||||
if (g_settings->exists("main_menu_script"))
|
||||
menuscript = g_settings->get("main_menu_script");
|
||||
std::string builtin_menuscript =
|
||||
porting::path_share + DIR_DELIM + "builtin"
|
||||
+ DIR_DELIM + "mainmenu.lua";
|
||||
|
||||
lua_pushcfunction(m_engineluastack, menuscript_ErrorHandler);
|
||||
m_luaerrorhandler = lua_gettop(m_engineluastack);
|
||||
|
||||
m_scriptdir = menuscript.substr(0,menuscript.find_last_of(DIR_DELIM)-1);
|
||||
if((menuscript == "") || (!runScript(menuscript))) {
|
||||
infostream
|
||||
<< "GUIEngine::GUIEngine execution of custom menu failed!"
|
||||
<< std::endl
|
||||
<< "\tfalling back to builtin menu"
|
||||
<< std::endl;
|
||||
m_scriptdir = fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "builtin"+ DIR_DELIM);
|
||||
if(!runScript(builtin_menuscript)) {
|
||||
errorstream
|
||||
<< "GUIEngine::GUIEngine unable to load builtin menu"
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
|
||||
m_menumanager->deletingMenu(m_menu);
|
||||
m_menu->drop();
|
||||
m_menu = 0;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
bool GUIEngine::runScript(std::string script) {
|
||||
|
||||
int ret = luaL_loadfile(m_engineluastack, script.c_str()) ||
|
||||
lua_pcall(m_engineluastack, 0, 0, m_luaerrorhandler);
|
||||
if(ret){
|
||||
errorstream<<"========== ERROR FROM LUA WHILE CREATING MAIN MENU ==========="<<std::endl;
|
||||
errorstream<<"Failed to load and run script from "<<std::endl;
|
||||
errorstream<<script<<":"<<std::endl;
|
||||
errorstream<<std::endl;
|
||||
errorstream<<lua_tostring(m_engineluastack, -1)<<std::endl;
|
||||
errorstream<<std::endl;
|
||||
errorstream<<"=================== END OF ERROR FROM LUA ===================="<<std::endl;
|
||||
lua_pop(m_engineluastack, 1); // Pop error message from stack
|
||||
lua_pop(m_engineluastack, 1); // Pop the error handler from stack
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::run()
|
||||
{
|
||||
|
||||
// Always create clouds because they may or may not be
|
||||
// needed based on the game selected
|
||||
video::IVideoDriver* driver = m_device->getVideoDriver();
|
||||
|
||||
cloudInit();
|
||||
|
||||
while(m_device->run() && (!m_startgame)) {
|
||||
driver->beginScene(true, true, video::SColor(255,140,186,250));
|
||||
|
||||
if (m_clouds_enabled)
|
||||
{
|
||||
cloudPreProcess();
|
||||
drawOverlay(driver);
|
||||
}
|
||||
else
|
||||
drawBackground(driver);
|
||||
|
||||
drawHeader(driver);
|
||||
drawFooter(driver);
|
||||
|
||||
m_device->getGUIEnvironment()->drawAll();
|
||||
|
||||
driver->endScene();
|
||||
|
||||
if (m_clouds_enabled)
|
||||
cloudPostProcess();
|
||||
else
|
||||
sleep_ms(25);
|
||||
}
|
||||
|
||||
m_menu->quitMenu();
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::handleEvent(std::string text)
|
||||
{
|
||||
lua_getglobal(m_engineluastack, "engine");
|
||||
|
||||
lua_getfield(m_engineluastack, -1, "event_handler");
|
||||
|
||||
if(lua_isnil(m_engineluastack, -1))
|
||||
return;
|
||||
|
||||
luaL_checktype(m_engineluastack, -1, LUA_TFUNCTION);
|
||||
|
||||
lua_pushstring(m_engineluastack, text.c_str());
|
||||
|
||||
if(lua_pcall(m_engineluastack, 1, 0, m_luaerrorhandler))
|
||||
scriptError("error: %s", lua_tostring(m_engineluastack, -1));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::handleButtons(std::map<std::string, std::string> fields)
|
||||
{
|
||||
lua_getglobal(m_engineluastack, "engine");
|
||||
|
||||
lua_getfield(m_engineluastack, -1, "button_handler");
|
||||
|
||||
if(lua_isnil(m_engineluastack, -1))
|
||||
return;
|
||||
|
||||
luaL_checktype(m_engineluastack, -1, LUA_TFUNCTION);
|
||||
|
||||
lua_newtable(m_engineluastack);
|
||||
for(std::map<std::string, std::string>::const_iterator
|
||||
i = fields.begin(); i != fields.end(); i++){
|
||||
const std::string &name = i->first;
|
||||
const std::string &value = i->second;
|
||||
lua_pushstring(m_engineluastack, name.c_str());
|
||||
lua_pushlstring(m_engineluastack, value.c_str(), value.size());
|
||||
lua_settable(m_engineluastack, -3);
|
||||
}
|
||||
|
||||
if(lua_pcall(m_engineluastack, 1, 0, m_luaerrorhandler))
|
||||
scriptError("error: %s", lua_tostring(m_engineluastack, -1));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
GUIEngine::~GUIEngine()
|
||||
{
|
||||
video::IVideoDriver* driver = m_device->getVideoDriver();
|
||||
assert(driver != 0);
|
||||
|
||||
lua_close(m_engineluastack);
|
||||
|
||||
m_irr_toplefttext->setText(L"");
|
||||
|
||||
//initialize texture pointers
|
||||
for (unsigned int i = 0; i < TEX_LAYER_MAX; i++) {
|
||||
if (m_textures[i] != 0)
|
||||
driver->removeTexture(m_textures[i]);
|
||||
}
|
||||
|
||||
m_cloud.clouds->drop();
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::cloudInit()
|
||||
{
|
||||
m_cloud.clouds = new Clouds(m_smgr->getRootSceneNode(),
|
||||
m_smgr, -1, rand(), 100);
|
||||
m_cloud.clouds->update(v2f(0, 0), video::SColor(255,200,200,255));
|
||||
|
||||
m_cloud.camera = m_smgr->addCameraSceneNode(0,
|
||||
v3f(0,0,0), v3f(0, 60, 100));
|
||||
m_cloud.camera->setFarValue(10000);
|
||||
|
||||
m_cloud.lasttime = m_device->getTimer()->getTime();
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::cloudPreProcess()
|
||||
{
|
||||
u32 time = m_device->getTimer()->getTime();
|
||||
|
||||
if(time > m_cloud.lasttime)
|
||||
m_cloud.dtime = (time - m_cloud.lasttime) / 1000.0;
|
||||
else
|
||||
m_cloud.dtime = 0;
|
||||
|
||||
m_cloud.lasttime = time;
|
||||
|
||||
m_cloud.clouds->step(m_cloud.dtime*3);
|
||||
m_cloud.clouds->render();
|
||||
m_smgr->drawAll();
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::cloudPostProcess()
|
||||
{
|
||||
float fps_max = g_settings->getFloat("fps_max");
|
||||
// Time of frame without fps limit
|
||||
float busytime;
|
||||
u32 busytime_u32;
|
||||
// not using getRealTime is necessary for wine
|
||||
u32 time = m_device->getTimer()->getTime();
|
||||
if(time > m_cloud.lasttime)
|
||||
busytime_u32 = time - m_cloud.lasttime;
|
||||
else
|
||||
busytime_u32 = 0;
|
||||
busytime = busytime_u32 / 1000.0;
|
||||
|
||||
// FPS limiter
|
||||
u32 frametime_min = 1000./fps_max;
|
||||
|
||||
if(busytime_u32 < frametime_min) {
|
||||
u32 sleeptime = frametime_min - busytime_u32;
|
||||
m_device->sleep(sleeptime);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::drawBackground(video::IVideoDriver* driver)
|
||||
{
|
||||
v2u32 screensize = driver->getScreenSize();
|
||||
|
||||
video::ITexture* texture = m_textures[TEX_LAYER_BACKGROUND];
|
||||
|
||||
/* If no texture, draw background of solid color */
|
||||
if(!texture){
|
||||
video::SColor color(255,80,58,37);
|
||||
core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
|
||||
driver->draw2DRectangle(color, rect, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Draw background texture */
|
||||
v2u32 sourcesize = texture->getSize();
|
||||
driver->draw2DImage(texture,
|
||||
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
||||
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::drawOverlay(video::IVideoDriver* driver)
|
||||
{
|
||||
v2u32 screensize = driver->getScreenSize();
|
||||
|
||||
video::ITexture* texture = m_textures[TEX_LAYER_OVERLAY];
|
||||
|
||||
/* If no texture, draw background of solid color */
|
||||
if(!texture)
|
||||
return;
|
||||
|
||||
/* Draw background texture */
|
||||
v2u32 sourcesize = texture->getSize();
|
||||
driver->draw2DImage(texture,
|
||||
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
||||
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::drawHeader(video::IVideoDriver* driver)
|
||||
{
|
||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
||||
|
||||
video::ITexture* texture = m_textures[TEX_LAYER_HEADER];
|
||||
|
||||
/* If no texture, draw nothing */
|
||||
if(!texture)
|
||||
return;
|
||||
|
||||
f32 mult = (((f32)screensize.Width / 2)) /
|
||||
((f32)texture->getOriginalSize().Width);
|
||||
|
||||
v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
|
||||
((f32)texture->getOriginalSize().Height) * mult);
|
||||
|
||||
// Don't draw the header is there isn't enough room
|
||||
s32 free_space = (((s32)screensize.Height)-320)/2;
|
||||
|
||||
if (free_space > splashsize.Y) {
|
||||
core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
|
||||
splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
|
||||
((free_space/2)-splashsize.Y/2)+10);
|
||||
|
||||
video::SColor bgcolor(255,50,50,50);
|
||||
|
||||
driver->draw2DImage(texture, splashrect,
|
||||
core::rect<s32>(core::position2d<s32>(0,0),
|
||||
core::dimension2di(texture->getSize())),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::drawFooter(video::IVideoDriver* driver)
|
||||
{
|
||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
||||
|
||||
video::ITexture* texture = m_textures[TEX_LAYER_FOOTER];
|
||||
|
||||
/* If no texture, draw nothing */
|
||||
if(!texture)
|
||||
return;
|
||||
|
||||
f32 mult = (((f32)screensize.Width)) /
|
||||
((f32)texture->getOriginalSize().Width);
|
||||
|
||||
v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
|
||||
((f32)texture->getOriginalSize().Height) * mult);
|
||||
|
||||
// Don't draw the footer if there isn't enough room
|
||||
s32 free_space = (((s32)screensize.Height)-320)/2;
|
||||
|
||||
if (free_space > footersize.Y) {
|
||||
core::rect<s32> rect(0,0,footersize.X,footersize.Y);
|
||||
rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
|
||||
rect -= v2s32(footersize.X/2, 0);
|
||||
|
||||
driver->draw2DImage(texture, rect,
|
||||
core::rect<s32>(core::position2d<s32>(0,0),
|
||||
core::dimension2di(texture->getSize())),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
bool GUIEngine::setTexture(texture_layer layer,std::string texturepath) {
|
||||
|
||||
video::IVideoDriver* driver = m_device->getVideoDriver();
|
||||
assert(driver != 0);
|
||||
|
||||
if (m_textures[layer] != 0)
|
||||
{
|
||||
driver->removeTexture(m_textures[layer]);
|
||||
m_textures[layer] = 0;
|
||||
}
|
||||
|
||||
if ((texturepath == "") || !fs::PathExists(texturepath))
|
||||
return false;
|
||||
|
||||
m_textures[layer] = driver->getTexture(texturepath.c_str());
|
||||
|
||||
if (m_textures[layer] == 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
#if USE_CURL
|
||||
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
FILE* targetfile = (FILE*) userp;
|
||||
fwrite(contents,size,nmemb,targetfile);
|
||||
return size * nmemb;
|
||||
}
|
||||
#endif
|
||||
bool GUIEngine::downloadFile(std::string url,std::string target) {
|
||||
#if USE_CURL
|
||||
//download file via curl
|
||||
CURL *curl;
|
||||
|
||||
curl = curl_easy_init();
|
||||
|
||||
if (curl)
|
||||
{
|
||||
CURLcode res;
|
||||
bool retval = true;
|
||||
|
||||
FILE* targetfile = fopen(target.c_str(),"wb");
|
||||
|
||||
if (targetfile) {
|
||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, targetfile);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK) {
|
||||
errorstream << "File at url \"" << url
|
||||
<<"\" not found (internet connection?)" <<std::endl;
|
||||
retval = false;
|
||||
}
|
||||
fclose(targetfile);
|
||||
}
|
||||
else {
|
||||
retval = false;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::scriptError(const char *fmt, ...)
|
||||
{
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
char buf[10000];
|
||||
vsnprintf(buf, 10000, fmt, argp);
|
||||
va_end(argp);
|
||||
errorstream<<"MAINMENU ERROR: "<<buf;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::setTopleftText(std::string append) {
|
||||
std::string toset = "Minetest " VERSION_STRING;
|
||||
|
||||
if (append != "") {
|
||||
toset += " / ";
|
||||
toset += append;
|
||||
}
|
||||
|
||||
m_irr_toplefttext->setText(narrow_to_wide(toset).c_str());
|
||||
}
|
260
src/guiEngine.h
Normal file
260
src/guiEngine.h
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 sapier
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef GUI_ENGINE_H_
|
||||
#define GUI_ENGINE_H_
|
||||
|
||||
/******************************************************************************/
|
||||
/* Includes */
|
||||
/******************************************************************************/
|
||||
#include "irrlichttypes.h"
|
||||
#include "modalMenu.h"
|
||||
#include "clouds.h"
|
||||
#include "guiLuaApi.h"
|
||||
#include "guiFormSpecMenu.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/* Typedefs and macros */
|
||||
/******************************************************************************/
|
||||
#define MAX_MENUBAR_BTN_COUNT 10
|
||||
#define MAX_MENUBAR_BTN_ID 256
|
||||
#define MIN_MENUBAR_BTN_ID (MAX_MENUBAR_BTN_ID - MAX_MENUBAR_BTN_COUNT)
|
||||
|
||||
/** texture layer ids */
|
||||
typedef enum {
|
||||
TEX_LAYER_BACKGROUND = 0,
|
||||
TEX_LAYER_OVERLAY,
|
||||
TEX_LAYER_HEADER,
|
||||
TEX_LAYER_FOOTER,
|
||||
TEX_LAYER_MAX
|
||||
} texture_layer;
|
||||
|
||||
/******************************************************************************/
|
||||
/* forward declarations */
|
||||
/******************************************************************************/
|
||||
class GUIEngine;
|
||||
struct MainMenuData;
|
||||
|
||||
/******************************************************************************/
|
||||
/* declarations */
|
||||
/******************************************************************************/
|
||||
|
||||
/** GUIEngine specific implementation of TextDest used within guiFormSpecMenu */
|
||||
class TextDestGuiEngine : public TextDest
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* default constructor
|
||||
* @param engine the engine data is transmitted for further processing
|
||||
*/
|
||||
TextDestGuiEngine(GUIEngine* engine);
|
||||
/**
|
||||
* receive fields transmitted by guiFormSpecMenu
|
||||
* @param fields map containing formspec field elements currently active
|
||||
*/
|
||||
void gotText(std::map<std::string, std::string> fields);
|
||||
|
||||
/**
|
||||
* receive text/events transmitted by guiFormSpecMenu
|
||||
* @param text textual representation of event
|
||||
*/
|
||||
void gotText(std::wstring text);
|
||||
private:
|
||||
/** target to transmit data to */
|
||||
GUIEngine* m_engine;
|
||||
};
|
||||
|
||||
|
||||
/** implementation of main menu based uppon formspecs */
|
||||
class GUIEngine {
|
||||
public:
|
||||
/** TextDestGuiEngine needs to transfer data to engine */
|
||||
friend class TextDestGuiEngine;
|
||||
/** guiLuaApi is used to initialize contained stack */
|
||||
friend class guiLuaApi;
|
||||
|
||||
/**
|
||||
* default constructor
|
||||
* @param dev device to draw at
|
||||
* @param parent parent gui element
|
||||
* @param menumgr manager to add menus to
|
||||
* @param smgr scene manager to add scene elements to
|
||||
* @param data struct to transfer data to main game handling
|
||||
*/
|
||||
GUIEngine( irr::IrrlichtDevice* dev,
|
||||
gui::IGUIElement* parent,
|
||||
IMenuManager *menumgr,
|
||||
scene::ISceneManager* smgr,
|
||||
MainMenuData* data);
|
||||
|
||||
/** default destructor */
|
||||
virtual ~GUIEngine();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* process field data recieved from formspec
|
||||
* @param fields data in field format
|
||||
*/
|
||||
void handleButtons(std::map<std::string, std::string> fields);
|
||||
/**
|
||||
* process events received from formspec
|
||||
* @param text events in textual form
|
||||
*/
|
||||
void handleEvent(std::string text);
|
||||
|
||||
/**
|
||||
* return dir of current menuscript
|
||||
*/
|
||||
std::string getScriptDir() {
|
||||
return m_scriptdir;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/* run main menu loop */
|
||||
void run();
|
||||
|
||||
/** handler to limit frame rate within main menu */
|
||||
void limitFrameRate();
|
||||
|
||||
/** device to draw at */
|
||||
irr::IrrlichtDevice* m_device;
|
||||
/** parent gui element */
|
||||
gui::IGUIElement* m_parent;
|
||||
/** manager to add menus to */
|
||||
IMenuManager* m_menumanager;
|
||||
/** scene manager to add scene elements to */
|
||||
scene::ISceneManager* m_smgr;
|
||||
/** pointer to data beeing transfered back to main game handling */
|
||||
MainMenuData* m_data;
|
||||
|
||||
/** representation of form source to be used in mainmenu formspec */
|
||||
FormspecFormSource* m_formspecgui;
|
||||
/** formspec input receiver */
|
||||
TextDestGuiEngine* m_buttonhandler;
|
||||
/** the formspec menu */
|
||||
GUIFormSpecMenu* m_menu;
|
||||
|
||||
/** variable used to abort menu and return back to main game handling */
|
||||
bool m_startgame;
|
||||
|
||||
/**
|
||||
* initialize lua stack
|
||||
* @param L stack to initialize
|
||||
*/
|
||||
void initalize_api(lua_State * L);
|
||||
|
||||
/**
|
||||
* run a lua script
|
||||
* @param script full path to script to run
|
||||
*/
|
||||
bool runScript(std::string script);
|
||||
|
||||
/**
|
||||
* script error handler to process errors within lua
|
||||
*/
|
||||
void scriptError(const char *fmt, ...);
|
||||
|
||||
/** lua stack */
|
||||
lua_State* m_engineluastack;
|
||||
/** lua internal stack number of error handler*/
|
||||
int m_luaerrorhandler;
|
||||
|
||||
/** script basefolder */
|
||||
std::string m_scriptdir;
|
||||
|
||||
/**
|
||||
* draw background layer
|
||||
* @param driver to use for drawing
|
||||
*/
|
||||
void drawBackground(video::IVideoDriver* driver);
|
||||
/**
|
||||
* draw overlay layer
|
||||
* @param driver to use for drawing
|
||||
*/
|
||||
void drawOverlay(video::IVideoDriver* driver);
|
||||
/**
|
||||
* draw header layer
|
||||
* @param driver to use for drawing
|
||||
*/
|
||||
void drawHeader(video::IVideoDriver* driver);
|
||||
/**
|
||||
* draw footer layer
|
||||
* @param driver to use for drawing
|
||||
*/
|
||||
void drawFooter(video::IVideoDriver* driver);
|
||||
|
||||
/**
|
||||
* load a texture for a specified layer
|
||||
* @param layer draw layer to specify texture
|
||||
* @param texturepath full path of texture to load
|
||||
*/
|
||||
bool setTexture(texture_layer layer,std::string texturepath);
|
||||
|
||||
/**
|
||||
* download a file using curl
|
||||
* @param url url to download
|
||||
* @param target file to store to
|
||||
*/
|
||||
bool downloadFile(std::string url,std::string target);
|
||||
|
||||
/** array containing pointers to current specified texture layers */
|
||||
video::ITexture* m_textures[TEX_LAYER_MAX];
|
||||
|
||||
/** draw version string in topleft corner */
|
||||
void drawVersion();
|
||||
|
||||
/**
|
||||
* specify text to be appended to version string
|
||||
* @param text to set
|
||||
*/
|
||||
void setTopleftText(std::string append);
|
||||
|
||||
/** pointer to gui element shown at topleft corner */
|
||||
irr::gui::IGUIStaticText* m_irr_toplefttext;
|
||||
|
||||
/** initialize cloud subsystem */
|
||||
void cloudInit();
|
||||
/** do preprocessing for cloud subsystem */
|
||||
void cloudPreProcess();
|
||||
/** do postprocessing for cloud subsystem */
|
||||
void cloudPostProcess();
|
||||
|
||||
/** internam data required for drawing clouds */
|
||||
struct clouddata {
|
||||
/** delta time since last cloud processing */
|
||||
f32 dtime;
|
||||
/** absolute time of last cloud processing */
|
||||
u32 lasttime;
|
||||
/** pointer to cloud class */
|
||||
Clouds* clouds;
|
||||
/** camera required for drawing clouds */
|
||||
scene::ICameraSceneNode* camera;
|
||||
};
|
||||
|
||||
/** is drawing of clouds enabled atm */
|
||||
bool m_clouds_enabled;
|
||||
/** data used to draw clouds */
|
||||
clouddata m_cloud;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* GUI_ENGINE_H_ */
|
133
src/guiFileSelectMenu.cpp
Normal file
133
src/guiFileSelectMenu.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 sapier
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "guiFileSelectMenu.h"
|
||||
#include "util/string.h"
|
||||
#include <locale.h>
|
||||
|
||||
GUIFileSelectMenu::GUIFileSelectMenu(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
|
||||
std::string title, std::string formname) :
|
||||
GUIModalMenu(env, parent, id, menumgr)
|
||||
{
|
||||
m_title = narrow_to_wide(title);
|
||||
m_parent = parent;
|
||||
m_formname = formname;
|
||||
m_text_dst = 0;
|
||||
m_accepted = false;
|
||||
m_previous_locale = setlocale(LC_ALL,0);
|
||||
}
|
||||
|
||||
GUIFileSelectMenu::~GUIFileSelectMenu()
|
||||
{
|
||||
removeChildren();
|
||||
setlocale(LC_ALL,m_previous_locale.c_str());
|
||||
}
|
||||
|
||||
void GUIFileSelectMenu::removeChildren()
|
||||
{
|
||||
const core::list<gui::IGUIElement*> &children = getChildren();
|
||||
core::list<gui::IGUIElement*> children_copy;
|
||||
for (core::list<gui::IGUIElement*>::ConstIterator i = children.begin(); i
|
||||
!= children.end(); i++)
|
||||
{
|
||||
children_copy.push_back(*i);
|
||||
}
|
||||
for (core::list<gui::IGUIElement*>::Iterator i = children_copy.begin(); i
|
||||
!= children_copy.end(); i++)
|
||||
{
|
||||
(*i)->remove();
|
||||
}
|
||||
}
|
||||
|
||||
void GUIFileSelectMenu::regenerateGui(v2u32 screensize)
|
||||
{
|
||||
removeChildren();
|
||||
m_fileOpenDialog = 0;
|
||||
|
||||
core::dimension2du size(600,400);
|
||||
core::rect < s32 > rect(0,0,screensize.X,screensize.Y);
|
||||
|
||||
DesiredRect = rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
|
||||
m_fileOpenDialog =
|
||||
Environment->addFileOpenDialog(m_title.c_str(),false,this,-1);
|
||||
|
||||
core::position2di pos = core::position2di(screensize.X/2 - size.Width/2,screensize.Y/2 -size.Height/2);
|
||||
m_fileOpenDialog->setRelativePosition(pos);
|
||||
m_fileOpenDialog->setMinSize(size);
|
||||
}
|
||||
|
||||
void GUIFileSelectMenu::drawMenu()
|
||||
{
|
||||
gui::IGUISkin* skin = Environment->getSkin();
|
||||
if (!skin)
|
||||
return;
|
||||
|
||||
gui::IGUIElement::draw();
|
||||
}
|
||||
|
||||
void GUIFileSelectMenu::acceptInput() {
|
||||
if ((m_text_dst != 0) && (this->m_formname != "")){
|
||||
std::map<std::string, std::string> fields;
|
||||
|
||||
if (m_accepted)
|
||||
fields[m_formname + "_accepted"] = wide_to_narrow(m_fileOpenDialog->getFileName());
|
||||
else
|
||||
fields[m_formname + "_canceled"] = m_formname;
|
||||
|
||||
this->m_text_dst->gotText(fields);
|
||||
}
|
||||
}
|
||||
|
||||
bool GUIFileSelectMenu::OnEvent(const SEvent& event)
|
||||
{
|
||||
|
||||
if (event.EventType == irr::EET_GUI_EVENT) {
|
||||
|
||||
int callerId = event.GUIEvent.Caller->getID();
|
||||
if (callerId >= 0) {
|
||||
std::cout << "CallerId:" << callerId << std::endl;
|
||||
}
|
||||
|
||||
switch (event.GUIEvent.EventType) {
|
||||
case gui::EGET_ELEMENT_CLOSED:
|
||||
case gui::EGET_FILE_CHOOSE_DIALOG_CANCELLED:
|
||||
m_accepted=false;
|
||||
acceptInput();
|
||||
quitMenu();
|
||||
return true;
|
||||
break;
|
||||
|
||||
case gui::EGET_DIRECTORY_SELECTED:
|
||||
case gui::EGET_FILE_SELECTED:
|
||||
m_accepted=true;
|
||||
acceptInput();
|
||||
quitMenu();
|
||||
return true;
|
||||
break;
|
||||
|
||||
default:
|
||||
//ignore this event
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Parent ? Parent->OnEvent(event) : false;
|
||||
}
|
80
src/guiFileSelectMenu.h
Normal file
80
src/guiFileSelectMenu.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 sapier
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef GUIFILESELECTMENU_H_
|
||||
#define GUIFILESELECTMENU_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "modalMenu.h"
|
||||
#include "IGUIFileOpenDialog.h"
|
||||
#include "guiFormSpecMenu.h" //required because of TextDest only !!!
|
||||
|
||||
|
||||
class GUIFileSelectMenu: public GUIModalMenu
|
||||
{
|
||||
public:
|
||||
GUIFileSelectMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
||||
s32 id, IMenuManager *menumgr,
|
||||
std::string title,
|
||||
std::string formid);
|
||||
~GUIFileSelectMenu();
|
||||
|
||||
void removeChildren();
|
||||
|
||||
/*
|
||||
Remove and re-add (or reposition) stuff
|
||||
*/
|
||||
void regenerateGui(v2u32 screensize);
|
||||
|
||||
void drawMenu();
|
||||
|
||||
bool OnEvent(const SEvent& event);
|
||||
|
||||
bool isRunning() {
|
||||
return m_running;
|
||||
}
|
||||
|
||||
void setTextDest(TextDest * dest) {
|
||||
m_text_dst = dest;
|
||||
}
|
||||
|
||||
private:
|
||||
void acceptInput();
|
||||
|
||||
std::wstring m_title;
|
||||
bool m_accepted;
|
||||
gui::IGUIElement* m_parent;
|
||||
|
||||
std::string m_selectedPath;
|
||||
|
||||
gui::IGUIFileOpenDialog* m_fileOpenDialog;
|
||||
|
||||
std::string m_previous_locale;
|
||||
|
||||
bool m_running;
|
||||
|
||||
TextDest *m_text_dst;
|
||||
|
||||
std::string m_formname;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* GUIFILESELECTMENU_H_ */
|
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#ifndef GUIINVENTORYMENU_HEADER
|
||||
#define GUIINVENTORYMENU_HEADER
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "inventory.h"
|
||||
#include "inventorymanager.h"
|
||||
@ -29,6 +31,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
class IGameDef;
|
||||
class InventoryManager;
|
||||
|
||||
typedef enum {
|
||||
f_Button,
|
||||
f_ListBox,
|
||||
f_TabHeader,
|
||||
f_CheckBox,
|
||||
f_DropDown,
|
||||
f_Unknown
|
||||
} FormspecFieldType;
|
||||
|
||||
struct TextDest
|
||||
{
|
||||
virtual ~TextDest() {};
|
||||
@ -113,10 +124,19 @@ class GUIFormSpecMenu : public GUIModalMenu
|
||||
pos(a_pos),
|
||||
geom(a_geom)
|
||||
{
|
||||
scale = true;
|
||||
}
|
||||
ImageDrawSpec(const std::string &a_name,
|
||||
v2s32 a_pos):
|
||||
name(a_name),
|
||||
pos(a_pos)
|
||||
{
|
||||
scale = false;
|
||||
}
|
||||
std::string name;
|
||||
v2s32 pos;
|
||||
v2s32 geom;
|
||||
bool scale;
|
||||
};
|
||||
|
||||
struct FieldSpec
|
||||
@ -131,7 +151,7 @@ class GUIFormSpecMenu : public GUIModalMenu
|
||||
fid(id)
|
||||
{
|
||||
send = false;
|
||||
is_button = false;
|
||||
ftype = f_Unknown;
|
||||
is_exit = false;
|
||||
tooltip="";
|
||||
}
|
||||
@ -140,12 +160,24 @@ class GUIFormSpecMenu : public GUIModalMenu
|
||||
std::wstring fdefault;
|
||||
int fid;
|
||||
bool send;
|
||||
bool is_button;
|
||||
FormspecFieldType ftype;
|
||||
bool is_exit;
|
||||
core::rect<s32> rect;
|
||||
std::string tooltip;
|
||||
};
|
||||
|
||||
struct BoxDrawSpec {
|
||||
BoxDrawSpec(v2s32 a_pos, v2s32 a_geom,irr::video::SColor a_color):
|
||||
pos(a_pos),
|
||||
geom(a_geom),
|
||||
color(a_color)
|
||||
{
|
||||
}
|
||||
v2s32 pos;
|
||||
v2s32 geom;
|
||||
irr::video::SColor color;
|
||||
};
|
||||
|
||||
public:
|
||||
GUIFormSpecMenu(irr::IrrlichtDevice* dev,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
@ -153,6 +185,7 @@ public:
|
||||
InventoryManager *invmgr,
|
||||
IGameDef *gamedef
|
||||
);
|
||||
|
||||
~GUIFormSpecMenu();
|
||||
|
||||
void setFormSpec(const std::string &formspec_string,
|
||||
@ -175,6 +208,20 @@ public:
|
||||
m_text_dst = text_dst;
|
||||
}
|
||||
|
||||
void allowClose(bool value)
|
||||
{
|
||||
m_allowclose = value;
|
||||
}
|
||||
|
||||
void useGettext(bool value) {
|
||||
m_use_gettext = true;
|
||||
}
|
||||
|
||||
void lockSize(bool lock,v2u32 basescreensize=v2u32(0,0)) {
|
||||
m_lock = lock;
|
||||
m_lockscreensize = basescreensize;
|
||||
}
|
||||
|
||||
void removeChildren();
|
||||
/*
|
||||
Remove and re-add (or reposition) stuff
|
||||
@ -188,18 +235,21 @@ public:
|
||||
void updateSelectedItem();
|
||||
ItemStack verifySelectedItem();
|
||||
|
||||
void acceptInput();
|
||||
void acceptInput(int evttype=-1);
|
||||
bool OnEvent(const SEvent& event);
|
||||
|
||||
int getListboxIndex(std::string listboxname);
|
||||
|
||||
protected:
|
||||
v2s32 getBasePos() const
|
||||
{
|
||||
return padding + AbsoluteRect.UpperLeftCorner;
|
||||
return padding + offset + AbsoluteRect.UpperLeftCorner;
|
||||
}
|
||||
|
||||
v2s32 padding;
|
||||
v2s32 spacing;
|
||||
v2s32 imgsize;
|
||||
v2s32 offset;
|
||||
|
||||
irr::IrrlichtDevice* m_device;
|
||||
InventoryManager *m_invmgr;
|
||||
@ -214,7 +264,10 @@ protected:
|
||||
std::vector<ImageDrawSpec> m_backgrounds;
|
||||
std::vector<ImageDrawSpec> m_images;
|
||||
std::vector<ImageDrawSpec> m_itemimages;
|
||||
std::vector<BoxDrawSpec> m_boxes;
|
||||
std::vector<FieldSpec> m_fields;
|
||||
std::vector<std::pair<FieldSpec,gui::IGUIListBox*> > m_listboxes;
|
||||
std::vector<std::pair<FieldSpec,gui::IGUICheckBox*> > m_checkboxes;
|
||||
|
||||
ItemSpec *m_selected_item;
|
||||
u32 m_selected_amount;
|
||||
@ -228,6 +281,74 @@ protected:
|
||||
|
||||
v2s32 m_pointer;
|
||||
gui::IGUIStaticText *m_tooltip_element;
|
||||
|
||||
bool m_allowclose;
|
||||
bool m_use_gettext;
|
||||
bool m_lock;
|
||||
v2u32 m_lockscreensize;
|
||||
private:
|
||||
typedef struct {
|
||||
v2s32 size;
|
||||
s32 helptext_h;
|
||||
core::rect<s32> rect;
|
||||
v2s32 basepos;
|
||||
int bp_set;
|
||||
v2u32 screensize;
|
||||
std::map<std::wstring,int> listbox_selections;
|
||||
} parserData;
|
||||
|
||||
std::vector<video::ITexture *> m_Textures;
|
||||
|
||||
void parseElement(parserData* data,std::string element);
|
||||
|
||||
void parseSize(parserData* data,std::string element);
|
||||
void parseList(parserData* data,std::string element);
|
||||
void parseCheckbox(parserData* data,std::string element);
|
||||
void parseImage(parserData* data,std::string element);
|
||||
void parseItemImage(parserData* data,std::string element);
|
||||
void parseButton(parserData* data,std::string element,std::string typ);
|
||||
void parseBackground(parserData* data,std::string element);
|
||||
void parseTextList(parserData* data,std::string element);
|
||||
void parseDropDown(parserData* data,std::string element);
|
||||
void parsePwdField(parserData* data,std::string element);
|
||||
void parseField(parserData* data,std::string element,std::string type);
|
||||
void parseSimpleField(parserData* data,std::vector<std::string> &parts);
|
||||
void parseTextArea(parserData* data,std::vector<std::string>& parts,std::string type);
|
||||
void parseLabel(parserData* data,std::string element);
|
||||
void parseVertLabel(parserData* data,std::string element);
|
||||
void parseImageButton(parserData* data,std::string element,std::string type);
|
||||
void parseItemImageButton(parserData* data,std::string element);
|
||||
void parseTabHeader(parserData* data,std::string element);
|
||||
void parseBox(parserData* data,std::string element);
|
||||
|
||||
irr::video::SColor getColor(std::string color,bool& valid_color);
|
||||
};
|
||||
|
||||
class FormspecFormSource: public IFormSource
|
||||
{
|
||||
public:
|
||||
FormspecFormSource(std::string formspec,FormspecFormSource** game_formspec)
|
||||
{
|
||||
m_formspec = formspec;
|
||||
m_game_formspec = game_formspec;
|
||||
}
|
||||
|
||||
~FormspecFormSource()
|
||||
{
|
||||
*m_game_formspec = 0;
|
||||
}
|
||||
|
||||
void setForm(std::string formspec) {
|
||||
m_formspec = formspec;
|
||||
}
|
||||
|
||||
std::string getForm()
|
||||
{
|
||||
return m_formspec;
|
||||
}
|
||||
|
||||
std::string m_formspec;
|
||||
FormspecFormSource** m_game_formspec;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
1067
src/guiLuaApi.cpp
Normal file
1067
src/guiLuaApi.cpp
Normal file
File diff suppressed because it is too large
Load Diff
183
src/guiLuaApi.h
Normal file
183
src/guiLuaApi.h
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2013 sapier
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef GUILUAAPI_H_
|
||||
#define GUILUAAPI_H_
|
||||
|
||||
/******************************************************************************/
|
||||
/* Includes */
|
||||
/******************************************************************************/
|
||||
#include "serverlist.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/* Typedefs and macros */
|
||||
/******************************************************************************/
|
||||
typedef int (*lua_CFunction) (lua_State *L);
|
||||
|
||||
/******************************************************************************/
|
||||
/* forward declarations */
|
||||
/******************************************************************************/
|
||||
class GUIEngine;
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
/* declarations */
|
||||
/******************************************************************************/
|
||||
|
||||
/** Implementation of lua api support for mainmenu */
|
||||
class guiLuaApi {
|
||||
|
||||
public:
|
||||
/**
|
||||
* initialize given Lua stack
|
||||
* @param L lua stack to initialize
|
||||
* @param engine pointer to GUIEngine element to use as reference
|
||||
*/
|
||||
static void initialize(lua_State* L,GUIEngine* engine);
|
||||
|
||||
/** default destructor */
|
||||
virtual ~guiLuaApi() {}
|
||||
|
||||
private:
|
||||
/**
|
||||
* read a text variable from gamedata table within lua stack
|
||||
* @param L stack to read variable from
|
||||
* @param name name of variable to read
|
||||
* @return string value of requested variable
|
||||
*/
|
||||
static std::string getTextData(lua_State *L, std::string name);
|
||||
|
||||
/**
|
||||
* read a integer variable from gamedata table within lua stack
|
||||
* @param L stack to read variable from
|
||||
* @param name name of variable to read
|
||||
* @return integer value of requested variable
|
||||
*/
|
||||
static int getIntegerData(lua_State *L, std::string name,bool& valid);
|
||||
|
||||
/**
|
||||
* read a bool variable from gamedata table within lua stack
|
||||
* @param L stack to read variable from
|
||||
* @param name name of variable to read
|
||||
* @return bool value of requested variable
|
||||
*/
|
||||
static int getBoolData(lua_State *L, std::string name,bool& valid);
|
||||
|
||||
/**
|
||||
* get the corresponding engine pointer from a lua stack
|
||||
* @param L stack to read pointer from
|
||||
* @return pointer to GUIEngine
|
||||
*/
|
||||
static GUIEngine* get_engine(lua_State *L);
|
||||
|
||||
|
||||
/**
|
||||
* register a static member function as lua api call at current position of stack
|
||||
* @param L stack to registe fct to
|
||||
* @param name of function within lua
|
||||
* @param fct C-Function to call on lua call of function
|
||||
* @param top current top of stack
|
||||
*/
|
||||
static bool registerFunction( lua_State* L,
|
||||
const char* name,
|
||||
lua_CFunction fct,
|
||||
int top
|
||||
);
|
||||
|
||||
/**
|
||||
* check if a path is within some of minetests folders
|
||||
* @param path path to check
|
||||
* @return true/false
|
||||
*/
|
||||
static bool isMinetestPath(std::string path);
|
||||
|
||||
//api calls
|
||||
|
||||
static int l_start(lua_State *L);
|
||||
|
||||
static int l_close(lua_State *L);
|
||||
|
||||
static int l_create_world(lua_State *L);
|
||||
|
||||
static int l_delete_world(lua_State *L);
|
||||
|
||||
static int l_get_worlds(lua_State *L);
|
||||
|
||||
static int l_get_games(lua_State *L);
|
||||
|
||||
static int l_get_favorites(lua_State *L);
|
||||
|
||||
static int l_delete_favorite(lua_State *L);
|
||||
|
||||
static int l_get_version(lua_State *L);
|
||||
|
||||
//gui
|
||||
|
||||
static int l_show_keys_menu(lua_State *L);
|
||||
|
||||
static int l_show_file_open_dialog(lua_State *L);
|
||||
|
||||
static int l_set_topleft_text(lua_State *L);
|
||||
|
||||
static int l_set_clouds(lua_State *L);
|
||||
|
||||
static int l_get_textlist_index(lua_State *L);
|
||||
|
||||
static int l_set_background(lua_State *L);
|
||||
|
||||
static int l_update_formspec(lua_State *L);
|
||||
|
||||
//settings
|
||||
|
||||
static int l_setting_set(lua_State *L);
|
||||
|
||||
static int l_setting_get(lua_State *L);
|
||||
|
||||
static int l_setting_getbool(lua_State *L);
|
||||
|
||||
static int l_setting_setbool(lua_State *L);
|
||||
|
||||
//filesystem
|
||||
|
||||
static int l_get_scriptdir(lua_State *L);
|
||||
|
||||
static int l_get_modpath(lua_State *L);
|
||||
|
||||
static int l_get_gamepath(lua_State *L);
|
||||
|
||||
static int l_get_dirlist(lua_State *L);
|
||||
|
||||
static int l_create_dir(lua_State *L);
|
||||
|
||||
static int l_delete_dir(lua_State *L);
|
||||
|
||||
static int l_copy_dir(lua_State *L);
|
||||
|
||||
static int l_extract_zip(lua_State *L);
|
||||
|
||||
static int l_get_modstore_details(lua_State *L);
|
||||
|
||||
static int l_get_modstore_list(lua_State *L);
|
||||
|
||||
static int l_download_file(lua_State *L);
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
1521
src/guiMainMenu.cpp
1521
src/guiMainMenu.cpp
File diff suppressed because it is too large
Load Diff
@ -24,15 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "modalMenu.h"
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include "subgame.h"
|
||||
#include "serverlist.h"
|
||||
|
||||
class IGameCallback;
|
||||
|
||||
enum {
|
||||
SERVERLIST_FAVORITES,
|
||||
SERVERLIST_PUBLIC,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
@ -45,113 +36,31 @@ enum
|
||||
|
||||
struct MainMenuData
|
||||
{
|
||||
// These are in the native format of the gui elements
|
||||
// Generic
|
||||
int selected_tab;
|
||||
std::string selected_game;
|
||||
std::string selected_game_name;
|
||||
// Client options
|
||||
std::string servername;
|
||||
std::string serverdescription;
|
||||
std::wstring address;
|
||||
std::wstring port;
|
||||
std::wstring name;
|
||||
std::wstring password;
|
||||
bool fancy_trees;
|
||||
bool smooth_lighting;
|
||||
bool clouds_3d;
|
||||
bool opaque_water;
|
||||
bool mip_map;
|
||||
bool anisotropic_filter;
|
||||
bool bilinear_filter;
|
||||
bool trilinear_filter;
|
||||
int enable_shaders;
|
||||
bool preload_item_visuals;
|
||||
bool enable_particles;
|
||||
bool liquid_finite;
|
||||
std::string address;
|
||||
std::string port;
|
||||
std::string name;
|
||||
std::string password;
|
||||
|
||||
// Server options
|
||||
bool creative_mode;
|
||||
bool enable_damage;
|
||||
bool enable_public;
|
||||
int selected_world;
|
||||
bool simple_singleplayer_mode;
|
||||
|
||||
// Actions
|
||||
std::wstring create_world_name;
|
||||
std::string create_world_gameid;
|
||||
bool only_refresh;
|
||||
|
||||
int selected_serverlist;
|
||||
|
||||
std::vector<WorldSpec> worlds;
|
||||
std::vector<SubgameSpec> games;
|
||||
std::vector<ServerListSpec> servers;
|
||||
bool kill;
|
||||
|
||||
//error handling
|
||||
std::string errormessage;
|
||||
MainMenuData():
|
||||
// Generic
|
||||
selected_tab(0),
|
||||
selected_game("minetest"),
|
||||
selected_game_name("Minetest"),
|
||||
// Client opts
|
||||
fancy_trees(false),
|
||||
smooth_lighting(false),
|
||||
// Server opts
|
||||
creative_mode(false),
|
||||
enable_damage(false),
|
||||
enable_public(false),
|
||||
selected_world(0),
|
||||
simple_singleplayer_mode(false),
|
||||
// Actions
|
||||
only_refresh(false),
|
||||
|
||||
selected_serverlist(SERVERLIST_FAVORITES)
|
||||
errormessage("")
|
||||
{}
|
||||
};
|
||||
|
||||
class GUIMainMenu : public GUIModalMenu
|
||||
{
|
||||
public:
|
||||
GUIMainMenu(gui::IGUIEnvironment* env,
|
||||
gui::IGUIElement* parent, s32 id,
|
||||
IMenuManager *menumgr,
|
||||
MainMenuData *data,
|
||||
IGameCallback *gamecallback);
|
||||
~GUIMainMenu();
|
||||
|
||||
void removeChildren();
|
||||
// Remove and re-add (or reposition) stuff
|
||||
void regenerateGui(v2u32 screensize);
|
||||
void drawMenu();
|
||||
void readInput(MainMenuData *dst);
|
||||
void acceptInput();
|
||||
bool getStatus()
|
||||
{ return m_accepted; }
|
||||
bool OnEvent(const SEvent& event);
|
||||
void createNewWorld(std::wstring name, std::string gameid);
|
||||
void deleteWorld(const std::vector<std::string> &paths);
|
||||
int getTab();
|
||||
void displayMessageMenu(std::wstring msg);
|
||||
|
||||
private:
|
||||
MainMenuData *m_data;
|
||||
bool m_accepted;
|
||||
IGameCallback *m_gamecallback;
|
||||
|
||||
gui::IGUIEnvironment* env;
|
||||
gui::IGUIElement* parent;
|
||||
s32 id;
|
||||
IMenuManager *menumgr;
|
||||
|
||||
std::vector<int> m_world_indices;
|
||||
|
||||
bool m_is_regenerating;
|
||||
v2s32 m_topleft_client;
|
||||
v2s32 m_size_client;
|
||||
v2s32 m_topleft_server;
|
||||
v2s32 m_size_server;
|
||||
void updateGuiServerList();
|
||||
void serverListOnSelected();
|
||||
ServerListSpec getServerListSpec(std::string address, std::string port);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
463
src/main.cpp
463
src/main.cpp
@ -78,6 +78,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "serverlist.h"
|
||||
#include "sound.h"
|
||||
#include "sound_openal.h"
|
||||
#include "guiEngine.h"
|
||||
|
||||
/*
|
||||
Settings.
|
||||
@ -660,180 +661,6 @@ private:
|
||||
bool rightreleased;
|
||||
};
|
||||
|
||||
struct MenuTextures
|
||||
{
|
||||
std::string current_gameid;
|
||||
bool global_textures;
|
||||
video::ITexture *background;
|
||||
video::ITexture *overlay;
|
||||
video::ITexture *header;
|
||||
video::ITexture *footer;
|
||||
|
||||
MenuTextures():
|
||||
global_textures(false),
|
||||
background(NULL),
|
||||
overlay(NULL),
|
||||
header(NULL),
|
||||
footer(NULL)
|
||||
{}
|
||||
|
||||
static video::ITexture* getMenuTexture(const std::string &tname,
|
||||
video::IVideoDriver* driver, const SubgameSpec *spec)
|
||||
{
|
||||
if(spec){
|
||||
std::string path;
|
||||
// eg. minetest_menu_background.png (for texture packs)
|
||||
std::string pack_tname = spec->id + "_menu_" + tname + ".png";
|
||||
path = getTexturePath(pack_tname);
|
||||
if(path != "")
|
||||
return driver->getTexture(path.c_str());
|
||||
// eg. games/minetest_game/menu/background.png
|
||||
path = getImagePath(spec->path + DIR_DELIM + "menu" + DIR_DELIM + tname + ".png");
|
||||
if(path != "")
|
||||
return driver->getTexture(path.c_str());
|
||||
} else {
|
||||
std::string path;
|
||||
// eg. menu_background.png
|
||||
std::string pack_tname = "menu_" + tname + ".png";
|
||||
path = getTexturePath(pack_tname);
|
||||
if(path != "")
|
||||
return driver->getTexture(path.c_str());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void update(video::IVideoDriver* driver, const SubgameSpec *spec, int tab)
|
||||
{
|
||||
if(tab == TAB_SINGLEPLAYER){
|
||||
if(spec->id == current_gameid)
|
||||
return;
|
||||
current_gameid = spec->id;
|
||||
global_textures = false;
|
||||
background = getMenuTexture("background", driver, spec);
|
||||
overlay = getMenuTexture("overlay", driver, spec);
|
||||
header = getMenuTexture("header", driver, spec);
|
||||
footer = getMenuTexture("footer", driver, spec);
|
||||
} else {
|
||||
if(global_textures)
|
||||
return;
|
||||
current_gameid = "";
|
||||
global_textures = true;
|
||||
background = getMenuTexture("background", driver, NULL);
|
||||
overlay = getMenuTexture("overlay", driver, NULL);
|
||||
header = getMenuTexture("header", driver, NULL);
|
||||
footer = getMenuTexture("footer", driver, NULL);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void drawMenuBackground(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||
{
|
||||
v2u32 screensize = driver->getScreenSize();
|
||||
video::ITexture *texture = menutextures.background;
|
||||
|
||||
/* If no texture, draw background of solid color */
|
||||
if(!texture){
|
||||
video::SColor color(255,80,58,37);
|
||||
core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
|
||||
driver->draw2DRectangle(color, rect, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Draw background texture */
|
||||
v2u32 sourcesize = texture->getSize();
|
||||
driver->draw2DImage(texture,
|
||||
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
||||
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
|
||||
void drawMenuOverlay(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||
{
|
||||
v2u32 screensize = driver->getScreenSize();
|
||||
video::ITexture *texture = menutextures.overlay;
|
||||
|
||||
/* If no texture, draw nothing */
|
||||
if(!texture)
|
||||
return;
|
||||
|
||||
/* Draw overlay texture */
|
||||
v2u32 sourcesize = texture->getSize();
|
||||
driver->draw2DImage(texture,
|
||||
core::rect<s32>(0, 0, screensize.X, screensize.Y),
|
||||
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
|
||||
void drawMenuHeader(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||
{
|
||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
||||
video::ITexture *texture = menutextures.header;
|
||||
|
||||
/* If no texture, draw nothing */
|
||||
if(!texture)
|
||||
return;
|
||||
|
||||
f32 mult = (((f32)screensize.Width / 2)) /
|
||||
((f32)texture->getOriginalSize().Width);
|
||||
|
||||
v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
|
||||
((f32)texture->getOriginalSize().Height) * mult);
|
||||
|
||||
// Don't draw the header is there isn't enough room
|
||||
s32 free_space = (((s32)screensize.Height)-320)/2;
|
||||
if (free_space > splashsize.Y) {
|
||||
core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
|
||||
splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
|
||||
((free_space/2)-splashsize.Y/2)+10);
|
||||
|
||||
video::SColor bgcolor(255,50,50,50);
|
||||
|
||||
driver->draw2DImage(texture, splashrect,
|
||||
core::rect<s32>(core::position2d<s32>(0,0),
|
||||
core::dimension2di(texture->getSize())),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
}
|
||||
|
||||
void drawMenuFooter(video::IVideoDriver* driver, const MenuTextures &menutextures)
|
||||
{
|
||||
core::dimension2d<u32> screensize = driver->getScreenSize();
|
||||
video::ITexture *texture = menutextures.footer;
|
||||
|
||||
/* If no texture, draw nothing */
|
||||
if(!texture)
|
||||
return;
|
||||
|
||||
f32 mult = (((f32)screensize.Width)) /
|
||||
((f32)texture->getOriginalSize().Width);
|
||||
|
||||
v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
|
||||
((f32)texture->getOriginalSize().Height) * mult);
|
||||
|
||||
// Don't draw the footer if there isn't enough room
|
||||
s32 free_space = (((s32)screensize.Height)-320)/2;
|
||||
if (free_space > footersize.Y) {
|
||||
core::rect<s32> rect(0,0,footersize.X,footersize.Y);
|
||||
rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
|
||||
rect -= v2s32(footersize.X/2, 0);
|
||||
|
||||
driver->draw2DImage(texture, rect,
|
||||
core::rect<s32>(core::position2d<s32>(0,0),
|
||||
core::dimension2di(texture->getSize())),
|
||||
NULL, NULL, true);
|
||||
}
|
||||
}
|
||||
|
||||
static const SubgameSpec* getMenuGame(const MainMenuData &menudata)
|
||||
{
|
||||
for(size_t i=0; i<menudata.games.size(); i++){
|
||||
if(menudata.games[i].id == menudata.selected_game){
|
||||
return &menudata.games[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // !SERVER
|
||||
|
||||
// These are defined global so that they're not optimized too much.
|
||||
@ -1730,83 +1557,37 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Initialize menu data
|
||||
MainMenuData menudata;
|
||||
if(g_settings->exists("selected_mainmenu_tab"))
|
||||
menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab");
|
||||
if(g_settings->exists("selected_serverlist"))
|
||||
menudata.selected_serverlist = g_settings->getS32("selected_serverlist");
|
||||
if(g_settings->exists("selected_mainmenu_game")){
|
||||
menudata.selected_game = g_settings->get("selected_mainmenu_game");
|
||||
menudata.selected_game_name = findSubgame(menudata.selected_game).name;
|
||||
}
|
||||
menudata.address = narrow_to_wide(address);
|
||||
menudata.name = narrow_to_wide(playername);
|
||||
menudata.port = narrow_to_wide(itos(port));
|
||||
menudata.kill = kill;
|
||||
menudata.address = address;
|
||||
menudata.name = playername;
|
||||
menudata.port = itos(port);
|
||||
menudata.errormessage = wide_to_narrow(error_message);
|
||||
error_message = L"";
|
||||
if(cmd_args.exists("password"))
|
||||
menudata.password = narrow_to_wide(cmd_args.get("password"));
|
||||
menudata.fancy_trees = g_settings->getBool("new_style_leaves");
|
||||
menudata.smooth_lighting = g_settings->getBool("smooth_lighting");
|
||||
menudata.clouds_3d = g_settings->getBool("enable_3d_clouds");
|
||||
menudata.opaque_water = g_settings->getBool("opaque_water");
|
||||
menudata.mip_map = g_settings->getBool("mip_map");
|
||||
menudata.anisotropic_filter = g_settings->getBool("anisotropic_filter");
|
||||
menudata.bilinear_filter = g_settings->getBool("bilinear_filter");
|
||||
menudata.trilinear_filter = g_settings->getBool("trilinear_filter");
|
||||
menudata.enable_shaders = g_settings->getS32("enable_shaders");
|
||||
menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals");
|
||||
menudata.enable_particles = g_settings->getBool("enable_particles");
|
||||
menudata.liquid_finite = g_settings->getBool("liquid_finite");
|
||||
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map);
|
||||
menudata.creative_mode = g_settings->getBool("creative_mode");
|
||||
menudata.enable_damage = g_settings->getBool("enable_damage");
|
||||
menudata.password = cmd_args.get("password");
|
||||
|
||||
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
|
||||
|
||||
menudata.enable_public = g_settings->getBool("server_announce");
|
||||
// Default to selecting nothing
|
||||
menudata.selected_world = -1;
|
||||
// Get world listing for the menu
|
||||
|
||||
std::vector<WorldSpec> worldspecs = getAvailableWorlds();
|
||||
// If there is only one world, select it
|
||||
if(worldspecs.size() == 1){
|
||||
menudata.selected_world = 0;
|
||||
}
|
||||
// Otherwise try to select according to selected_world_path
|
||||
else if(g_settings->exists("selected_world_path")){
|
||||
std::string trypath = g_settings->get("selected_world_path");
|
||||
for(u32 i=0; i<worldspecs.size(); i++){
|
||||
if(worldspecs[i].path == trypath){
|
||||
menudata.selected_world = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a world was commanded, append and select it
|
||||
if(commanded_world != ""){
|
||||
|
||||
std::string gameid = getWorldGameId(commanded_world, true);
|
||||
std::string name = _("[--world parameter]");
|
||||
if(gameid == ""){
|
||||
gameid = g_settings->get("default_game");
|
||||
name += " [new]";
|
||||
}
|
||||
WorldSpec spec(commanded_world, name, gameid);
|
||||
worldspecs.push_back(spec);
|
||||
menudata.selected_world = worldspecs.size()-1;
|
||||
//TODO find within worldspecs and set config
|
||||
}
|
||||
// Copy worldspecs to menu
|
||||
menudata.worlds = worldspecs;
|
||||
// Get game listing
|
||||
menudata.games = getAvailableGames();
|
||||
// If selected game doesn't exist, take first from list
|
||||
if(findSubgame(menudata.selected_game).id == "" &&
|
||||
!menudata.games.empty()){
|
||||
menudata.selected_game = menudata.games[0].id;
|
||||
}
|
||||
const SubgameSpec *menugame = getMenuGame(menudata);
|
||||
|
||||
MenuTextures menutextures;
|
||||
menutextures.update(driver, menugame, menudata.selected_tab);
|
||||
|
||||
if(skip_main_menu == false)
|
||||
{
|
||||
video::IVideoDriver* driver = device->getVideoDriver();
|
||||
float fps_max = g_settings->getFloat("fps_max");
|
||||
|
||||
infostream<<"Waiting for other menus"<<std::endl;
|
||||
while(device->run() && kill == false)
|
||||
{
|
||||
@ -1814,7 +1595,6 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
driver->beginScene(true, true,
|
||||
video::SColor(255,128,128,128));
|
||||
drawMenuBackground(driver, menutextures);
|
||||
guienv->drawAll();
|
||||
driver->endScene();
|
||||
// On some computers framerate doesn't seem to be
|
||||
@ -1823,170 +1603,40 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
infostream<<"Waited for other menus"<<std::endl;
|
||||
|
||||
GUIMainMenu *menu =
|
||||
new GUIMainMenu(guienv, guiroot, -1,
|
||||
&g_menumgr, &menudata, g_gamecallback);
|
||||
menu->allowFocusRemoval(true);
|
||||
GUIEngine* temp = new GUIEngine(device, guiroot, &g_menumgr,smgr,&menudata);
|
||||
|
||||
delete temp;
|
||||
//once finished you'll never end up here
|
||||
smgr->clear();
|
||||
kill = menudata.kill;
|
||||
|
||||
if(error_message != L"")
|
||||
{
|
||||
verbosestream<<"error_message = "
|
||||
<<wide_to_narrow(error_message)<<std::endl;
|
||||
|
||||
GUIMessageMenu *menu2 =
|
||||
new GUIMessageMenu(guienv, guiroot, -1,
|
||||
&g_menumgr, error_message.c_str());
|
||||
menu2->drop();
|
||||
error_message = L"";
|
||||
}
|
||||
|
||||
// Time is in milliseconds, for clouds
|
||||
u32 lasttime = device->getTimer()->getTime();
|
||||
|
||||
MenuMusicFetcher soundfetcher;
|
||||
ISoundManager *sound = NULL;
|
||||
#if USE_SOUND
|
||||
sound = createOpenALSoundManager(&soundfetcher);
|
||||
#endif
|
||||
if(!sound)
|
||||
sound = &dummySoundManager;
|
||||
SimpleSoundSpec spec;
|
||||
spec.name = "main_menu";
|
||||
spec.gain = 1;
|
||||
s32 handle = sound->playSound(spec, true);
|
||||
|
||||
infostream<<"Created main menu"<<std::endl;
|
||||
|
||||
while(device->run() && kill == false)
|
||||
{
|
||||
if(menu->getStatus() == true)
|
||||
break;
|
||||
|
||||
// Game can be selected in the menu
|
||||
menugame = getMenuGame(menudata);
|
||||
menutextures.update(driver, menugame, menu->getTab());
|
||||
// Clouds for the main menu
|
||||
bool cloud_menu_background = g_settings->getBool("menu_clouds");
|
||||
if(menugame){
|
||||
// If game has regular background and no overlay, don't use clouds
|
||||
if(cloud_menu_background && menutextures.background &&
|
||||
!menutextures.overlay){
|
||||
cloud_menu_background = false;
|
||||
}
|
||||
// If game game has overlay and no regular background, always draw clouds
|
||||
else if(menutextures.overlay && !menutextures.background){
|
||||
cloud_menu_background = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Time calc for the clouds
|
||||
f32 dtime=0; // in seconds
|
||||
if (cloud_menu_background) {
|
||||
u32 time = device->getTimer()->getTime();
|
||||
if(time > lasttime)
|
||||
dtime = (time - lasttime) / 1000.0;
|
||||
else
|
||||
dtime = 0;
|
||||
lasttime = time;
|
||||
}
|
||||
|
||||
//driver->beginScene(true, true, video::SColor(255,0,0,0));
|
||||
driver->beginScene(true, true, video::SColor(255,140,186,250));
|
||||
|
||||
if (cloud_menu_background) {
|
||||
// *3 otherwise the clouds would move very slowly
|
||||
g_menuclouds->step(dtime*3);
|
||||
g_menuclouds->render();
|
||||
g_menucloudsmgr->drawAll();
|
||||
drawMenuOverlay(driver, menutextures);
|
||||
drawMenuHeader(driver, menutextures);
|
||||
drawMenuFooter(driver, menutextures);
|
||||
} else {
|
||||
drawMenuBackground(driver, menutextures);
|
||||
drawMenuHeader(driver, menutextures);
|
||||
drawMenuFooter(driver, menutextures);
|
||||
}
|
||||
|
||||
guienv->drawAll();
|
||||
|
||||
driver->endScene();
|
||||
|
||||
// On some computers framerate doesn't seem to be
|
||||
// automatically limited
|
||||
if (cloud_menu_background) {
|
||||
// Time of frame without fps limit
|
||||
float busytime;
|
||||
u32 busytime_u32;
|
||||
// not using getRealTime is necessary for wine
|
||||
u32 time = device->getTimer()->getTime();
|
||||
if(time > lasttime)
|
||||
busytime_u32 = time - lasttime;
|
||||
else
|
||||
busytime_u32 = 0;
|
||||
busytime = busytime_u32 / 1000.0;
|
||||
|
||||
// FPS limiter
|
||||
u32 frametime_min = 1000./fps_max;
|
||||
|
||||
if(busytime_u32 < frametime_min) {
|
||||
u32 sleeptime = frametime_min - busytime_u32;
|
||||
device->sleep(sleeptime);
|
||||
}
|
||||
} else {
|
||||
sleep_ms(25);
|
||||
}
|
||||
}
|
||||
sound->stopSound(handle);
|
||||
if(sound != &dummySoundManager){
|
||||
delete sound;
|
||||
sound = NULL;
|
||||
}
|
||||
|
||||
// Save controls status
|
||||
menu->readInput(&menudata);
|
||||
|
||||
infostream<<"Dropping main menu"<<std::endl;
|
||||
|
||||
menu->drop();
|
||||
}
|
||||
|
||||
playername = wide_to_narrow(menudata.name);
|
||||
if (playername == "")
|
||||
playername = std::string("Guest") + itos(myrand_range(1000,9999));
|
||||
password = translatePassword(playername, menudata.password);
|
||||
//update worldspecs (necessary as new world may have been created)
|
||||
worldspecs = getAvailableWorlds();
|
||||
|
||||
if (menudata.name == "")
|
||||
menudata.name = std::string("Guest") + itos(myrand_range(1000,9999));
|
||||
else
|
||||
playername = menudata.name;
|
||||
|
||||
password = translatePassword(playername, narrow_to_wide(menudata.password));
|
||||
//infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
|
||||
|
||||
address = wide_to_narrow(menudata.address);
|
||||
int newport = stoi(wide_to_narrow(menudata.port));
|
||||
address = menudata.address;
|
||||
int newport = stoi(menudata.port);
|
||||
if(newport != 0)
|
||||
port = newport;
|
||||
|
||||
simple_singleplayer_mode = menudata.simple_singleplayer_mode;
|
||||
|
||||
// Save settings
|
||||
g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab);
|
||||
g_settings->setS32("selected_serverlist", menudata.selected_serverlist);
|
||||
g_settings->set("selected_mainmenu_game", menudata.selected_game);
|
||||
g_settings->set("new_style_leaves", itos(menudata.fancy_trees));
|
||||
g_settings->set("smooth_lighting", itos(menudata.smooth_lighting));
|
||||
g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d));
|
||||
g_settings->set("opaque_water", itos(menudata.opaque_water));
|
||||
|
||||
g_settings->set("mip_map", itos(menudata.mip_map));
|
||||
g_settings->set("anisotropic_filter", itos(menudata.anisotropic_filter));
|
||||
g_settings->set("bilinear_filter", itos(menudata.bilinear_filter));
|
||||
g_settings->set("trilinear_filter", itos(menudata.trilinear_filter));
|
||||
|
||||
g_settings->setS32("enable_shaders", menudata.enable_shaders);
|
||||
g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals));
|
||||
g_settings->set("enable_particles", itos(menudata.enable_particles));
|
||||
g_settings->set("liquid_finite", itos(menudata.liquid_finite));
|
||||
|
||||
g_settings->set("creative_mode", itos(menudata.creative_mode));
|
||||
g_settings->set("enable_damage", itos(menudata.enable_damage));
|
||||
g_settings->set("server_announce", itos(menudata.enable_public));
|
||||
g_settings->set("name", playername);
|
||||
g_settings->set("address", address);
|
||||
g_settings->set("port", itos(port));
|
||||
if(menudata.selected_world != -1)
|
||||
|
||||
if((menudata.selected_world >= 0) &&
|
||||
(menudata.selected_world < worldspecs.size()))
|
||||
g_settings->set("selected_world_path",
|
||||
worldspecs[menudata.selected_world].path);
|
||||
|
||||
@ -2010,8 +1660,8 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
ServerListSpec server;
|
||||
server["name"] = menudata.servername;
|
||||
server["address"] = wide_to_narrow(menudata.address);
|
||||
server["port"] = wide_to_narrow(menudata.port);
|
||||
server["address"] = menudata.address;
|
||||
server["port"] = menudata.port;
|
||||
server["description"] = menudata.serverdescription;
|
||||
ServerList::insert(server);
|
||||
}
|
||||
@ -2022,30 +1672,7 @@ int main(int argc, char *argv[])
|
||||
infostream<<"Selected world: "<<worldspec.name
|
||||
<<" ["<<worldspec.path<<"]"<<std::endl;
|
||||
}
|
||||
|
||||
// Only refresh if so requested
|
||||
if(menudata.only_refresh){
|
||||
infostream<<"Refreshing menu"<<std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create new world if requested
|
||||
if(menudata.create_world_name != L"")
|
||||
{
|
||||
std::string path = porting::path_user + DIR_DELIM
|
||||
"worlds" + DIR_DELIM
|
||||
+ wide_to_narrow(menudata.create_world_name);
|
||||
// Create world if it doesn't exist
|
||||
if(!initializeWorld(path, menudata.create_world_gameid)){
|
||||
error_message = wgettext("Failed to initialize world");
|
||||
errorstream<<wide_to_narrow(error_message)<<std::endl;
|
||||
continue;
|
||||
}
|
||||
g_settings->set("selected_world_path", path);
|
||||
g_settings->set("selected_mainmenu_game", menudata.create_world_gameid);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If local game
|
||||
if(current_address == "")
|
||||
{
|
||||
@ -2085,8 +1712,10 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// Break out of menu-game loop to shut down cleanly
|
||||
if(device->run() == false || kill == true)
|
||||
if(device->run() == false || kill == true) {
|
||||
g_settings->updateConfigFile(configpath.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
Run game
|
||||
@ -2138,11 +1767,11 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
}
|
||||
} // Menu-game loop
|
||||
|
||||
|
||||
|
||||
|
||||
g_menuclouds->drop();
|
||||
g_menucloudsmgr->drop();
|
||||
|
||||
|
||||
delete input;
|
||||
|
||||
/*
|
||||
|
13
src/map.cpp
13
src/map.cpp
@ -3237,17 +3237,18 @@ v2s16 ServerMap::getSectorPos(std::string dirname)
|
||||
{
|
||||
unsigned int x, y;
|
||||
int r;
|
||||
size_t spos = dirname.rfind(DIR_DELIM_C) + 1;
|
||||
assert(spos != std::string::npos);
|
||||
if(dirname.size() - spos == 8)
|
||||
std::string component;
|
||||
fs::RemoveLastPathComponent(dirname, &component, 1);
|
||||
if(component.size() == 8)
|
||||
{
|
||||
// Old layout
|
||||
r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
|
||||
r = sscanf(component.c_str(), "%4x%4x", &x, &y);
|
||||
}
|
||||
else if(dirname.size() - spos == 3)
|
||||
else if(component.size() == 3)
|
||||
{
|
||||
// New layout
|
||||
r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
|
||||
fs::RemoveLastPathComponent(dirname, &component, 2);
|
||||
r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
|
||||
// Sign-extend the 12 bit values up to 16 bits...
|
||||
if(x&0x800) x|=0xF000;
|
||||
if(y&0x800) y|=0xF000;
|
||||
|
28
src/mods.cpp
28
src/mods.cpp
@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
*/
|
||||
|
||||
#include "mods.h"
|
||||
#include "main.h"
|
||||
#include "filesys.h"
|
||||
#include "strfnd.h"
|
||||
#include "log.h"
|
||||
@ -25,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "settings.h"
|
||||
#include "strfnd.h"
|
||||
#include <cctype>
|
||||
#include "convert_json.h"
|
||||
|
||||
static bool parseDependsLine(std::istream &is,
|
||||
std::string &dep, std::set<char> &symbols)
|
||||
@ -389,3 +391,29 @@ void ModConfiguration::resolveDependencies()
|
||||
// Step 4: write back list of unsatisfied mods
|
||||
m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end());
|
||||
}
|
||||
|
||||
#if USE_CURL
|
||||
Json::Value getModstoreUrl(std::string url)
|
||||
{
|
||||
struct curl_slist *chunk = NULL;
|
||||
|
||||
bool special_http_header = true;
|
||||
|
||||
try{
|
||||
special_http_header = g_settings->getBool("modstore_disable_special_http_header");
|
||||
}
|
||||
catch(SettingNotFoundException &e) {
|
||||
}
|
||||
|
||||
if (special_http_header)
|
||||
chunk = curl_slist_append(chunk, "Accept: application/vnd.minetest.mmdb-v1+json");
|
||||
|
||||
Json::Value retval = fetchJsonValue(url,chunk);
|
||||
|
||||
if (chunk != NULL)
|
||||
curl_slist_free_all(chunk);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
68
src/mods.h
68
src/mods.h
@ -29,6 +29,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <map>
|
||||
#include <exception>
|
||||
#include <list>
|
||||
#include "json/json.h"
|
||||
#include "config.h"
|
||||
|
||||
#if USE_CURL
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
|
||||
#define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
|
||||
|
||||
@ -154,4 +160,66 @@ private:
|
||||
|
||||
};
|
||||
|
||||
#if USE_CURL
|
||||
Json::Value getModstoreUrl(std::string url);
|
||||
#else
|
||||
inline Json::Value getModstoreUrl(std::string url) {
|
||||
return Json::Value();
|
||||
}
|
||||
#endif
|
||||
|
||||
struct ModLicenseInfo {
|
||||
int id;
|
||||
std::string shortinfo;
|
||||
std::string url;
|
||||
};
|
||||
|
||||
struct ModAuthorInfo {
|
||||
int id;
|
||||
std::string username;
|
||||
};
|
||||
|
||||
struct ModStoreMod {
|
||||
int id;
|
||||
std::string title;
|
||||
std::string basename;
|
||||
ModAuthorInfo author;
|
||||
float rating;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
struct ModStoreCategoryInfo {
|
||||
int id;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct ModStoreVersionEntry {
|
||||
int id;
|
||||
std::string date;
|
||||
std::string file;
|
||||
bool approved;
|
||||
//ugly version number
|
||||
int mtversion;
|
||||
};
|
||||
|
||||
struct ModStoreModDetails {
|
||||
/* version_set?? */
|
||||
std::vector<ModStoreCategoryInfo> categories;
|
||||
ModAuthorInfo author;
|
||||
ModLicenseInfo license;
|
||||
int id;
|
||||
std::string title;
|
||||
std::string basename;
|
||||
std::string description;
|
||||
std::string repository;
|
||||
float rating;
|
||||
std::vector<std::string> depends;
|
||||
std::vector<std::string> softdeps;
|
||||
|
||||
std::string download_url;
|
||||
std::string screenshot_url;
|
||||
std::vector<ModStoreVersionEntry> versions;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "porting.h"
|
||||
#include "log.h"
|
||||
#include "json/json.h"
|
||||
#include "convert_json.h"
|
||||
#if USE_CURL
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
@ -68,35 +69,22 @@ std::vector<ServerListSpec> getLocal()
|
||||
|
||||
|
||||
#if USE_CURL
|
||||
|
||||
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
|
||||
std::vector<ServerListSpec> getOnline()
|
||||
{
|
||||
std::string liststring;
|
||||
CURL *curl;
|
||||
Json::Value root = fetchJsonValue((g_settings->get("serverlist_url")+"/list").c_str(),0);
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl)
|
||||
{
|
||||
CURLcode res;
|
||||
std::vector<ServerListSpec> serverlist;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, (g_settings->get("serverlist_url")+"/list").c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ServerList::WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &liststring);
|
||||
|
||||
res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK)
|
||||
errorstream<<"Serverlist at url "<<g_settings->get("serverlist_url")<<" not found (internet connection?)"<<std::endl;
|
||||
curl_easy_cleanup(curl);
|
||||
if (root.isArray()) {
|
||||
for (unsigned int i = 0; i < root.size(); i++)
|
||||
{
|
||||
if (root[i].isObject()) {
|
||||
serverlist.push_back(root[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ServerList::deSerializeJson(liststring);
|
||||
|
||||
return serverlist;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -189,30 +177,6 @@ std::string serialize(std::vector<ServerListSpec> serverlist)
|
||||
return liststring;
|
||||
}
|
||||
|
||||
std::vector<ServerListSpec> deSerializeJson(std::string liststring)
|
||||
{
|
||||
std::vector<ServerListSpec> serverlist;
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
std::istringstream stream(liststring);
|
||||
if (!liststring.size()) {
|
||||
return serverlist;
|
||||
}
|
||||
if (!reader.parse( stream, root ) )
|
||||
{
|
||||
errorstream << "Failed to parse server list " << reader.getFormattedErrorMessages();
|
||||
return serverlist;
|
||||
}
|
||||
if (root["list"].isArray())
|
||||
for (unsigned int i = 0; i < root["list"].size(); i++)
|
||||
{
|
||||
if (root["list"][i].isObject()) {
|
||||
serverlist.push_back(root["list"][i]);
|
||||
}
|
||||
}
|
||||
return serverlist;
|
||||
}
|
||||
|
||||
std::string serializeJson(std::vector<ServerListSpec> serverlist)
|
||||
{
|
||||
Json::Value root;
|
||||
|
208
src/test.cpp
208
src/test.cpp
@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "settings.h"
|
||||
#include "log.h"
|
||||
#include "util/string.h"
|
||||
#include "filesys.h"
|
||||
#include "voxelalgorithms.h"
|
||||
#include "inventory.h"
|
||||
#include "util/numeric.h"
|
||||
@ -171,6 +172,212 @@ struct TestUtilities: public TestBase
|
||||
}
|
||||
};
|
||||
|
||||
struct TestPath: public TestBase
|
||||
{
|
||||
// adjusts a POSIX path to system-specific conventions
|
||||
// -> changes '/' to DIR_DELIM
|
||||
// -> absolute paths start with "C:\\" on windows
|
||||
std::string p(std::string path)
|
||||
{
|
||||
for(size_t i = 0; i < path.size(); ++i){
|
||||
if(path[i] == '/'){
|
||||
path.replace(i, 1, DIR_DELIM);
|
||||
i += std::string(DIR_DELIM).size() - 1; // generally a no-op
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if(path[0] == '\\')
|
||||
path = "C:" + path;
|
||||
#endif
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
void Run()
|
||||
{
|
||||
std::string path, result, removed;
|
||||
|
||||
/*
|
||||
Test fs::IsDirDelimiter
|
||||
*/
|
||||
UASSERT(fs::IsDirDelimiter('/') == true);
|
||||
UASSERT(fs::IsDirDelimiter('A') == false);
|
||||
UASSERT(fs::IsDirDelimiter(0) == false);
|
||||
#ifdef _WIN32
|
||||
UASSERT(fs::IsDirDelimiter('\\') == true);
|
||||
#else
|
||||
UASSERT(fs::IsDirDelimiter('\\') == false);
|
||||
#endif
|
||||
|
||||
/*
|
||||
Test fs::PathStartsWith
|
||||
*/
|
||||
{
|
||||
const int numpaths = 12;
|
||||
std::string paths[numpaths] = {
|
||||
"",
|
||||
p("/"),
|
||||
p("/home/user/minetest"),
|
||||
p("/home/user/minetest/bin"),
|
||||
p("/home/user/.minetest"),
|
||||
p("/tmp/dir/file"),
|
||||
p("/tmp/file/"),
|
||||
p("/tmP/file"),
|
||||
p("/tmp"),
|
||||
p("/tmp/dir"),
|
||||
p("/home/user2/minetest/worlds"),
|
||||
p("/home/user2/minetest/world"),
|
||||
};
|
||||
/*
|
||||
expected fs::PathStartsWith results
|
||||
0 = returns false
|
||||
1 = returns true
|
||||
2 = returns false on windows, false elsewhere
|
||||
3 = returns true on windows, true elsewhere
|
||||
4 = returns true if and only if
|
||||
FILESYS_CASE_INSENSITIVE is true
|
||||
*/
|
||||
int expected_results[numpaths][numpaths] = {
|
||||
{1,2,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,0,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,1,0,0,0,0,0,0,0,0,0},
|
||||
{1,1,1,1,0,0,0,0,0,0,0,0},
|
||||
{1,1,0,0,1,0,0,0,0,0,0,0},
|
||||
{1,1,0,0,0,1,0,0,1,1,0,0},
|
||||
{1,1,0,0,0,0,1,4,1,0,0,0},
|
||||
{1,1,0,0,0,0,4,1,4,0,0,0},
|
||||
{1,1,0,0,0,0,0,0,1,0,0,0},
|
||||
{1,1,0,0,0,0,0,0,1,1,0,0},
|
||||
{1,1,0,0,0,0,0,0,0,0,1,0},
|
||||
{1,1,0,0,0,0,0,0,0,0,0,1},
|
||||
};
|
||||
|
||||
for (int i = 0; i < numpaths; i++)
|
||||
for (int j = 0; j < numpaths; j++){
|
||||
/*verbosestream<<"testing fs::PathStartsWith(\""
|
||||
<<paths[i]<<"\", \""
|
||||
<<paths[j]<<"\")"<<std::endl;*/
|
||||
bool starts = fs::PathStartsWith(paths[i], paths[j]);
|
||||
int expected = expected_results[i][j];
|
||||
if(expected == 0){
|
||||
UASSERT(starts == false);
|
||||
}
|
||||
else if(expected == 1){
|
||||
UASSERT(starts == true);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
else if(expected == 2){
|
||||
UASSERT(starts == false);
|
||||
}
|
||||
else if(expected == 3){
|
||||
UASSERT(starts == true);
|
||||
}
|
||||
#else
|
||||
else if(expected == 2){
|
||||
UASSERT(starts == true);
|
||||
}
|
||||
else if(expected == 3){
|
||||
UASSERT(starts == false);
|
||||
}
|
||||
#endif
|
||||
else if(expected == 4){
|
||||
UASSERT(starts == (bool)FILESYS_CASE_INSENSITIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Test fs::RemoveLastPathComponent
|
||||
*/
|
||||
UASSERT(fs::RemoveLastPathComponent("") == "");
|
||||
path = p("/home/user/minetest/bin/..//worlds/world1");
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 0);
|
||||
UASSERT(result == path);
|
||||
UASSERT(removed == "");
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 1);
|
||||
UASSERT(result == p("/home/user/minetest/bin/..//worlds"));
|
||||
UASSERT(removed == p("world1"));
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 2);
|
||||
UASSERT(result == p("/home/user/minetest/bin/.."));
|
||||
UASSERT(removed == p("worlds/world1"));
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 3);
|
||||
UASSERT(result == p("/home/user/minetest/bin"));
|
||||
UASSERT(removed == p("../worlds/world1"));
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 4);
|
||||
UASSERT(result == p("/home/user/minetest"));
|
||||
UASSERT(removed == p("bin/../worlds/world1"));
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 5);
|
||||
UASSERT(result == p("/home/user"));
|
||||
UASSERT(removed == p("minetest/bin/../worlds/world1"));
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 6);
|
||||
UASSERT(result == p("/home"));
|
||||
UASSERT(removed == p("user/minetest/bin/../worlds/world1"));
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 7);
|
||||
#ifdef _WIN32
|
||||
UASSERT(result == "C:");
|
||||
#else
|
||||
UASSERT(result == "");
|
||||
#endif
|
||||
UASSERT(removed == p("home/user/minetest/bin/../worlds/world1"));
|
||||
|
||||
/*
|
||||
Now repeat the test with a trailing delimiter
|
||||
*/
|
||||
path = p("/home/user/minetest/bin/..//worlds/world1/");
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 0);
|
||||
UASSERT(result == path);
|
||||
UASSERT(removed == "");
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 1);
|
||||
UASSERT(result == p("/home/user/minetest/bin/..//worlds"));
|
||||
UASSERT(removed == p("world1"));
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 2);
|
||||
UASSERT(result == p("/home/user/minetest/bin/.."));
|
||||
UASSERT(removed == p("worlds/world1"));
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 3);
|
||||
UASSERT(result == p("/home/user/minetest/bin"));
|
||||
UASSERT(removed == p("../worlds/world1"));
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 4);
|
||||
UASSERT(result == p("/home/user/minetest"));
|
||||
UASSERT(removed == p("bin/../worlds/world1"));
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 5);
|
||||
UASSERT(result == p("/home/user"));
|
||||
UASSERT(removed == p("minetest/bin/../worlds/world1"));
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 6);
|
||||
UASSERT(result == p("/home"));
|
||||
UASSERT(removed == p("user/minetest/bin/../worlds/world1"));
|
||||
result = fs::RemoveLastPathComponent(path, &removed, 7);
|
||||
#ifdef _WIN32
|
||||
UASSERT(result == "C:");
|
||||
#else
|
||||
UASSERT(result == "");
|
||||
#endif
|
||||
UASSERT(removed == p("home/user/minetest/bin/../worlds/world1"));
|
||||
|
||||
/*
|
||||
Test fs::RemoveRelativePathComponent
|
||||
*/
|
||||
path = p("/home/user/minetest/bin");
|
||||
result = fs::RemoveRelativePathComponents(path);
|
||||
UASSERT(result == path);
|
||||
path = p("/home/user/minetest/bin/../worlds/world1");
|
||||
result = fs::RemoveRelativePathComponents(path);
|
||||
UASSERT(result == p("/home/user/minetest/worlds/world1"));
|
||||
path = p("/home/user/minetest/bin/../worlds/world1/");
|
||||
result = fs::RemoveRelativePathComponents(path);
|
||||
UASSERT(result == p("/home/user/minetest/worlds/world1"));
|
||||
path = p(".");
|
||||
result = fs::RemoveRelativePathComponents(path);
|
||||
UASSERT(result == "");
|
||||
path = p("./subdir/../..");
|
||||
result = fs::RemoveRelativePathComponents(path);
|
||||
UASSERT(result == "");
|
||||
path = p("/a/b/c/.././../d/../e/f/g/../h/i/j/../../../..");
|
||||
result = fs::RemoveRelativePathComponents(path);
|
||||
UASSERT(result == p("/a/e"));
|
||||
}
|
||||
};
|
||||
|
||||
struct TestSettings: public TestBase
|
||||
{
|
||||
void Run()
|
||||
@ -1785,6 +1992,7 @@ void run_tests()
|
||||
|
||||
infostream<<"run_tests() started"<<std::endl;
|
||||
TEST(TestUtilities);
|
||||
TEST(TestPath);
|
||||
TEST(TestSettings);
|
||||
TEST(TestCompress);
|
||||
TEST(TestSerialization);
|
||||
|
BIN
textures/base/pack/no_screenshot.png
Normal file
BIN
textures/base/pack/no_screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
Loading…
Reference in New Issue
Block a user