Replace C++ mainmenu by formspec powered one

This commit is contained in:
sapier 2013-06-23 18:30:21 +02:00 committed by kwolekr
parent fe4ce03d52
commit 967121a34b
37 changed files with 8058 additions and 3949 deletions

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

File diff suppressed because it is too large Load Diff

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

@ -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

@ -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 ^ Position and size units are inventory slots
^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px ^ 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>] field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]
^ Textual field; will be sent to server when a button is clicked ^ 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 ^ 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 ^ label is the text on the label
^ Position and size units are inventory slots ^ 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>] button[<X>,<Y>;<W>,<H>;<name>;<label>]
^ Clickable button. When clicked, fields will be sent. ^ Clickable button. When clicked, fields will be sent.
^ x, y and name work as per field ^ 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 ^ image is the filename of an image
^ Position and size units are inventory slots ^ 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>] item_image_button[<X>,<Y>;<W>,<H>;<item name>;<name>;<label>]
^ x, y, w, h, name and label work as per button ^ x, y, w, h, name and label work as per button
^ item name is the registered name of an item/node, ^ 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>] image_button_exit[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
^ When clicked, fields will be sent and the form will quit. ^ 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: Inventory location:
- "context": Selected node metadata (deprecated: "current_name") - "context": Selected node metadata (deprecated: "current_name")

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 # Enable/disable running an IPv6 server. An IPv6 server may be restricted
# to IPv6 clients, depending on system configuration. # to IPv6 clients, depending on system configuration.
#ipv6_server = false #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 staticobject.cpp
serverlist.cpp serverlist.cpp
pathfinder.cpp pathfinder.cpp
convert_json.cpp
${SCRIPT_SRCS} ${SCRIPT_SRCS}
${UTIL_SRCS} ${UTIL_SRCS}
) )
@ -313,7 +314,6 @@ set(minetest_SRCS
clientobject.cpp clientobject.cpp
chat.cpp chat.cpp
hud.cpp hud.cpp
guiMainMenu.cpp
guiKeyChangeMenu.cpp guiKeyChangeMenu.cpp
guiMessageMenu.cpp guiMessageMenu.cpp
guiTextInputMenu.cpp guiTextInputMenu.cpp
@ -323,15 +323,16 @@ set(minetest_SRCS
guiVolumeChange.cpp guiVolumeChange.cpp
guiDeathScreen.cpp guiDeathScreen.cpp
guiChatConsole.cpp guiChatConsole.cpp
guiCreateWorld.cpp
guiConfigureWorld.cpp
guiConfirmMenu.cpp
client.cpp client.cpp
filecache.cpp filecache.cpp
tile.cpp tile.cpp
shader.cpp shader.cpp
game.cpp game.cpp
main.cpp main.cpp
guiEngine.cpp
guiLuaApi.cpp
guiFileSelectMenu.cpp
convert_json.cpp
) )
if(USE_FREETYPE) if(USE_FREETYPE)
@ -488,7 +489,7 @@ else()
endif() 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_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) if(USE_GPROF)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg")

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. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#ifndef GUICONFIRMMENU_HEADER #ifndef __CONVERT_JSON_H__
#define GUICONFIRMMENU_HEADER #define __CONVERT_JSON_H__
#include "irrlichttypes_extrabloated.h" #include "json/json.h"
#include "modalMenu.h"
#include <string>
struct ConfirmDest struct ModStoreMod;
{ struct ModStoreModDetails;
virtual void answer(bool answer) = 0;
virtual ~ConfirmDest() {};
};
class GUIConfirmMenu : public GUIModalMenu std::vector<ModStoreMod> readModStoreList(Json::Value& modlist);
{ ModStoreModDetails readModStoreModDetails(Json::Value& details);
public:
GUIConfirmMenu(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr,
ConfirmDest *dest,
std::wstring message_text);
~GUIConfirmMenu();
void removeChildren(); Json::Value fetchJsonValue(const std::string url,
// Remove and re-add (or reposition) stuff struct curl_slist *chunk);
void regenerateGui(v2u32 screensize);
void drawMenu();
void acceptInput(bool answer);
bool OnEvent(const SEvent& event);
private:
ConfirmDest *m_dest;
std::wstring m_message_text;
};
#endif #endif

@ -256,6 +256,11 @@ void set_default_settings(Settings *settings)
// IPv6 // IPv6
settings->setDefault("enable_ipv6", "true"); settings->setDefault("enable_ipv6", "true");
settings->setDefault("ipv6_server", "false"); 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) void override_default_settings(Settings *settings, Settings *from)

@ -20,7 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filesys.h" #include "filesys.h"
#include "strfnd.h" #include "strfnd.h"
#include <iostream> #include <iostream>
#include <stdio.h>
#include <string.h> #include <string.h>
#include <errno.h>
#include "log.h" #include "log.h"
namespace fs namespace fs
@ -30,11 +32,9 @@ namespace fs
#define _WIN32_WINNT 0x0501 #define _WIN32_WINNT 0x0501
#include <windows.h> #include <windows.h>
#include <stdio.h>
#include <malloc.h> #include <malloc.h>
#include <tchar.h> #include <tchar.h>
#include <wchar.h> #include <wchar.h>
#include <stdio.h>
#define BUFSIZE MAX_PATH #define BUFSIZE MAX_PATH
@ -145,6 +145,11 @@ bool IsDir(std::string path)
(attr & FILE_ATTRIBUTE_DIRECTORY)); (attr & FILE_ATTRIBUTE_DIRECTORY));
} }
bool IsDirDelimiter(char c)
{
return c == '/' || c == '\\';
}
bool RecursiveDelete(std::string path) bool RecursiveDelete(std::string path)
{ {
infostream<<"Recursively deleting \""<<path<<"\""<<std::endl; 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 #else // POSIX
#include <sys/types.h> #include <sys/types.h>
#include <dirent.h> #include <dirent.h>
#include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
@ -301,6 +321,11 @@ bool IsDir(std::string path)
return ((statbuf.st_mode & S_IFDIR) == S_IFDIR); return ((statbuf.st_mode & S_IFDIR) == S_IFDIR);
} }
bool IsDirDelimiter(char c)
{
return c == '/';
}
bool RecursiveDelete(std::string path) 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 #endif
void GetRecursiveSubPaths(std::string path, std::vector<std::string> &dst) void GetRecursiveSubPaths(std::string path, std::vector<std::string> &dst)
@ -414,16 +453,14 @@ bool RecursiveDeleteContent(std::string path)
bool CreateAllDirs(std::string path) bool CreateAllDirs(std::string path)
{ {
size_t pos;
std::vector<std::string> tocreate; std::vector<std::string> tocreate;
std::string basepath = path; std::string basepath = path;
while(!PathExists(basepath)) while(!PathExists(basepath))
{ {
tocreate.push_back(basepath); tocreate.push_back(basepath);
pos = basepath.rfind(DIR_DELIM_C); basepath = RemoveLastPathComponent(basepath);
if(pos == std::string::npos) if(basepath.empty())
break; break;
basepath = basepath.substr(0,pos);
} }
for(int i=tocreate.size()-1;i>=0;i--) for(int i=tocreate.size()-1;i>=0;i--)
if(!CreateDir(tocreate[i])) if(!CreateDir(tocreate[i]))
@ -431,5 +468,221 @@ bool CreateAllDirs(std::string path)
return true; 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 } // namespace fs

@ -26,10 +26,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifdef _WIN32 // WINDOWS #ifdef _WIN32 // WINDOWS
#define DIR_DELIM "\\" #define DIR_DELIM "\\"
#define DIR_DELIM_C '\\' #define FILESYS_CASE_INSENSITIVE 1
#else // POSIX #else // POSIX
#define DIR_DELIM "/" #define DIR_DELIM "/"
#define DIR_DELIM_C '/' #define FILESYS_CASE_INSENSITIVE 0
#endif #endif
namespace fs namespace fs
@ -49,12 +49,17 @@ bool PathExists(std::string path);
bool IsDir(std::string path); bool IsDir(std::string path);
bool IsDirDelimiter(char c);
// Only pass full paths to this one. True on success. // Only pass full paths to this one. True on success.
// NOTE: The WIN32 version returns always true. // NOTE: The WIN32 version returns always true.
bool RecursiveDelete(std::string path); bool RecursiveDelete(std::string path);
bool DeleteSingleFileOrEmptyDirectory(std::string path); bool DeleteSingleFileOrEmptyDirectory(std::string path);
// Returns path to temp directory, can return "" on error
std::string TempPath();
/* Multiplatform */ /* Multiplatform */
// The path itself not included // 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. // Create all directories on the given path that don't already exist.
bool CreateAllDirs(std::string path); 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 }//fs
#endif #endif

@ -208,33 +208,6 @@ public:
Client *m_client; 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 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

@ -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

@ -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

@ -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

@ -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 #ifndef GUIINVENTORYMENU_HEADER
#define GUIINVENTORYMENU_HEADER #define GUIINVENTORYMENU_HEADER
#include <utility>
#include "irrlichttypes_extrabloated.h" #include "irrlichttypes_extrabloated.h"
#include "inventory.h" #include "inventory.h"
#include "inventorymanager.h" #include "inventorymanager.h"
@ -29,6 +31,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class IGameDef; class IGameDef;
class InventoryManager; class InventoryManager;
typedef enum {
f_Button,
f_ListBox,
f_TabHeader,
f_CheckBox,
f_DropDown,
f_Unknown
} FormspecFieldType;
struct TextDest struct TextDest
{ {
virtual ~TextDest() {}; virtual ~TextDest() {};
@ -113,10 +124,19 @@ class GUIFormSpecMenu : public GUIModalMenu
pos(a_pos), pos(a_pos),
geom(a_geom) 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; std::string name;
v2s32 pos; v2s32 pos;
v2s32 geom; v2s32 geom;
bool scale;
}; };
struct FieldSpec struct FieldSpec
@ -131,7 +151,7 @@ class GUIFormSpecMenu : public GUIModalMenu
fid(id) fid(id)
{ {
send = false; send = false;
is_button = false; ftype = f_Unknown;
is_exit = false; is_exit = false;
tooltip=""; tooltip="";
} }
@ -140,12 +160,24 @@ class GUIFormSpecMenu : public GUIModalMenu
std::wstring fdefault; std::wstring fdefault;
int fid; int fid;
bool send; bool send;
bool is_button; FormspecFieldType ftype;
bool is_exit; bool is_exit;
core::rect<s32> rect; core::rect<s32> rect;
std::string tooltip; 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: public:
GUIFormSpecMenu(irr::IrrlichtDevice* dev, GUIFormSpecMenu(irr::IrrlichtDevice* dev,
gui::IGUIElement* parent, s32 id, gui::IGUIElement* parent, s32 id,
@ -153,6 +185,7 @@ public:
InventoryManager *invmgr, InventoryManager *invmgr,
IGameDef *gamedef IGameDef *gamedef
); );
~GUIFormSpecMenu(); ~GUIFormSpecMenu();
void setFormSpec(const std::string &formspec_string, void setFormSpec(const std::string &formspec_string,
@ -175,6 +208,20 @@ public:
m_text_dst = text_dst; 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(); void removeChildren();
/* /*
Remove and re-add (or reposition) stuff Remove and re-add (or reposition) stuff
@ -188,18 +235,21 @@ public:
void updateSelectedItem(); void updateSelectedItem();
ItemStack verifySelectedItem(); ItemStack verifySelectedItem();
void acceptInput(); void acceptInput(int evttype=-1);
bool OnEvent(const SEvent& event); bool OnEvent(const SEvent& event);
int getListboxIndex(std::string listboxname);
protected: protected:
v2s32 getBasePos() const v2s32 getBasePos() const
{ {
return padding + AbsoluteRect.UpperLeftCorner; return padding + offset + AbsoluteRect.UpperLeftCorner;
} }
v2s32 padding; v2s32 padding;
v2s32 spacing; v2s32 spacing;
v2s32 imgsize; v2s32 imgsize;
v2s32 offset;
irr::IrrlichtDevice* m_device; irr::IrrlichtDevice* m_device;
InventoryManager *m_invmgr; InventoryManager *m_invmgr;
@ -214,7 +264,10 @@ protected:
std::vector<ImageDrawSpec> m_backgrounds; std::vector<ImageDrawSpec> m_backgrounds;
std::vector<ImageDrawSpec> m_images; std::vector<ImageDrawSpec> m_images;
std::vector<ImageDrawSpec> m_itemimages; std::vector<ImageDrawSpec> m_itemimages;
std::vector<BoxDrawSpec> m_boxes;
std::vector<FieldSpec> m_fields; 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; ItemSpec *m_selected_item;
u32 m_selected_amount; u32 m_selected_amount;
@ -228,6 +281,74 @@ protected:
v2s32 m_pointer; v2s32 m_pointer;
gui::IGUIStaticText *m_tooltip_element; 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 #endif

1067
src/guiLuaApi.cpp Normal file

File diff suppressed because it is too large Load Diff

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

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 "modalMenu.h"
#include <string> #include <string>
#include <list> #include <list>
#include "subgame.h"
#include "serverlist.h"
class IGameCallback;
enum {
SERVERLIST_FAVORITES,
SERVERLIST_PUBLIC,
};
enum enum
{ {
@ -45,113 +36,31 @@ enum
struct MainMenuData 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 // Client options
std::string servername; std::string servername;
std::string serverdescription; std::string serverdescription;
std::wstring address; std::string address;
std::wstring port; std::string port;
std::wstring name; std::string name;
std::wstring password; std::string 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;
// Server options // Server options
bool creative_mode;
bool enable_damage;
bool enable_public; bool enable_public;
int selected_world; int selected_world;
bool simple_singleplayer_mode; bool simple_singleplayer_mode;
// Actions // Actions
std::wstring create_world_name; bool kill;
std::string create_world_gameid;
bool only_refresh;
int selected_serverlist;
std::vector<WorldSpec> worlds;
std::vector<SubgameSpec> games;
std::vector<ServerListSpec> servers;
//error handling
std::string errormessage;
MainMenuData(): 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), enable_public(false),
selected_world(0), selected_world(0),
simple_singleplayer_mode(false), simple_singleplayer_mode(false),
// Actions errormessage("")
only_refresh(false),
selected_serverlist(SERVERLIST_FAVORITES)
{} {}
}; };
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 #endif

@ -78,6 +78,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "serverlist.h" #include "serverlist.h"
#include "sound.h" #include "sound.h"
#include "sound_openal.h" #include "sound_openal.h"
#include "guiEngine.h"
/* /*
Settings. Settings.
@ -660,180 +661,6 @@ private:
bool rightreleased; 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 #endif // !SERVER
// These are defined global so that they're not optimized too much. // 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 // Initialize menu data
MainMenuData menudata; MainMenuData menudata;
if(g_settings->exists("selected_mainmenu_tab")) menudata.kill = kill;
menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab"); menudata.address = address;
if(g_settings->exists("selected_serverlist")) menudata.name = playername;
menudata.selected_serverlist = g_settings->getS32("selected_serverlist"); menudata.port = itos(port);
if(g_settings->exists("selected_mainmenu_game")){ menudata.errormessage = wide_to_narrow(error_message);
menudata.selected_game = g_settings->get("selected_mainmenu_game"); error_message = L"";
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));
if(cmd_args.exists("password")) if(cmd_args.exists("password"))
menudata.password = narrow_to_wide(cmd_args.get("password")); menudata.password = cmd_args.get("password");
menudata.fancy_trees = g_settings->getBool("new_style_leaves");
menudata.smooth_lighting = g_settings->getBool("smooth_lighting"); driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
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.enable_public = g_settings->getBool("server_announce"); 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(); 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 a world was commanded, append and select it
if(commanded_world != ""){ if(commanded_world != ""){
std::string gameid = getWorldGameId(commanded_world, true); std::string gameid = getWorldGameId(commanded_world, true);
std::string name = _("[--world parameter]"); std::string name = _("[--world parameter]");
if(gameid == ""){ if(gameid == ""){
gameid = g_settings->get("default_game"); gameid = g_settings->get("default_game");
name += " [new]"; name += " [new]";
} }
WorldSpec spec(commanded_world, name, gameid); //TODO find within worldspecs and set config
worldspecs.push_back(spec);
menudata.selected_world = worldspecs.size()-1;
} }
// 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) if(skip_main_menu == false)
{ {
video::IVideoDriver* driver = device->getVideoDriver(); video::IVideoDriver* driver = device->getVideoDriver();
float fps_max = g_settings->getFloat("fps_max");
infostream<<"Waiting for other menus"<<std::endl; infostream<<"Waiting for other menus"<<std::endl;
while(device->run() && kill == false) while(device->run() && kill == false)
{ {
@ -1814,7 +1595,6 @@ int main(int argc, char *argv[])
break; break;
driver->beginScene(true, true, driver->beginScene(true, true,
video::SColor(255,128,128,128)); video::SColor(255,128,128,128));
drawMenuBackground(driver, menutextures);
guienv->drawAll(); guienv->drawAll();
driver->endScene(); driver->endScene();
// On some computers framerate doesn't seem to be // 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; infostream<<"Waited for other menus"<<std::endl;
GUIMainMenu *menu = GUIEngine* temp = new GUIEngine(device, guiroot, &g_menumgr,smgr,&menudata);
new GUIMainMenu(guienv, guiroot, -1,
&g_menumgr, &menudata, g_gamecallback);
menu->allowFocusRemoval(true);
if(error_message != L"") delete temp;
{ //once finished you'll never end up here
verbosestream<<"error_message = " smgr->clear();
<<wide_to_narrow(error_message)<<std::endl; kill = menudata.kill;
GUIMessageMenu *menu2 =
new GUIMessageMenu(guienv, guiroot, -1,
&g_menumgr, error_message.c_str());
menu2->drop();
error_message = L"";
} }
// Time is in milliseconds, for clouds //update worldspecs (necessary as new world may have been created)
u32 lasttime = device->getTimer()->getTime(); worldspecs = getAvailableWorlds();
MenuMusicFetcher soundfetcher; if (menudata.name == "")
ISoundManager *sound = NULL; menudata.name = std::string("Guest") + itos(myrand_range(1000,9999));
#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 else
dtime = 0; playername = menudata.name;
lasttime = time;
}
//driver->beginScene(true, true, video::SColor(255,0,0,0)); password = translatePassword(playername, narrow_to_wide(menudata.password));
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);
//infostream<<"Main: password hash: '"<<password<<"'"<<std::endl; //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl;
address = wide_to_narrow(menudata.address); address = menudata.address;
int newport = stoi(wide_to_narrow(menudata.port)); int newport = stoi(menudata.port);
if(newport != 0) if(newport != 0)
port = newport; port = newport;
simple_singleplayer_mode = menudata.simple_singleplayer_mode; simple_singleplayer_mode = menudata.simple_singleplayer_mode;
// Save settings // 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("name", playername);
g_settings->set("address", address); g_settings->set("address", address);
g_settings->set("port", itos(port)); 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", g_settings->set("selected_world_path",
worldspecs[menudata.selected_world].path); worldspecs[menudata.selected_world].path);
@ -2010,8 +1660,8 @@ int main(int argc, char *argv[])
{ {
ServerListSpec server; ServerListSpec server;
server["name"] = menudata.servername; server["name"] = menudata.servername;
server["address"] = wide_to_narrow(menudata.address); server["address"] = menudata.address;
server["port"] = wide_to_narrow(menudata.port); server["port"] = menudata.port;
server["description"] = menudata.serverdescription; server["description"] = menudata.serverdescription;
ServerList::insert(server); ServerList::insert(server);
} }
@ -2023,29 +1673,6 @@ int main(int argc, char *argv[])
<<" ["<<worldspec.path<<"]"<<std::endl; <<" ["<<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 local game
if(current_address == "") if(current_address == "")
{ {
@ -2085,8 +1712,10 @@ int main(int argc, char *argv[])
} }
// Break out of menu-game loop to shut down cleanly // 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; break;
}
/* /*
Run game Run game

@ -3237,17 +3237,18 @@ v2s16 ServerMap::getSectorPos(std::string dirname)
{ {
unsigned int x, y; unsigned int x, y;
int r; int r;
size_t spos = dirname.rfind(DIR_DELIM_C) + 1; std::string component;
assert(spos != std::string::npos); fs::RemoveLastPathComponent(dirname, &component, 1);
if(dirname.size() - spos == 8) if(component.size() == 8)
{ {
// Old layout // 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 // 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... // Sign-extend the 12 bit values up to 16 bits...
if(x&0x800) x|=0xF000; if(x&0x800) x|=0xF000;
if(y&0x800) y|=0xF000; if(y&0x800) y|=0xF000;

@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#include "mods.h" #include "mods.h"
#include "main.h"
#include "filesys.h" #include "filesys.h"
#include "strfnd.h" #include "strfnd.h"
#include "log.h" #include "log.h"
@ -25,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h" #include "settings.h"
#include "strfnd.h" #include "strfnd.h"
#include <cctype> #include <cctype>
#include "convert_json.h"
static bool parseDependsLine(std::istream &is, static bool parseDependsLine(std::istream &is,
std::string &dep, std::set<char> &symbols) std::string &dep, std::set<char> &symbols)
@ -389,3 +391,29 @@ void ModConfiguration::resolveDependencies()
// Step 4: write back list of unsatisfied mods // Step 4: write back list of unsatisfied mods
m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end()); 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

@ -29,6 +29,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <map> #include <map>
#include <exception> #include <exception>
#include <list> #include <list>
#include "json/json.h"
#include "config.h"
#if USE_CURL
#include <curl/curl.h>
#endif
#define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_" #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 #endif

@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h" #include "porting.h"
#include "log.h" #include "log.h"
#include "json/json.h" #include "json/json.h"
#include "convert_json.h"
#if USE_CURL #if USE_CURL
#include <curl/curl.h> #include <curl/curl.h>
#endif #endif
@ -68,35 +69,22 @@ std::vector<ServerListSpec> getLocal()
#if USE_CURL #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::vector<ServerListSpec> getOnline()
{ {
std::string liststring; Json::Value root = fetchJsonValue((g_settings->get("serverlist_url")+"/list").c_str(),0);
CURL *curl;
curl = curl_easy_init(); std::vector<ServerListSpec> serverlist;
if (curl)
if (root.isArray()) {
for (unsigned int i = 0; i < root.size(); i++)
{ {
CURLcode res; if (root[i].isObject()) {
serverlist.push_back(root[i]);
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);
} }
return ServerList::deSerializeJson(liststring); }
}
return serverlist;
} }
#endif #endif
@ -189,30 +177,6 @@ std::string serialize(std::vector<ServerListSpec> serverlist)
return liststring; 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) std::string serializeJson(std::vector<ServerListSpec> serverlist)
{ {
Json::Value root; Json::Value root;

@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h" #include "settings.h"
#include "log.h" #include "log.h"
#include "util/string.h" #include "util/string.h"
#include "filesys.h"
#include "voxelalgorithms.h" #include "voxelalgorithms.h"
#include "inventory.h" #include "inventory.h"
#include "util/numeric.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 struct TestSettings: public TestBase
{ {
void Run() void Run()
@ -1785,6 +1992,7 @@ void run_tests()
infostream<<"run_tests() started"<<std::endl; infostream<<"run_tests() started"<<std::endl;
TEST(TestUtilities); TEST(TestUtilities);
TEST(TestPath);
TEST(TestSettings); TEST(TestSettings);
TEST(TestCompress); TEST(TestCompress);
TEST(TestSerialization); TEST(TestSerialization);

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB