Organize builtin into subdirectories
authorShadowNinja <shadowninja@minetest.net>
Sun, 27 Apr 2014 21:55:49 +0000 (17:55 -0400)
committerShadowNinja <shadowninja@minetest.net>
Wed, 7 May 2014 21:14:23 +0000 (17:14 -0400)
69 files changed:
builtin/async/init.lua [new file with mode: 0644]
builtin/async_env.lua [deleted file]
builtin/async_event.lua [deleted file]
builtin/auth.lua [deleted file]
builtin/builtin.lua [deleted file]
builtin/chatcommands.lua [deleted file]
builtin/common/async_event.lua [new file with mode: 0644]
builtin/common/misc_helpers.lua [new file with mode: 0644]
builtin/common/serialize.lua [new file with mode: 0644]
builtin/common/vector.lua [new file with mode: 0644]
builtin/deprecated.lua [deleted file]
builtin/detached_inventory.lua [deleted file]
builtin/falling.lua [deleted file]
builtin/features.lua [deleted file]
builtin/filterlist.lua [deleted file]
builtin/forceloading.lua [deleted file]
builtin/game/auth.lua [new file with mode: 0644]
builtin/game/chatcommands.lua [new file with mode: 0644]
builtin/game/deprecated.lua [new file with mode: 0644]
builtin/game/detached_inventory.lua [new file with mode: 0644]
builtin/game/falling.lua [new file with mode: 0644]
builtin/game/features.lua [new file with mode: 0644]
builtin/game/forceloading.lua [new file with mode: 0644]
builtin/game/init.lua [new file with mode: 0644]
builtin/game/item.lua [new file with mode: 0644]
builtin/game/item_entity.lua [new file with mode: 0644]
builtin/game/misc.lua [new file with mode: 0644]
builtin/game/privileges.lua [new file with mode: 0644]
builtin/game/register.lua [new file with mode: 0644]
builtin/game/statbars.lua [new file with mode: 0644]
builtin/game/static_spawn.lua [new file with mode: 0644]
builtin/game/voxelarea.lua [new file with mode: 0644]
builtin/gamemgr.lua [deleted file]
builtin/init.lua [new file with mode: 0644]
builtin/item.lua [deleted file]
builtin/item_entity.lua [deleted file]
builtin/mainmenu.lua [deleted file]
builtin/mainmenu/filterlist.lua [new file with mode: 0644]
builtin/mainmenu/gamemgr.lua [new file with mode: 0644]
builtin/mainmenu/init.lua [new file with mode: 0644]
builtin/mainmenu/menubar.lua [new file with mode: 0644]
builtin/mainmenu/modmgr.lua [new file with mode: 0644]
builtin/mainmenu/modstore.lua [new file with mode: 0644]
builtin/mainmenu/textures.lua [new file with mode: 0644]
builtin/misc.lua [deleted file]
builtin/misc_helpers.lua [deleted file]
builtin/misc_register.lua [deleted file]
builtin/mm_menubar.lua [deleted file]
builtin/mm_textures.lua [deleted file]
builtin/modmgr.lua [deleted file]
builtin/modstore.lua [deleted file]
builtin/privileges.lua [deleted file]
builtin/serialize.lua [deleted file]
builtin/statbars.lua [deleted file]
builtin/static_spawn.lua [deleted file]
builtin/vector.lua [deleted file]
builtin/voxelarea.lua [deleted file]
src/defaultsettings.cpp
src/guiEngine.cpp
src/script/cpp_api/s_async.cpp
src/script/cpp_api/s_base.cpp
src/script/lua_api/l_mainmenu.cpp
src/script/lua_api/l_mainmenu.h
src/script/lua_api/l_server.cpp
src/script/lua_api/l_util.cpp
src/script/lua_api/l_util.h
src/script/scripting_game.cpp
src/script/scripting_mainmenu.cpp
src/server.cpp

diff --git a/builtin/async/init.lua b/builtin/async/init.lua
new file mode 100644 (file)
index 0000000..79b1a0a
--- /dev/null
@@ -0,0 +1,18 @@
+engine.log("info", "Initializing Asynchronous environment")
+
+local core = engine or minetest
+
+function core.job_processor(serialized_func, serialized_param)
+       local func = loadstring(serialized_func)
+       local param = core.deserialize(serialized_param)
+       local retval = nil
+
+       if type(func) == "function" then
+               retval = core.serialize(func(param))
+       else
+               core.log("error", "ASYNC WORKER: Unable to deserialize function")
+       end
+
+       return retval or core.serialize(nil)
+end
+
diff --git a/builtin/async_env.lua b/builtin/async_env.lua
deleted file mode 100644 (file)
index cdcb82e..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-engine.log("info", "Initializing Asynchronous environment")
-local tbl = engine or minetest
-
-minetest = tbl
-dofile(SCRIPTDIR .. DIR_DELIM .. "serialize.lua")
-dofile(SCRIPTDIR .. DIR_DELIM .. "misc_helpers.lua")
-
-function tbl.job_processor(serialized_func, serialized_param)
-       local func = loadstring(serialized_func)
-       local param = tbl.deserialize(serialized_param)
-       local retval = nil
-
-       if type(func) == "function" then
-               retval = tbl.serialize(func(param))
-       else
-               tbl.log("error", "ASYNC WORKER: Unable to deserialize function")
-       end
-
-       return retval or tbl.serialize(nil)
-end
-
diff --git a/builtin/async_event.lua b/builtin/async_event.lua
deleted file mode 100644 (file)
index 2c3fb8f..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-local tbl = engine or minetest
-
-local SCRIPTDIR = SCRIPTDIR or tbl.get_scriptdir()
-minetest = tbl
-dofile(SCRIPTDIR .. DIR_DELIM .. "serialize.lua")
-
-tbl.async_jobs = {}
-
-local function handle_job(jobid, serialized_retval)
-       local retval = tbl.deserialize(serialized_retval)
-       assert(type(tbl.async_jobs[jobid]) == "function")
-       tbl.async_jobs[jobid](retval)
-       tbl.async_jobs[jobid] = nil
-end
-
-if engine ~= nil then
-       tbl.async_event_handler = handle_job
-else
-       minetest.register_globalstep(function(dtime)
-               for i, job in ipairs(tbl.get_finished_jobs()) do
-                       handle_job(job.jobid, job.retval)
-               end
-       end)
-end
-
-function tbl.handle_async(func, parameter, callback)
-       -- Serialize function
-       local serialized_func = string.dump(func)
-
-       assert(serialized_func ~= nil)
-
-       -- Serialize parameters
-       local serialized_param = tbl.serialize(parameter)
-
-       if serialized_param == nil then
-               return false
-       end
-
-       local jobid = tbl.do_async_callback(serialized_func, serialized_param)
-
-       tbl.async_jobs[jobid] = callback
-
-       return true
-end
-
diff --git a/builtin/auth.lua b/builtin/auth.lua
deleted file mode 100644 (file)
index b6cca60..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
--- Minetest: builtin/auth.lua
-
---
--- Authentication handler
---
-
-function minetest.string_to_privs(str, delim)
-       assert(type(str) == "string")
-       delim = delim or ','
-       privs = {}
-       for _, priv in pairs(string.split(str, delim)) do
-               privs[priv:trim()] = true
-       end
-       return privs
-end
-
-function minetest.privs_to_string(privs, delim)
-       assert(type(privs) == "table")
-       delim = delim or ','
-       list = {}
-       for priv, bool in pairs(privs) do
-               if bool then
-                       table.insert(list, priv)
-               end
-       end
-       return table.concat(list, delim)
-end
-
-assert(minetest.string_to_privs("a,b").b == true)
-assert(minetest.privs_to_string({a=true,b=true}) == "a,b")
-
-minetest.auth_file_path = minetest.get_worldpath().."/auth.txt"
-minetest.auth_table = {}
-
-local function read_auth_file()
-       local newtable = {}
-       local file, errmsg = io.open(minetest.auth_file_path, 'rb')
-       if not file then
-               minetest.log("info", minetest.auth_file_path.." could not be opened for reading ("..errmsg.."); assuming new world")
-               return
-       end
-       for line in file:lines() do
-               if line ~= "" then
-                       local name, password, privilegestring = string.match(line, "([^:]*):([^:]*):([^:]*)")
-                       if not name or not password or not privilegestring then
-                               error("Invalid line in auth.txt: "..dump(line))
-                       end
-                       local privileges = minetest.string_to_privs(privilegestring)
-                       newtable[name] = {password=password, privileges=privileges}
-               end
-       end
-       io.close(file)
-       minetest.auth_table = newtable
-       minetest.notify_authentication_modified()
-end
-
-local function save_auth_file()
-       local newtable = {}
-       -- Check table for validness before attempting to save
-       for name, stuff in pairs(minetest.auth_table) do
-               assert(type(name) == "string")
-               assert(name ~= "")
-               assert(type(stuff) == "table")
-               assert(type(stuff.password) == "string")
-               assert(type(stuff.privileges) == "table")
-       end
-       local file, errmsg = io.open(minetest.auth_file_path, 'w+b')
-       if not file then
-               error(minetest.auth_file_path.." could not be opened for writing: "..errmsg)
-       end
-       for name, stuff in pairs(minetest.auth_table) do
-               local privstring = minetest.privs_to_string(stuff.privileges)
-               file:write(name..":"..stuff.password..":"..privstring..'\n')
-       end
-       io.close(file)
-end
-
-read_auth_file()
-
-minetest.builtin_auth_handler = {
-       get_auth = function(name)
-               assert(type(name) == "string")
-               -- Figure out what password to use for a new player (singleplayer
-               -- always has an empty password, otherwise use default, which is
-               -- usually empty too)
-               local new_password_hash = ""
-               -- If not in authentication table, return nil
-               if not minetest.auth_table[name] then
-                       return nil
-               end
-               -- Figure out what privileges the player should have.
-               -- Take a copy of the privilege table
-               local privileges = {}
-               for priv, _ in pairs(minetest.auth_table[name].privileges) do
-                       privileges[priv] = true
-               end
-               -- If singleplayer, give all privileges except those marked as give_to_singleplayer = false
-               if minetest.is_singleplayer() then
-                       for priv, def in pairs(minetest.registered_privileges) do
-                               if def.give_to_singleplayer then
-                                       privileges[priv] = true
-                               end
-                       end
-               -- For the admin, give everything
-               elseif name == minetest.setting_get("name") then
-                       for priv, def in pairs(minetest.registered_privileges) do
-                               privileges[priv] = true
-                       end
-               end
-               -- All done
-               return {
-                       password = minetest.auth_table[name].password,
-                       privileges = privileges,
-               }
-       end,
-       create_auth = function(name, password)
-               assert(type(name) == "string")
-               assert(type(password) == "string")
-               minetest.log('info', "Built-in authentication handler adding player '"..name.."'")
-               minetest.auth_table[name] = {
-                       password = password,
-                       privileges = minetest.string_to_privs(minetest.setting_get("default_privs")),
-               }
-               save_auth_file()
-       end,
-       set_password = function(name, password)
-               assert(type(name) == "string")
-               assert(type(password) == "string")
-               if not minetest.auth_table[name] then
-                       minetest.builtin_auth_handler.create_auth(name, password)
-               else
-                       minetest.log('info', "Built-in authentication handler setting password of player '"..name.."'")
-                       minetest.auth_table[name].password = password
-                       save_auth_file()
-               end
-               return true
-       end,
-       set_privileges = function(name, privileges)
-               assert(type(name) == "string")
-               assert(type(privileges) == "table")
-               if not minetest.auth_table[name] then
-                       minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
-               end
-               minetest.auth_table[name].privileges = privileges
-               minetest.notify_authentication_modified(name)
-               save_auth_file()
-       end,
-       reload = function()
-               read_auth_file()
-               return true
-       end,
-}
-
-function minetest.register_authentication_handler(handler)
-       if minetest.registered_auth_handler then
-               error("Add-on authentication handler already registered by "..minetest.registered_auth_handler_modname)
-       end
-       minetest.registered_auth_handler = handler
-       minetest.registered_auth_handler_modname = minetest.get_current_modname()
-end
-
-function minetest.get_auth_handler()
-       if minetest.registered_auth_handler then
-               return minetest.registered_auth_handler
-       end
-       return minetest.builtin_auth_handler
-end
-
-function minetest.set_player_password(name, password)
-       if minetest.get_auth_handler().set_password then
-               minetest.get_auth_handler().set_password(name, password)
-       end
-end
-
-function minetest.set_player_privs(name, privs)
-       if minetest.get_auth_handler().set_privileges then
-               minetest.get_auth_handler().set_privileges(name, privs)
-       end
-end
-
-function minetest.auth_reload()
-       if minetest.get_auth_handler().reload then
-               return minetest.get_auth_handler().reload()
-       end
-       return false
-end
-
-
diff --git a/builtin/builtin.lua b/builtin/builtin.lua
deleted file mode 100644 (file)
index 1babe00..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
---
--- This file contains built-in stuff in Minetest implemented in Lua.
---
--- It is always loaded and executed after registration of the C API,
--- before loading and running any mods.
---
-
--- Initialize some very basic things
-print = minetest.debug
-math.randomseed(os.time())
-os.setlocale("C", "numeric")
-
--- Load other files
-local modpath = minetest.get_modpath("__builtin")
-dofile(modpath.."/serialize.lua")
-dofile(modpath.."/misc_helpers.lua")
-dofile(modpath.."/item.lua")
-dofile(modpath.."/misc_register.lua")
-dofile(modpath.."/item_entity.lua")
-dofile(modpath.."/deprecated.lua")
-dofile(modpath.."/misc.lua")
-dofile(modpath.."/privileges.lua")
-dofile(modpath.."/auth.lua")
-dofile(modpath.."/chatcommands.lua")
-dofile(modpath.."/static_spawn.lua")
-dofile(modpath.."/detached_inventory.lua")
-dofile(modpath.."/falling.lua")
-dofile(modpath.."/features.lua")
-dofile(modpath.."/voxelarea.lua")
-dofile(modpath.."/vector.lua")
-dofile(modpath.."/forceloading.lua")
-dofile(modpath.."/statbars.lua")
diff --git a/builtin/chatcommands.lua b/builtin/chatcommands.lua
deleted file mode 100644 (file)
index f8df83d..0000000
+++ /dev/null
@@ -1,725 +0,0 @@
--- Minetest: builtin/chatcommands.lua
-
---
--- Chat command handler
---
-
-minetest.chatcommands = {}
-function minetest.register_chatcommand(cmd, def)
-       def = def or {}
-       def.params = def.params or ""
-       def.description = def.description or ""
-       def.privs = def.privs or {}
-       minetest.chatcommands[cmd] = def
-end
-
-minetest.register_on_chat_message(function(name, message)
-       local cmd, param = string.match(message, "^/([^ ]+) *(.*)")
-       if not param then
-               param = ""
-       end
-       local cmd_def = minetest.chatcommands[cmd]
-       if cmd_def then
-               local has_privs, missing_privs = minetest.check_player_privs(name, cmd_def.privs)
-               if has_privs then
-                       cmd_def.func(name, param)
-               else
-                       minetest.chat_send_player(name, "You don't have permission to run this command (missing privileges: "..table.concat(missing_privs, ", ")..")")
-               end
-               return true -- handled chat message
-       end
-       return false
-end)
-
---
--- Chat commands
---
-minetest.register_chatcommand("me", {
-       params = "<action>",
-       description = "chat action (eg. /me orders a pizza)",
-       privs = {shout=true},
-       func = function(name, param)
-               minetest.chat_send_all("* " .. name .. " " .. param)
-       end,
-})
-
-minetest.register_chatcommand("help", {
-       privs = {},
-       params = "(nothing)/all/privs/<cmd>",
-       description = "Get help for commands or list privileges",
-       func = function(name, param)
-               local format_help_line = function(cmd, def)
-                       local msg = "/"..cmd
-                       if def.params and def.params ~= "" then msg = msg .. " " .. def.params end
-                       if def.description and def.description ~= "" then msg = msg .. ": " .. def.description end
-                       return msg
-               end
-               if param == "" then
-                       local msg = ""
-                       cmds = {}
-                       for cmd, def in pairs(minetest.chatcommands) do
-                               if minetest.check_player_privs(name, def.privs) then
-                                       table.insert(cmds, cmd)
-                               end
-                       end
-                       minetest.chat_send_player(name, "Available commands: "..table.concat(cmds, " "))
-                       minetest.chat_send_player(name, "Use '/help <cmd>' to get more information, or '/help all' to list everything.")
-               elseif param == "all" then
-                       minetest.chat_send_player(name, "Available commands:")
-                       for cmd, def in pairs(minetest.chatcommands) do
-                               if minetest.check_player_privs(name, def.privs) then
-                                       minetest.chat_send_player(name, format_help_line(cmd, def))
-                               end
-                       end
-               elseif param == "privs" then
-                       minetest.chat_send_player(name, "Available privileges:")
-                       for priv, def in pairs(minetest.registered_privileges) do
-                               minetest.chat_send_player(name, priv..": "..def.description)
-                       end
-               else
-                       local cmd = param
-                       def = minetest.chatcommands[cmd]
-                       if not def then
-                               minetest.chat_send_player(name, "Command not available: "..cmd)
-                       else
-                               minetest.chat_send_player(name, format_help_line(cmd, def))
-                       end
-               end
-       end,
-})
-minetest.register_chatcommand("privs", {
-       params = "<name>",
-       description = "print out privileges of player",
-       func = function(name, param)
-               if param == "" then
-                       param = name
-               else
-                       --[[if not minetest.check_player_privs(name, {privs=true}) then
-                               minetest.chat_send_player(name, "Privileges of "..param.." are hidden from you.")
-                               return
-                       end]]
-               end
-               minetest.chat_send_player(name, "Privileges of "..param..": "..minetest.privs_to_string(minetest.get_player_privs(param), ' '))
-       end,
-})
-minetest.register_chatcommand("grant", {
-       params = "<name> <privilege>|all",
-       description = "Give privilege to player",
-       privs = {},
-       func = function(name, param)
-               if not minetest.check_player_privs(name, {privs=true}) and
-                               not minetest.check_player_privs(name, {basic_privs=true}) then
-                       minetest.chat_send_player(name, "Your privileges are insufficient.")
-                       return
-               end
-               local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
-               if not grantname or not grantprivstr then
-                       minetest.chat_send_player(name, "Invalid parameters (see /help grant)")
-                       return
-               elseif not minetest.auth_table[grantname] then
-                       minetest.chat_send_player(name, "Player "..grantname.." does not exist.")
-                       return
-               end
-               local grantprivs = minetest.string_to_privs(grantprivstr)
-               if grantprivstr == "all" then
-                       grantprivs = minetest.registered_privileges
-               end
-               local privs = minetest.get_player_privs(grantname)
-               local privs_known = true
-               for priv, _ in pairs(grantprivs) do
-                       if priv ~= "interact" and priv ~= "shout" and priv ~= "interact_extra" and not minetest.check_player_privs(name, {privs=true}) then
-                               minetest.chat_send_player(name, "Your privileges are insufficient.")
-                               return
-                       end
-                       if not minetest.registered_privileges[priv] then
-                               minetest.chat_send_player(name, "Unknown privilege: "..priv)
-                               privs_known = false
-                       end
-                       privs[priv] = true
-               end
-               if not privs_known then
-                       return
-               end
-               minetest.set_player_privs(grantname, privs)
-               minetest.log(name..' granted ('..minetest.privs_to_string(grantprivs, ', ')..') privileges to '..grantname)
-               minetest.chat_send_player(name, "Privileges of "..grantname..": "..minetest.privs_to_string(minetest.get_player_privs(grantname), ' '))
-               if grantname ~= name then
-                       minetest.chat_send_player(grantname, name.." granted you privileges: "..minetest.privs_to_string(grantprivs, ' '))
-               end
-       end,
-})
-minetest.register_chatcommand("revoke", {
-       params = "<name> <privilege>|all",
-       description = "Remove privilege from player",
-       privs = {},
-       func = function(name, param)
-               if not minetest.check_player_privs(name, {privs=true}) and
-                               not minetest.check_player_privs(name, {basic_privs=true}) then
-                       minetest.chat_send_player(name, "Your privileges are insufficient.")
-                       return
-               end
-               local revokename, revokeprivstr = string.match(param, "([^ ]+) (.+)")
-               if not revokename or not revokeprivstr then
-                       minetest.chat_send_player(name, "Invalid parameters (see /help revoke)")
-                       return
-               elseif not minetest.auth_table[revokename] then
-                       minetest.chat_send_player(name, "Player "..revokename.." does not exist.")
-                       return
-               end
-               local revokeprivs = minetest.string_to_privs(revokeprivstr)
-               local privs = minetest.get_player_privs(revokename)
-               for priv, _ in pairs(revokeprivs) do
-                       if priv ~= "interact" and priv ~= "shout" and priv ~= "interact_extra" and not minetest.check_player_privs(name, {privs=true}) then
-                               minetest.chat_send_player(name, "Your privileges are insufficient.")
-                               return
-                       end
-               end
-               if revokeprivstr == "all" then
-                       privs = {}
-               else
-                       for priv, _ in pairs(revokeprivs) do
-                               privs[priv] = nil
-                       end
-               end
-               minetest.set_player_privs(revokename, privs)
-               minetest.log(name..' revoked ('..minetest.privs_to_string(revokeprivs, ', ')..') privileges from '..revokename)
-               minetest.chat_send_player(name, "Privileges of "..revokename..": "..minetest.privs_to_string(minetest.get_player_privs(revokename), ' '))
-               if revokename ~= name then
-                       minetest.chat_send_player(revokename, name.." revoked privileges from you: "..minetest.privs_to_string(revokeprivs, ' '))
-               end
-       end,
-})
-minetest.register_chatcommand("setpassword", {
-       params = "<name> <password>",
-       description = "set given password",
-       privs = {password=true},
-       func = function(name, param)
-               local toname, raw_password = string.match(param, "^([^ ]+) +(.+)$")
-               if not toname then
-                       toname = string.match(param, "^([^ ]+) *$")
-                       raw_password = nil
-               end
-               if not toname then
-                       minetest.chat_send_player(name, "Name field required")
-                       return
-               end
-               local actstr = "?"
-               if not raw_password then
-                       minetest.set_player_password(toname, "")
-                       actstr = "cleared"
-               else
-                       minetest.set_player_password(toname, minetest.get_password_hash(toname, raw_password))
-                       actstr = "set"
-               end
-               minetest.chat_send_player(name, "Password of player \""..toname.."\" "..actstr)
-               if toname ~= name then
-                       minetest.chat_send_player(toname, "Your password was "..actstr.." by "..name)
-               end
-       end,
-})
-minetest.register_chatcommand("clearpassword", {
-       params = "<name>",
-       description = "set empty password",
-       privs = {password=true},
-       func = function(name, param)
-               toname = param
-               if toname == "" then
-                       minetest.chat_send_player(name, "Name field required")
-                       return
-               end
-               minetest.set_player_password(toname, '')
-               minetest.chat_send_player(name, "Password of player \""..toname.."\" cleared")
-       end,
-})
-
-minetest.register_chatcommand("auth_reload", {
-       params = "",
-       description = "reload authentication data",
-       privs = {server=true},
-       func = function(name, param)
-               local done = minetest.auth_reload()
-               if done then
-                       minetest.chat_send_player(name, "Done.")
-               else
-                       minetest.chat_send_player(name, "Failed.")
-               end
-       end,
-})
-
-minetest.register_chatcommand("teleport", {
-       params = "<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>",
-       description = "teleport to given position",
-       privs = {teleport=true},
-       func = function(name, param)
-               -- Returns (pos, true) if found, otherwise (pos, false)
-               local function find_free_position_near(pos)
-                       local tries = {
-                               {x=1,y=0,z=0},
-                               {x=-1,y=0,z=0},
-                               {x=0,y=0,z=1},
-                               {x=0,y=0,z=-1},
-                       }
-                       for _, d in ipairs(tries) do
-                               local p = {x = pos.x+d.x, y = pos.y+d.y, z = pos.z+d.z}
-                               local n = minetest.get_node_or_nil(p)
-                               if n and n.name then
-                                       local def = minetest.registered_nodes[n.name]
-                                       if def and not def.walkable then
-                                               return p, true
-                                       end
-                               end
-                       end
-                       return pos, false
-               end
-
-               local teleportee = nil
-               local p = {}
-               p.x, p.y, p.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
-               p.x = tonumber(p.x)
-               p.y = tonumber(p.y)
-               p.z = tonumber(p.z)
-               teleportee = minetest.get_player_by_name(name)
-               if teleportee and p.x and p.y and p.z then
-                       minetest.chat_send_player(name, "Teleporting to ("..p.x..", "..p.y..", "..p.z..")")
-                       teleportee:setpos(p)
-                       return
-               end
-               
-               local teleportee = nil
-               local p = nil
-               local target_name = nil
-               target_name = string.match(param, "^([^ ]+)$")
-               teleportee = minetest.get_player_by_name(name)
-               if target_name then
-                       local target = minetest.get_player_by_name(target_name)
-                       if target then
-                               p = target:getpos()
-                       end
-               end
-               if teleportee and p then
-                       p = find_free_position_near(p)
-                       minetest.chat_send_player(name, "Teleporting to "..target_name.." at ("..p.x..", "..p.y..", "..p.z..")")
-                       teleportee:setpos(p)
-                       return
-               end
-               
-               if minetest.check_player_privs(name, {bring=true}) then
-                       local teleportee = nil
-                       local p = {}
-                       local teleportee_name = nil
-                       teleportee_name, p.x, p.y, p.z = string.match(param, "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
-                       p.x = tonumber(p.x)
-                       p.y = tonumber(p.y)
-                       p.z = tonumber(p.z)
-                       if teleportee_name then
-                               teleportee = minetest.get_player_by_name(teleportee_name)
-                       end
-                       if teleportee and p.x and p.y and p.z then
-                               minetest.chat_send_player(name, "Teleporting "..teleportee_name.." to ("..p.x..", "..p.y..", "..p.z..")")
-                               teleportee:setpos(p)
-                               return
-                       end
-                       
-                       local teleportee = nil
-                       local p = nil
-                       local teleportee_name = nil
-                       local target_name = nil
-                       teleportee_name, target_name = string.match(param, "^([^ ]+) +([^ ]+)$")
-                       if teleportee_name then
-                               teleportee = minetest.get_player_by_name(teleportee_name)
-                       end
-                       if target_name then
-                               local target = minetest.get_player_by_name(target_name)
-                               if target then
-                                       p = target:getpos()
-                               end
-                       end
-                       if teleportee and p then
-                               p = find_free_position_near(p)
-                               minetest.chat_send_player(name, "Teleporting "..teleportee_name.." to "..target_name.." at ("..p.x..", "..p.y..", "..p.z..")")
-                               teleportee:setpos(p)
-                               return
-                       end
-               end
-
-               minetest.chat_send_player(name, "Invalid parameters (\""..param.."\") or player not found (see /help teleport)")
-               return
-       end,
-})
-
-minetest.register_chatcommand("set", {
-       params = "[-n] <name> <value> | <name>",
-       description = "set or read server configuration setting",
-       privs = {server=true},
-       func = function(name, param)
-               local arg, setname, setvalue = string.match(param, "(-[n]) ([^ ]+) (.+)")
-               if arg and arg == "-n" and setname and setvalue then
-                       minetest.setting_set(setname, setvalue)
-                       minetest.chat_send_player(name, setname.." = "..setvalue)
-                       return
-               end
-               local setname, setvalue = string.match(param, "([^ ]+) (.+)")
-               if setname and setvalue then
-                       if not minetest.setting_get(setname) then
-                               minetest.chat_send_player(name, "Failed. Use '/set -n <name> <value>' to create a new setting.")
-                               return
-                       end
-                       minetest.setting_set(setname, setvalue)
-                       minetest.chat_send_player(name, setname.." = "..setvalue)
-                       return
-               end
-               local setname = string.match(param, "([^ ]+)")
-               if setname then
-                       local setvalue = minetest.setting_get(setname)
-                       if not setvalue then
-                               setvalue = "<not set>"
-                       end
-                       minetest.chat_send_player(name, setname.." = "..setvalue)
-                       return
-               end
-               minetest.chat_send_player(name, "Invalid parameters (see /help set)")
-       end,
-})
-
-minetest.register_chatcommand("mods", {
-       params = "",
-       description = "lists mods installed on the server",
-       privs = {},
-       func = function(name, param)
-               local response = ""
-               local modnames = minetest.get_modnames()
-               for i, mod in ipairs(modnames) do
-                       response = response .. mod
-                       -- Add space if not at the end
-                       if i ~= #modnames then
-                               response = response .. " "
-                       end
-               end
-               minetest.chat_send_player(name, response)
-       end,
-})
-
-local function handle_give_command(cmd, giver, receiver, stackstring)
-       minetest.log("action", giver.." invoked "..cmd..', stackstring="'
-                       ..stackstring..'"')
-       minetest.log(cmd..' invoked, stackstring="'..stackstring..'"')
-       local itemstack = ItemStack(stackstring)
-       if itemstack:is_empty() then
-               minetest.chat_send_player(giver, 'error: cannot give an empty item')
-               return
-       elseif not itemstack:is_known() then
-               minetest.chat_send_player(giver, 'error: cannot give an unknown item')
-               return
-       end
-       local receiverref = minetest.get_player_by_name(receiver)
-       if receiverref == nil then
-               minetest.chat_send_player(giver, receiver..' is not a known player')
-               return
-       end
-       local leftover = receiverref:get_inventory():add_item("main", itemstack)
-       if leftover:is_empty() then
-               partiality = ""
-       elseif leftover:get_count() == itemstack:get_count() then
-               partiality = "could not be "
-       else
-               partiality = "partially "
-       end
-       -- The actual item stack string may be different from what the "giver"
-       -- entered (e.g. big numbers are always interpreted as 2^16-1).
-       stackstring = itemstack:to_string()
-       if giver == receiver then
-               minetest.chat_send_player(giver, '"'..stackstring
-                       ..'" '..partiality..'added to inventory.');
-       else
-               minetest.chat_send_player(giver, '"'..stackstring
-                       ..'" '..partiality..'added to '..receiver..'\'s inventory.');
-               minetest.chat_send_player(receiver, '"'..stackstring
-                       ..'" '..partiality..'added to inventory.');
-       end
-end
-
-minetest.register_chatcommand("give", {
-       params = "<name> <itemstring>",
-       description = "give item to player",
-       privs = {give=true},
-       func = function(name, param)
-               local toname, itemstring = string.match(param, "^([^ ]+) +(.+)$")
-               if not toname or not itemstring then
-                       minetest.chat_send_player(name, "name and itemstring required")
-                       return
-               end
-               handle_give_command("/give", name, toname, itemstring)
-       end,
-})
-minetest.register_chatcommand("giveme", {
-       params = "<itemstring>",
-       description = "give item to yourself",
-       privs = {give=true},
-       func = function(name, param)
-               local itemstring = string.match(param, "(.+)$")
-               if not itemstring then
-                       minetest.chat_send_player(name, "itemstring required")
-                       return
-               end
-               handle_give_command("/giveme", name, name, itemstring)
-       end,
-})
-minetest.register_chatcommand("spawnentity", {
-       params = "<entityname>",
-       description = "spawn entity at your position",
-       privs = {give=true, interact=true},
-       func = function(name, param)
-               local entityname = string.match(param, "(.+)$")
-               if not entityname then
-                       minetest.chat_send_player(name, "entityname required")
-                       return
-               end
-               minetest.log("action", '/spawnentity invoked, entityname="'..entityname..'"')
-               local player = minetest.get_player_by_name(name)
-               if player == nil then
-                       minetest.log("error", "Unable to spawn entity, player is nil")
-                       return true -- Handled chat message
-               end
-               local p = player:getpos()
-               p.y = p.y + 1
-               minetest.add_entity(p, entityname)
-               minetest.chat_send_player(name, '"'..entityname
-                               ..'" spawned.');
-       end,
-})
-minetest.register_chatcommand("pulverize", {
-       params = "",
-       description = "delete item in hand",
-       privs = {},
-       func = function(name, param)
-               local player = minetest.get_player_by_name(name)
-               if player == nil then
-                       minetest.log("error", "Unable to pulverize, player is nil")
-                       return true -- Handled chat message
-               end
-               if player:get_wielded_item():is_empty() then
-                       minetest.chat_send_player(name, 'Unable to pulverize, no item in hand.')
-               else
-                       player:set_wielded_item(nil)
-                       minetest.chat_send_player(name, 'An item was pulverized.')
-               end
-       end,
-})
-
--- Key = player name
-minetest.rollback_punch_callbacks = {}
-
-minetest.register_on_punchnode(function(pos, node, puncher)
-       local name = puncher:get_player_name()
-       if minetest.rollback_punch_callbacks[name] then
-               minetest.rollback_punch_callbacks[name](pos, node, puncher)
-               minetest.rollback_punch_callbacks[name] = nil
-       end
-end)
-
-minetest.register_chatcommand("rollback_check", {
-       params = "[<range>] [<seconds>] [limit]",
-       description = "check who has last touched a node or near it, "..
-                       "max. <seconds> ago (default range=0, seconds=86400=24h, limit=5)",
-       privs = {rollback=true},
-       func = function(name, param)
-               local range, seconds, limit =
-                       param:match("(%d+) *(%d*) *(%d*)")
-               range = tonumber(range) or 0
-               seconds = tonumber(seconds) or 86400
-               limit = tonumber(limit) or 5
-               if limit > 100 then
-                       minetest.chat_send_player(name, "That limit is too high!")
-                       return
-               end
-               minetest.chat_send_player(name, "Punch a node (range="..
-                               range..", seconds="..seconds.."s, limit="..limit..")")
-
-               minetest.rollback_punch_callbacks[name] = function(pos, node, puncher)
-                       local name = puncher:get_player_name()
-                       minetest.chat_send_player(name, "Checking "..minetest.pos_to_string(pos).."...")
-                       local actions = minetest.rollback_get_node_actions(pos, range, seconds, limit)
-                       local num_actions = #actions
-                       if num_actions == 0 then
-                               minetest.chat_send_player(name, "Nobody has touched the "..
-                                               "specified location in "..seconds.." seconds")
-                               return
-                       end
-                       local time = os.time()
-                       for i = num_actions, 1, -1 do
-                               local action = actions[i]
-                               minetest.chat_send_player(name,
-                                       ("%s %s %s -> %s %d seconds ago.")
-                                               :format(
-                                                       minetest.pos_to_string(action.pos),
-                                                       action.actor,
-                                                       action.oldnode.name,
-                                                       action.newnode.name,
-                                                       time - action.time))
-                       end
-               end
-       end,
-})
-
-minetest.register_chatcommand("rollback", {
-       params = "<player name> [<seconds>] | :<actor> [<seconds>]",
-       description = "revert actions of a player; default for <seconds> is 60",
-       privs = {rollback=true},
-       func = function(name, param)
-               local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)")
-               if not target_name then
-                       local player_name = nil
-                       player_name, seconds = string.match(param, "([^ ]+) *(%d*)")
-                       if not player_name then
-                               minetest.chat_send_player(name, "Invalid parameters. See /help rollback and /help rollback_check")
-                               return
-                       end
-                       target_name = "player:"..player_name
-               end
-               seconds = tonumber(seconds) or 60
-               minetest.chat_send_player(name, "Reverting actions of "..
-                               target_name.." since "..seconds.." seconds.")
-               local success, log = minetest.rollback_revert_actions_by(
-                               target_name, seconds)
-               if #log > 100 then
-                       minetest.chat_send_player(name, "(log is too long to show)")
-               else
-                       for _, line in pairs(log) do
-                               minetest.chat_send_player(name, line)
-                       end
-               end
-               if success then
-                       minetest.chat_send_player(name, "Reverting actions succeeded.")
-               else
-                       minetest.chat_send_player(name, "Reverting actions FAILED.")
-               end
-       end,
-})
-
-minetest.register_chatcommand("status", {
-       params = "",
-       description = "print server status line",
-       privs = {},
-       func = function(name, param)
-               minetest.chat_send_player(name, minetest.get_server_status())
-       end,
-})
-
-minetest.register_chatcommand("time", {
-       params = "<0...24000>",
-       description = "set time of day",
-       privs = {settime=true},
-       func = function(name, param)
-               if param == "" then
-                       minetest.chat_send_player(name, "Missing parameter")
-                       return
-               end
-               local newtime = tonumber(param)
-               if newtime == nil then
-                       minetest.chat_send_player(name, "Invalid time")
-               else
-                       minetest.set_timeofday((newtime % 24000) / 24000)
-                       minetest.chat_send_player(name, "Time of day changed.")
-                       minetest.log("action", name .. " sets time " .. newtime)
-               end
-       end,
-})
-
-minetest.register_chatcommand("shutdown", {
-       params = "",
-       description = "shutdown server",
-       privs = {server=true},
-       func = function(name, param)
-               minetest.log("action", name .. " shuts down server")
-               minetest.request_shutdown()
-               minetest.chat_send_all("*** Server shutting down (operator request).")
-       end,
-})
-
-minetest.register_chatcommand("ban", {
-       params = "<name>",
-       description = "ban IP of player",
-       privs = {ban=true},
-       func = function(name, param)
-               if param == "" then
-                       minetest.chat_send_player(name, "Ban list: " .. minetest.get_ban_list())
-                       return
-               end
-               if not minetest.get_player_by_name(param) then
-                       minetest.chat_send_player(name, "No such player")
-                       return
-               end
-               if not minetest.ban_player(param) then
-                       minetest.chat_send_player(name, "Failed to ban player")
-               else
-                       local desc = minetest.get_ban_description(param)
-                       minetest.chat_send_player(name, "Banned " .. desc .. ".")
-                       minetest.log("action", name .. " bans " .. desc .. ".")
-               end
-       end,
-})
-
-minetest.register_chatcommand("unban", {
-       params = "<name/ip>",
-       description = "remove IP ban",
-       privs = {ban=true},
-       func = function(name, param)
-               if not minetest.unban_player_or_ip(param) then
-                       minetest.chat_send_player(name, "Failed to unban player/IP")
-               else
-                       minetest.chat_send_player(name, "Unbanned " .. param)
-                       minetest.log("action", name .. " unbans " .. param)
-               end
-       end,
-})
-
-minetest.register_chatcommand("kick", {
-       params = "<name> [reason]",
-       description = "kick a player",
-       privs = {kick=true},
-       func = function(name, param)
-               local tokick, reason = string.match(param, "([^ ]+) (.+)")
-               if not tokick then
-                       tokick = param
-               end
-               if not minetest.kick_player(tokick, reason) then
-                       minetest.chat_send_player(name, "Failed to kick player " .. tokick)
-               else
-                       minetest.chat_send_player(name, "kicked " .. tokick)
-                       minetest.log("action", name .. " kicked " .. tokick)
-               end
-       end,
-})
-
-minetest.register_chatcommand("clearobjects", {
-       params = "",
-       description = "clear all objects in world",
-       privs = {server=true},
-       func = function(name, param)
-               minetest.log("action", name .. " clears all objects")
-               minetest.chat_send_all("Clearing all objects.  This may take long.  You may experience a timeout.  (by " .. name .. ")")
-               minetest.clear_objects()
-               minetest.log("action", "object clearing done")
-               minetest.chat_send_all("*** Cleared all objects.")
-       end,
-})
-
-minetest.register_chatcommand("msg", {
-       params = "<name> <message>",
-       description = "Send a private message",
-       privs = {shout=true},
-       func = function(name, param)
-               local found, _, sendto, message = param:find("^([^%s]+)%s(.+)$")
-               if found then
-                       if minetest.get_player_by_name(sendto) then
-                               minetest.log("action", "PM from "..name.." to "..sendto..": "..message)
-                               minetest.chat_send_player(sendto, "PM from "..name..": "..message)
-                               minetest.chat_send_player(name, "Message sent")
-                       else
-                               minetest.chat_send_player(name, "The player "..sendto.." is not online")
-                       end
-               else
-                       minetest.chat_send_player(name, "Invalid usage, see /help msg")
-               end
-       end,
-})
diff --git a/builtin/common/async_event.lua b/builtin/common/async_event.lua
new file mode 100644 (file)
index 0000000..ef4bf43
--- /dev/null
@@ -0,0 +1,42 @@
+
+local core = engine or minetest
+
+core.async_jobs = {}
+
+local function handle_job(jobid, serialized_retval)
+       local retval = core.deserialize(serialized_retval)
+       assert(type(core.async_jobs[jobid]) == "function")
+       core.async_jobs[jobid](retval)
+       core.async_jobs[jobid] = nil
+end
+
+if engine ~= nil then
+       core.async_event_handler = handle_job
+else
+       minetest.register_globalstep(function(dtime)
+               for i, job in ipairs(core.get_finished_jobs()) do
+                       handle_job(job.jobid, job.retval)
+               end
+       end)
+end
+
+function core.handle_async(func, parameter, callback)
+       -- Serialize function
+       local serialized_func = string.dump(func)
+
+       assert(serialized_func ~= nil)
+
+       -- Serialize parameters
+       local serialized_param = core.serialize(parameter)
+
+       if serialized_param == nil then
+               return false
+       end
+
+       local jobid = core.do_async_callback(serialized_func, serialized_param)
+
+       core.async_jobs[jobid] = callback
+
+       return true
+end
+
diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua
new file mode 100644 (file)
index 0000000..9c73497
--- /dev/null
@@ -0,0 +1,428 @@
+-- Minetest: builtin/misc_helpers.lua
+
+--------------------------------------------------------------------------------
+function basic_dump2(o)
+       if type(o) == "number" then
+               return tostring(o)
+       elseif type(o) == "string" then
+               return string.format("%q", o)
+       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 dump2(o, name, dumped)
+       name = name or "_"
+       dumped = dumped or {}
+       io.write(name, " = ")
+       if type(o) == "number" or type(o) == "string" or type(o) == "boolean"
+                       or type(o) == "function" or type(o) == "nil"
+                       or type(o) == "userdata" then
+               io.write(basic_dump2(o), "\n")
+       elseif type(o) == "table" then
+               if dumped[o] then
+                       io.write(dumped[o], "\n")
+               else
+                       dumped[o] = name
+                       io.write("{}\n") -- new table
+                       for k,v in pairs(o) do
+                               local fieldname = string.format("%s[%s]", name, basic_dump2(k))
+                               dump2(v, fieldname, dumped)
+                       end
+               end
+       else
+               error("cannot dump a " .. type(o))
+               return nil
+       end
+end
+
+--------------------------------------------------------------------------------
+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] = "[" .. dump(k, dumped) .. "] = " .. 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 file_exists(filename)
+       local f = io.open(filename, "r")
+       if f==nil then
+               return false
+       else
+               f:close()
+               return true
+       end
+end
+
+--------------------------------------------------------------------------------
+function string:trim()
+       return (self:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+assert(string.trim("\n \t\tfoo bar\t ") == "foo bar")
+
+--------------------------------------------------------------------------------
+function math.hypot(x, y)
+       local t
+       x = math.abs(x)
+       y = math.abs(y)
+       t = math.min(x, y)
+       x = math.max(x, y)
+       if x == 0 then return 0 end
+       t = t / x
+       return x * math.sqrt(1 + t * t)
+end
+
+--------------------------------------------------------------------------------
+function get_last_folder(text,count)
+       local parts = text:split(DIR_DELIM)
+       
+       if count == nil then
+               return parts[#parts]
+       end
+       
+       local retval = ""
+       for i=1,count,1 do
+               retval = retval .. parts[#parts - (count-i)] .. DIR_DELIM
+       end
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function cleanup_path(temppath)
+       
+       local parts = temppath:split("-")
+       temppath = ""   
+       for i=1,#parts,1 do
+               if temppath ~= "" then
+                       temppath = temppath .. "_"
+               end
+               temppath = temppath .. parts[i]
+       end
+       
+       parts = temppath:split(".")
+       temppath = ""   
+       for i=1,#parts,1 do
+               if temppath ~= "" then
+                       temppath = temppath .. "_"
+               end
+               temppath = temppath .. parts[i]
+       end
+       
+       parts = temppath:split("'")
+       temppath = ""   
+       for i=1,#parts,1 do
+               if temppath ~= "" then
+                       temppath = temppath .. ""
+               end
+               temppath = temppath .. parts[i]
+       end
+       
+       parts = temppath:split(" ")
+       temppath = ""   
+       for i=1,#parts,1 do
+               if temppath ~= "" then
+                       temppath = temppath
+               end
+               temppath = temppath .. parts[i]
+       end
+       
+       return temppath
+end
+
+local tbl = engine or minetest
+function tbl.formspec_escape(text)
+       if text ~= nil then
+               text = string.gsub(text,"\\","\\\\")
+               text = string.gsub(text,"%]","\\]")
+               text = string.gsub(text,"%[","\\[")
+               text = string.gsub(text,";","\\;")
+               text = string.gsub(text,",","\\,")
+       end
+       return text
+end
+
+
+function tbl.splittext(text,charlimit)
+       local retval = {}
+
+       local current_idx = 1
+       
+       local start,stop = string.find(text," ",current_idx)
+       local nl_start,nl_stop = string.find(text,"\n",current_idx)
+       local gotnewline = false
+       if nl_start ~= nil and (start == nil or nl_start < start) then
+               start = nl_start
+               stop = nl_stop
+               gotnewline = true
+       end
+       local last_line = ""
+       while start ~= nil do
+               if string.len(last_line) + (stop-start) > charlimit then
+                       table.insert(retval,last_line)
+                       last_line = ""
+               end
+               
+               if last_line ~= "" then
+                       last_line = last_line .. " "
+               end
+               
+               last_line = last_line .. string.sub(text,current_idx,stop -1)
+               
+               if gotnewline then
+                       table.insert(retval,last_line)
+                       last_line = ""
+                       gotnewline = false
+               end
+               current_idx = stop+1
+               
+               start,stop = string.find(text," ",current_idx)
+               nl_start,nl_stop = string.find(text,"\n",current_idx)
+       
+               if nl_start ~= nil and (start == nil or nl_start < start) then
+                       start = nl_start
+                       stop = nl_stop
+                       gotnewline = true
+               end
+       end
+       
+       --add last part of text
+       if string.len(last_line) + (string.len(text) - current_idx) > charlimit then
+                       table.insert(retval,last_line)
+                       table.insert(retval,string.sub(text,current_idx))
+       else
+               last_line = last_line .. " " .. string.sub(text,current_idx)
+               table.insert(retval,last_line)
+       end
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+
+if minetest then
+       local dirs1 = {9, 18, 7, 12}
+       local dirs2 = {20, 23, 22, 21}
+
+       function minetest.rotate_and_place(itemstack, placer, pointed_thing,
+                               infinitestacks, orient_flags)
+               orient_flags = orient_flags or {}
+
+               local unode = minetest.get_node_or_nil(pointed_thing.under)
+               if not unode then
+                       return
+               end
+               local undef = minetest.registered_nodes[unode.name]
+               if undef and undef.on_rightclick then
+                       undef.on_rightclick(pointed_thing.under, unode, placer,
+                                       itemstack, pointed_thing)
+                       return
+               end
+               local pitch = placer:get_look_pitch()
+               local fdir = minetest.dir_to_facedir(placer:get_look_dir())
+               local wield_name = itemstack:get_name()
+
+               local above = pointed_thing.above
+               local under = pointed_thing.under
+               local iswall = (above.y == under.y)
+               local isceiling = not iswall and (above.y < under.y)
+               local anode = minetest.get_node_or_nil(above)
+               if not anode then
+                       return
+               end
+               local pos = pointed_thing.above
+               local node = anode
+
+               if undef and undef.buildable_to then
+                       pos = pointed_thing.under
+                       node = unode
+                       iswall = false
+               end
+
+               if minetest.is_protected(pos, placer:get_player_name()) then
+                       minetest.record_protection_violation(pos,
+                                       placer:get_player_name())
+                       return
+               end
+
+               local ndef = minetest.registered_nodes[node.name]
+               if not ndef or not ndef.buildable_to then
+                       return
+               end
+
+               if orient_flags.force_floor then
+                       iswall = false
+                       isceiling = false
+               elseif orient_flags.force_ceiling then
+                       iswall = false
+                       isceiling = true
+               elseif orient_flags.force_wall then
+                       iswall = true
+                       isceiling = false
+               elseif orient_flags.invert_wall then
+                       iswall = not iswall
+               end
+
+               if iswall then
+                       minetest.set_node(pos, {name = wield_name,
+                                       param2 = dirs1[fdir+1]})
+               elseif isceiling then
+                       if orient_flags.force_facedir then
+                               minetest.set_node(pos, {name = wield_name,
+                                               param2 = 20})
+                       else
+                               minetest.set_node(pos, {name = wield_name,
+                                               param2 = dirs2[fdir+1]})
+                       end
+               else -- place right side up
+                       if orient_flags.force_facedir then
+                               minetest.set_node(pos, {name = wield_name,
+                                               param2 = 0})
+                       else
+                               minetest.set_node(pos, {name = wield_name,
+                                               param2 = fdir})
+                       end
+               end
+
+               if not infinitestacks then
+                       itemstack:take_item()
+                       return itemstack
+               end
+       end
+
+
+--------------------------------------------------------------------------------
+--Wrapper for rotate_and_place() to check for sneak and assume Creative mode
+--implies infinite stacks when performing a 6d rotation.
+--------------------------------------------------------------------------------
+
+
+       minetest.rotate_node = function(itemstack, placer, pointed_thing)
+               minetest.rotate_and_place(itemstack, placer, pointed_thing,
+                               minetest.setting_getbool("creative_mode"),
+                               {invert_wall = placer:get_player_control().sneak})
+               return itemstack
+       end
+end
+
+--------------------------------------------------------------------------------
+function tbl.explode_table_event(evt)
+       if evt ~= nil then
+               local parts = evt:split(":")
+               if #parts == 3 then
+                       local t = parts[1]:trim()
+                       local r = tonumber(parts[2]:trim())
+                       local c = tonumber(parts[3]:trim())
+                       if type(r) == "number" and type(c) == "number" and t ~= "INV" then
+                               return {type=t, row=r, column=c}
+                       end
+               end
+       end
+       return {type="INV", row=0, column=0}
+end
+
+--------------------------------------------------------------------------------
+function tbl.explode_textlist_event(evt)
+       if evt ~= nil then
+               local parts = evt:split(":")
+               if #parts == 2 then
+                       local t = parts[1]:trim()
+                       local r = tonumber(parts[2]:trim())
+                       if type(r) == "number" and t ~= "INV" then
+                               return {type=t, index=r}
+                       end
+               end
+       end
+       return {type="INV", index=0}
+end
+
+--------------------------------------------------------------------------------
+-- mainmenu only functions
+--------------------------------------------------------------------------------
+if engine ~= nil then
+       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 fgettext(text, ...)
+               text = engine.gettext(text)
+               local arg = {n=select('#', ...), ...}
+               if arg.n >= 1 then
+                       -- Insert positional parameters ($1, $2, ...)
+                       result = ''
+                       pos = 1
+                       while pos <= text:len() do
+                               newpos = text:find('[$]', pos)
+                               if newpos == nil then
+                                       result = result .. text:sub(pos)
+                                       pos = text:len() + 1
+                               else
+                                       paramindex = tonumber(text:sub(newpos+1, newpos+1))
+                                       result = result .. text:sub(pos, newpos-1) .. tostring(arg[paramindex])
+                                       pos = newpos + 2
+                               end
+                       end
+                       text = result
+               end
+               return engine.formspec_escape(text)
+       end
+end
+--------------------------------------------------------------------------------
+-- core only fct
+--------------------------------------------------------------------------------
+if minetest ~= nil then
+       --------------------------------------------------------------------------------
+       function minetest.pos_to_string(pos)
+               return "(" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")"
+       end
+end
+
diff --git a/builtin/common/serialize.lua b/builtin/common/serialize.lua
new file mode 100644 (file)
index 0000000..93fffe8
--- /dev/null
@@ -0,0 +1,223 @@
+-- Minetest: builtin/serialize.lua
+
+-- https://github.com/fab13n/metalua/blob/no-dll/src/lib/serialize.lua
+-- Copyright (c) 2006-2997 Fabien Fleutot <metalua@gmail.com>
+-- License: MIT
+--------------------------------------------------------------------------------
+-- Serialize an object into a source code string. This string, when passed as
+-- an argument to deserialize(), returns an object structurally identical
+-- to the original one. The following are currently supported:
+-- * strings, numbers, booleans, nil
+-- * tables thereof. Tables can have shared part, but can't be recursive yet.
+-- Caveat: metatables and environments aren't saved.
+--------------------------------------------------------------------------------
+
+local no_identity = { number=1, boolean=1, string=1, ['nil']=1 }
+
+function minetest.serialize(x)
+
+       local gensym_max   =  0  -- index of the gensym() symbol generator
+       local seen_once    = { } -- element->true set of elements seen exactly once in the table
+       local multiple     = { } -- element->varname set of elements seen more than once
+       local nested       = { } -- transient, set of elements currently being traversed
+       local nest_points  = { }
+       local nest_patches = { }
+       
+       local function gensym()
+               gensym_max = gensym_max + 1 ;  return gensym_max
+       end
+
+       -----------------------------------------------------------------------------
+       -- nest_points are places where a table appears within itself, directly or not.
+       -- for instance, all of these chunks create nest points in table x:
+       -- "x = { }; x[x] = 1", "x = { }; x[1] = x", "x = { }; x[1] = { y = { x } }".
+       -- To handle those, two tables are created by mark_nest_point:
+       -- * nest_points [parent] associates all keys and values in table parent which
+       --   create a nest_point with boolean `true'
+       -- * nest_patches contain a list of { parent, key, value } tuples creating
+       --   a nest point. They're all dumped after all the other table operations
+       --   have been performed.
+       --
+       -- mark_nest_point (p, k, v) fills tables nest_points and nest_patches with
+       -- informations required to remember that key/value (k,v) create a nest point
+       -- in table parent. It also marks `parent' as occuring multiple times, since
+       -- several references to it will be required in order to patch the nest
+       -- points.
+       -----------------------------------------------------------------------------
+       local function mark_nest_point (parent, k, v)
+               local nk, nv = nested[k], nested[v]
+               assert (not nk or seen_once[k] or multiple[k])
+               assert (not nv or seen_once[v] or multiple[v])
+               local mode = (nk and nv and "kv") or (nk and "k") or ("v")
+               local parent_np = nest_points [parent]
+               local pair = { k, v }
+               if not parent_np then parent_np = { }; nest_points [parent] = parent_np end
+               parent_np [k], parent_np [v] = nk, nv
+               table.insert (nest_patches, { parent, k, v })
+               seen_once [parent], multiple [parent]  = nil, true
+       end
+
+       -----------------------------------------------------------------------------
+       -- First pass, list the tables and functions which appear more than once in x
+       -----------------------------------------------------------------------------
+       local function mark_multiple_occurences (x)
+               if no_identity [type(x)] then return end
+               if     seen_once [x]     then seen_once [x], multiple [x] = nil, true
+               elseif multiple  [x]     then -- pass
+               else   seen_once [x] = true end
+               
+               if type (x) == 'table' then
+                       nested [x] = true
+                       for k, v in pairs (x) do
+                               if nested[k] or nested[v] then mark_nest_point (x, k, v) else
+                                       mark_multiple_occurences (k)
+                                       mark_multiple_occurences (v)
+                               end
+                       end
+                       nested [x] = nil
+               end
+       end
+
+       local dumped    = { } -- multiply occuring values already dumped in localdefs
+       local localdefs = { } -- already dumped local definitions as source code lines
+
+       -- mutually recursive functions:
+       local dump_val, dump_or_ref_val
+
+       --------------------------------------------------------------------
+       -- if x occurs multiple times, dump the local var rather than the
+       -- value. If it's the first time it's dumped, also dump the content
+       -- in localdefs.
+       --------------------------------------------------------------------
+       function dump_or_ref_val (x)
+               if nested[x] then return 'false' end -- placeholder for recursive reference
+               if not multiple[x] then return dump_val (x) end
+               local var = dumped [x]
+               if var then return "_[" .. var .. "]" end -- already referenced
+               local val = dump_val(x) -- first occurence, create and register reference
+               var = gensym()
+               table.insert(localdefs, "_["..var.."]="..val)
+               dumped [x] = var
+               return "_[" .. var .. "]"
+       end
+
+       -----------------------------------------------------------------------------
+       -- Second pass, dump the object; subparts occuring multiple times are dumped
+       -- in local variables which can be referenced multiple times;
+       -- care is taken to dump locla vars in asensible order.
+       -----------------------------------------------------------------------------
+       function dump_val(x)
+               local  t = type(x)
+               if     x==nil        then return 'nil'
+               elseif t=="number"   then return tostring(x)
+               elseif t=="string"   then return string.format("%q", x)
+               elseif t=="boolean"  then return x and "true" or "false"
+               elseif t=="function" then
+                       return "loadstring("..string.format("%q", string.dump(x))..")"
+               elseif t=="table" then
+                       local acc        = { }
+                       local idx_dumped = { }
+                       local np         = nest_points [x]
+                       for i, v in ipairs(x) do
+                               if np and np[v] then
+                                       table.insert (acc, 'false') -- placeholder
+                               else
+                                       table.insert (acc, dump_or_ref_val(v))
+                               end
+                               idx_dumped[i] = true
+                       end
+                       for k, v in pairs(x) do
+                               if np and (np[k] or np[v]) then
+                                       --check_multiple(k); check_multiple(v) -- force dumps in localdefs
+                               elseif not idx_dumped[k] then
+                                       table.insert (acc, "[" .. dump_or_ref_val(k) .. "] = " .. dump_or_ref_val(v))
+                               end
+                       end
+                       return "{ "..table.concat(acc,", ").." }"
+               else
+                       error ("Can't serialize data of type "..t)
+               end
+       end
+       
+       local function dump_nest_patches()
+               for _, entry in ipairs(nest_patches) do
+                       local p, k, v = unpack (entry)
+                       assert (multiple[p])
+                       local set = dump_or_ref_val (p) .. "[" .. dump_or_ref_val (k) .. "] = " .. 
+                               dump_or_ref_val (v) .. " -- rec "
+                       table.insert (localdefs, set)
+               end
+       end
+
+       mark_multiple_occurences (x)
+       local toplevel = dump_or_ref_val (x)
+       dump_nest_patches()
+
+       if next (localdefs) then
+               return "local _={ }\n" ..
+                       table.concat (localdefs, "\n") .. 
+                       "\nreturn " .. toplevel
+       else
+               return "return " .. toplevel
+       end
+end
+
+-- Deserialization.
+-- http://stackoverflow.com/questions/5958818/loading-serialized-data-into-a-table
+--
+
+local env = {
+       loadstring = loadstring,
+}
+
+local function noop() end
+
+local safe_env = {
+       loadstring = noop,
+}
+
+local function stringtotable(sdata, safe)
+       if sdata:byte(1) == 27 then return nil, "binary bytecode prohibited" end
+       local f, message = assert(loadstring(sdata))
+       if not f then return nil, message end
+       if safe then
+               setfenv(f, safe_env)
+       else
+               setfenv(f, env)
+       end
+       return f()
+end
+
+function minetest.deserialize(sdata, safe)
+       local table = {}
+       local okay, results = pcall(stringtotable, sdata, safe)
+       if okay then
+               return results
+       end
+       minetest.log('error', 'minetest.deserialize(): '.. results)
+       return nil
+end
+
+-- Run some unit tests
+local function unit_test()
+       function unitTest(name, success)
+               if not success then
+                       error(name .. ': failed')
+               end
+       end
+
+       unittest_input = {cat={sound="nyan", speed=400}, dog={sound="woof"}}
+       unittest_output = minetest.deserialize(minetest.serialize(unittest_input))
+
+       unitTest("test 1a", unittest_input.cat.sound == unittest_output.cat.sound)
+       unitTest("test 1b", unittest_input.cat.speed == unittest_output.cat.speed)
+       unitTest("test 1c", unittest_input.dog.sound == unittest_output.dog.sound)
+
+       unittest_input = {escapechars="\n\r\t\v\\\"\'", noneuropean="θשׁ٩∂"}
+       unittest_output = minetest.deserialize(minetest.serialize(unittest_input))
+       unitTest("test 3a", unittest_input.escapechars == unittest_output.escapechars)
+       unitTest("test 3b", unittest_input.noneuropean == unittest_output.noneuropean)
+end
+unit_test() -- Run it
+unit_test = nil -- Hide it
+
diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua
new file mode 100644 (file)
index 0000000..77944b6
--- /dev/null
@@ -0,0 +1,146 @@
+
+vector = {}
+
+local function assert_vector(v)
+       assert(type(v) == "table" and v.x and v.y and v.z, "Invalid vector")
+end
+
+function vector.new(a, b, c)
+       if type(a) == "table" then
+               assert(a.x and a.y and a.z, "Invalid vector passed to vector.new()")
+               return {x=a.x, y=a.y, z=a.z}
+       elseif a then
+               assert(b and c, "Invalid arguments for vector.new()")
+               return {x=a, y=b, z=c}
+       end
+       return {x=0, y=0, z=0}
+end
+
+function vector.equals(a, b)
+       assert_vector(a)
+       assert_vector(b)
+       return a.x == b.x and
+              a.y == b.y and
+              a.z == b.z
+end
+
+function vector.length(v)
+       assert_vector(v)
+       return math.hypot(v.x, math.hypot(v.y, v.z))
+end
+
+function vector.normalize(v)
+       assert_vector(v)
+       local len = vector.length(v)
+       if len == 0 then
+               return {x=0, y=0, z=0}
+       else
+               return vector.divide(v, len)
+       end
+end
+
+function vector.round(v)
+       assert_vector(v)
+       return {
+               x = math.floor(v.x + 0.5),
+               y = math.floor(v.y + 0.5),
+               z = math.floor(v.z + 0.5)
+       }
+end
+
+function vector.distance(a, b)
+       assert_vector(a)
+       assert_vector(b)
+       local x = a.x - b.x
+       local y = a.y - b.y
+       local z = a.z - b.z
+       return math.hypot(x, math.hypot(y, z))
+end
+
+function vector.direction(pos1, pos2)
+       assert_vector(pos1)
+       assert_vector(pos2)
+       local x_raw = pos2.x - pos1.x
+       local y_raw = pos2.y - pos1.y
+       local z_raw = pos2.z - pos1.z
+       local x_abs = math.abs(x_raw)
+       local y_abs = math.abs(y_raw)
+       local z_abs = math.abs(z_raw)
+       if x_abs >= y_abs and
+          x_abs >= z_abs then
+               y_raw = y_raw * (1 / x_abs)
+               z_raw = z_raw * (1 / x_abs)
+               x_raw = x_raw / x_abs
+       end
+       if y_abs >= x_abs and
+          y_abs >= z_abs then
+               x_raw = x_raw * (1 / y_abs)
+               z_raw = z_raw * (1 / y_abs)
+               y_raw = y_raw / y_abs
+       end
+       if z_abs >= y_abs and
+          z_abs >= x_abs then
+               x_raw = x_raw * (1 / z_abs)
+               y_raw = y_raw * (1 / z_abs)
+               z_raw = z_raw / z_abs
+       end
+       return {x=x_raw, y=y_raw, z=z_raw}
+end
+
+
+function vector.add(a, b)
+       assert_vector(a)
+       if type(b) == "table" then
+           assert_vector(b)
+               return {x = a.x + b.x,
+                       y = a.y + b.y,
+                       z = a.z + b.z}
+       else
+               return {x = a.x + b,
+                       y = a.y + b,
+                       z = a.z + b}
+       end
+end
+
+function vector.subtract(a, b)
+       assert_vector(a)
+       if type(b) == "table" then
+           assert_vector(b)
+               return {x = a.x - b.x,
+                       y = a.y - b.y,
+                       z = a.z - b.z}
+       else
+               return {x = a.x - b,
+                       y = a.y - b,
+                       z = a.z - b}
+       end
+end
+
+function vector.multiply(a, b)
+       assert_vector(a)
+       if type(b) == "table" then
+           assert_vector(b)
+               return {x = a.x * b.x,
+                       y = a.y * b.y,
+                       z = a.z * b.z}
+       else
+               return {x = a.x * b,
+                       y = a.y * b,
+                       z = a.z * b}
+       end
+end
+
+function vector.divide(a, b)
+       assert_vector(a)
+       if type(b) == "table" then
+        assert_vector(b)
+               return {x = a.x / b.x,
+                       y = a.y / b.y,
+                       z = a.z / b.z}
+       else
+               return {x = a.x / b,
+                       y = a.y / b,
+                       z = a.z / b}
+       end
+end
+
diff --git a/builtin/deprecated.lua b/builtin/deprecated.lua
deleted file mode 100644 (file)
index d8b578d..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
--- Minetest: builtin/deprecated.lua
-
---
--- Default material types
---
-function digprop_err()
-       minetest.log("info", debug.traceback())
-       minetest.log("info", "WARNING: The minetest.digprop_* functions are obsolete and need to be replaced by item groups.")
-end
-
-minetest.digprop_constanttime = digprop_err
-minetest.digprop_stonelike = digprop_err
-minetest.digprop_dirtlike = digprop_err
-minetest.digprop_gravellike = digprop_err
-minetest.digprop_woodlike = digprop_err
-minetest.digprop_leaveslike = digprop_err
-minetest.digprop_glasslike = digprop_err
-
-minetest.node_metadata_inventory_move_allow_all = function()
-       minetest.log("info", "WARNING: minetest.node_metadata_inventory_move_allow_all is obsolete and does nothing.")
-end
-
-minetest.add_to_creative_inventory = function(itemstring)
-       minetest.log('info', "WARNING: minetest.add_to_creative_inventory: This function is deprecated and does nothing.")
-end
-
---
--- EnvRef
---
-minetest.env = {}
-local envref_deprecation_message_printed = false
-setmetatable(minetest.env, {
-       __index = function(table, key)
-               if not envref_deprecation_message_printed then
-                       minetest.log("info", "WARNING: minetest.env:[...] is deprecated and should be replaced with minetest.[...]")
-                       envref_deprecation_message_printed = true
-               end
-               local func = minetest[key]
-               if type(func) == "function" then
-                       rawset(table, key, function(self, ...)
-                               return func(...)
-                       end)
-               else
-                       rawset(table, key, nil)
-               end
-               return rawget(table, key)
-       end
-})
-
-function minetest.rollback_get_last_node_actor(pos, range, seconds)
-       return minetest.rollback_get_node_actions(pos, range, seconds, 1)[1]
-end
-
diff --git a/builtin/detached_inventory.lua b/builtin/detached_inventory.lua
deleted file mode 100644 (file)
index 3757f13..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
--- Minetest: builtin/detached_inventory.lua
-
-minetest.detached_inventories = {}
-
-function minetest.create_detached_inventory(name, callbacks)
-       local stuff = {}
-       stuff.name = name
-       if callbacks then
-               stuff.allow_move = callbacks.allow_move
-               stuff.allow_put = callbacks.allow_put
-               stuff.allow_take = callbacks.allow_take
-               stuff.on_move = callbacks.on_move
-               stuff.on_put = callbacks.on_put
-               stuff.on_take = callbacks.on_take
-       end
-       minetest.detached_inventories[name] = stuff
-       return minetest.create_detached_inventory_raw(name)
-end
-
diff --git a/builtin/falling.lua b/builtin/falling.lua
deleted file mode 100644 (file)
index 7ac348b..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
--- Minetest: builtin/item.lua
-
---
--- Falling stuff
---
-
-minetest.register_entity("__builtin:falling_node", {
-       initial_properties = {
-               physical = true,
-               collide_with_objects = false,
-               collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
-               visual = "wielditem",
-               textures = {},
-               visual_size = {x=0.667, y=0.667},
-       },
-
-       node = {},
-
-       set_node = function(self, node)
-               self.node = node
-               local stack = ItemStack(node.name)
-               local itemtable = stack:to_table()
-               local itemname = nil
-               if itemtable then
-                       itemname = stack:to_table().name
-               end
-               local item_texture = nil
-               local item_type = ""
-               if minetest.registered_items[itemname] then
-                       item_texture = minetest.registered_items[itemname].inventory_image
-                       item_type = minetest.registered_items[itemname].type
-               end
-               prop = {
-                       is_visible = true,
-                       textures = {node.name},
-               }
-               self.object:set_properties(prop)
-       end,
-
-       get_staticdata = function(self)
-               return self.node.name
-       end,
-
-       on_activate = function(self, staticdata)
-               self.object:set_armor_groups({immortal=1})
-               --self.object:setacceleration({x=0, y=-10, z=0})
-               self:set_node({name=staticdata})
-       end,
-
-       on_step = function(self, dtime)
-               -- Set gravity
-               self.object:setacceleration({x=0, y=-10, z=0})
-               -- Turn to actual sand when collides to ground or just move
-               local pos = self.object:getpos()
-               local bcp = {x=pos.x, y=pos.y-0.7, z=pos.z} -- Position of bottom center point
-               local bcn = minetest.get_node(bcp)
-               local bcd = minetest.registered_nodes[bcn.name]
-               -- Note: walkable is in the node definition, not in item groups
-               if not bcd or
-                               (bcd.walkable or
-                               (minetest.get_item_group(self.node.name, "float") ~= 0 and
-                               bcd.liquidtype ~= "none")) then
-                       if bcd and bcd.leveled and
-                                       bcn.name == self.node.name then
-                               local addlevel = self.node.level
-                               if addlevel == nil or addlevel <= 0 then
-                                       addlevel = bcd.leveled
-                               end
-                               if minetest.add_node_level(bcp, addlevel) == 0 then
-                                       self.object:remove()
-                                       return
-                               end
-                       elseif bcd and bcd.buildable_to and
-                                       (minetest.get_item_group(self.node.name, "float") == 0 or
-                                       bcd.liquidtype == "none") then
-                               minetest.remove_node(bcp)
-                               return
-                       end
-                       local np = {x=bcp.x, y=bcp.y+1, z=bcp.z}
-                       -- Check what's here
-                       local n2 = minetest.get_node(np)
-                       -- If it's not air or liquid, remove node and replace it with
-                       -- it's drops
-                       if n2.name ~= "air" and (not minetest.registered_nodes[n2.name] or
-                                       minetest.registered_nodes[n2.name].liquidtype == "none") then
-                               local drops = minetest.get_node_drops(n2.name, "")
-                               minetest.remove_node(np)
-                               -- Add dropped items
-                               local _, dropped_item
-                               for _, dropped_item in ipairs(drops) do
-                                       minetest.add_item(np, dropped_item)
-                               end
-                               -- Run script hook
-                               local _, callback
-                               for _, callback in ipairs(minetest.registered_on_dignodes) do
-                                       callback(np, n2, nil)
-                               end
-                       end
-                       -- Create node and remove entity
-                       minetest.add_node(np, self.node)
-                       self.object:remove()
-                       nodeupdate(np)
-               else
-                       -- Do nothing
-               end
-       end
-})
-
-function spawn_falling_node(p, node)
-       obj = minetest.add_entity(p, "__builtin:falling_node")
-       obj:get_luaentity():set_node(node)
-end
-
-function drop_attached_node(p)
-       local nn = minetest.get_node(p).name
-       minetest.remove_node(p)
-       for _,item in ipairs(minetest.get_node_drops(nn, "")) do
-               local pos = {
-                       x = p.x + math.random()/2 - 0.25,
-                       y = p.y + math.random()/2 - 0.25,
-                       z = p.z + math.random()/2 - 0.25,
-               }
-               minetest.add_item(pos, item)
-       end
-end
-
-function check_attached_node(p, n)
-       local def = minetest.registered_nodes[n.name]
-       local d = {x=0, y=0, z=0}
-       if def.paramtype2 == "wallmounted" then
-               if n.param2 == 0 then
-                       d.y = 1
-               elseif n.param2 == 1 then
-                       d.y = -1
-               elseif n.param2 == 2 then
-                       d.x = 1
-               elseif n.param2 == 3 then
-                       d.x = -1
-               elseif n.param2 == 4 then
-                       d.z = 1
-               elseif n.param2 == 5 then
-                       d.z = -1
-               end
-       else
-               d.y = -1
-       end
-       local p2 = {x=p.x+d.x, y=p.y+d.y, z=p.z+d.z}
-       local nn = minetest.get_node(p2).name
-       local def2 = minetest.registered_nodes[nn]
-       if def2 and not def2.walkable then
-               return false
-       end
-       return true
-end
-
---
--- Some common functions
---
-
-function nodeupdate_single(p, delay)
-       n = minetest.get_node(p)
-       if minetest.get_item_group(n.name, "falling_node") ~= 0 then
-               p_bottom = {x=p.x, y=p.y-1, z=p.z}
-               n_bottom = minetest.get_node(p_bottom)
-               -- Note: walkable is in the node definition, not in item groups
-               if minetest.registered_nodes[n_bottom.name] and
-                               (minetest.get_item_group(n.name, "float") == 0 or minetest.registered_nodes[n_bottom.name].liquidtype == "none") and
-                               (n.name ~= n_bottom.name or (minetest.registered_nodes[n_bottom.name].leveled and minetest.env:get_node_level(p_bottom) < minetest.env:get_node_max_level(p_bottom))) and
-                               (not minetest.registered_nodes[n_bottom.name].walkable or
-                                       minetest.registered_nodes[n_bottom.name].buildable_to) then
-                       if delay then
-                               minetest.after(0.1, nodeupdate_single, {x=p.x, y=p.y, z=p.z}, false)
-                       else
-                               n.level = minetest.env:get_node_level(p)
-                               minetest.remove_node(p)
-                               spawn_falling_node(p, n)
-                               nodeupdate(p)
-                       end
-               end
-       end
-       
-       if minetest.get_item_group(n.name, "attached_node") ~= 0 then
-               if not check_attached_node(p, n) then
-                       drop_attached_node(p)
-                       nodeupdate(p)
-               end
-       end
-end
-
-function nodeupdate(p, delay)
-       -- Round p to prevent falling entities to get stuck
-       p.x = math.floor(p.x+0.5)
-       p.y = math.floor(p.y+0.5)
-       p.z = math.floor(p.z+0.5)
-       
-       for x = -1,1 do
-       for y = -1,1 do
-       for z = -1,1 do
-               nodeupdate_single({x=p.x+x, y=p.y+y, z=p.z+z}, delay or not (x==0 and y==0 and z==0))
-       end
-       end
-       end
-end
-
---
--- Global callbacks
---
-
-function on_placenode(p, node)
-       nodeupdate(p)
-end
-minetest.register_on_placenode(on_placenode)
-
-function on_dignode(p, node)
-       nodeupdate(p)
-end
-minetest.register_on_dignode(on_dignode)
diff --git a/builtin/features.lua b/builtin/features.lua
deleted file mode 100644 (file)
index f3de3ba..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
--- Minetest: builtin/features.lua
-
-minetest.features = {
-       glasslike_framed = true,
-       nodebox_as_selectionbox = true,
-       chat_send_player_param3 = true,
-       get_all_craft_recipes_works = true,
-       use_texture_alpha = true,
-       no_legacy_abms = true,
-}
-
-function minetest.has_feature(arg)
-       if type(arg) == "table" then
-               missing_features = {}
-               result = true
-               for ft, _ in pairs(arg) do
-                       if not minetest.features[ftr] then
-                               missing_features[ftr] = true
-                               result = false
-                       end
-               end
-               return result, missing_features
-       elseif type(arg) == "string" then
-               if not minetest.features[arg] then
-                       return false, {[arg]=true}
-               end
-               return true, {}
-       end
-end
diff --git a/builtin/filterlist.lua b/builtin/filterlist.lua
deleted file mode 100644 (file)
index 379a5ce..0000000
+++ /dev/null
@@ -1,301 +0,0 @@
---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.
-
---------------------------------------------------------------------------------
--- Generic implementation of a filter/sortable list                           --
--- Usage:                                                                     --
--- Filterlist needs to be initialized on creation. To achieve this you need to --
--- pass following functions:                                                  --
--- raw_fct() (mandatory):                                                     --
---     function returning a table containing the elements to be filtered      --
--- compare_fct(element1,element2) (mandatory):                                --
---     function returning true/false if element1 is same element as element2  --
--- uid_match_fct(element1,uid) (optional)                                     --
---     function telling if uid is attached to element1                        --
--- filter_fct(element,filtercriteria) (optional)                              --
---     function returning true/false if filtercriteria met to element         --
--- fetch_param (optional)                                                     --
---     parameter passed to raw_fct to aquire correct raw data                 --
---                                                                            --
---------------------------------------------------------------------------------
-filterlist = {}
-
---------------------------------------------------------------------------------
-function filterlist.refresh(this)
-       this.m_raw_list = this.m_raw_list_fct(this.m_fetch_param)
-       filterlist.process(this)
-end
-
---------------------------------------------------------------------------------
-function filterlist.create(raw_fct,compare_fct,uid_match_fct,filter_fct,fetch_param)
-
-       assert((raw_fct ~= nil) and (type(raw_fct) == "function"))
-       assert((compare_fct ~= nil) and (type(compare_fct) == "function"))
-       
-       local this = {}
-       
-       this.m_raw_list_fct  = raw_fct
-       this.m_compare_fct   = compare_fct
-       this.m_filter_fct    = filter_fct
-       this.m_uid_match_fct = uid_match_fct
-       
-       this.m_filtercriteria = nil
-       this.m_fetch_param = fetch_param
-       
-       this.m_sortmode = "none"
-       this.m_sort_list = {}
-
-       this.m_processed_list = nil
-       this.m_raw_list = this.m_raw_list_fct(this.m_fetch_param)
-
-       filterlist.process(this)
-       
-       return this
-end
-
---------------------------------------------------------------------------------
-function filterlist.add_sort_mechanism(this,name,fct)
-       this.m_sort_list[name] = fct
-end
-
---------------------------------------------------------------------------------
-function filterlist.set_filtercriteria(this,criteria)
-       if criteria == this.m_filtercriteria and
-               type(criteria) ~= "table" then
-               return
-       end
-       this.m_filtercriteria = criteria
-       filterlist.process(this)
-end
-
---------------------------------------------------------------------------------
-function filterlist.get_filtercriteria(this)
-       return this.m_filtercriteria
-end
-
---------------------------------------------------------------------------------
---supported sort mode "alphabetic|none"
-function filterlist.set_sortmode(this,mode)
-       if (mode == this.m_sortmode) then
-               return
-       end
-       this.m_sortmode = mode
-       filterlist.process(this)
-end
-
---------------------------------------------------------------------------------
-function filterlist.get_list(this)
-       return this.m_processed_list
-end
-
---------------------------------------------------------------------------------
-function filterlist.get_raw_list(this)
-       return this.m_raw_list
-end
-
---------------------------------------------------------------------------------
-function filterlist.get_raw_element(this,idx)
-       if type(idx) ~= "number" then
-               idx = tonumber(idx)
-       end
-       
-       if idx ~= nil and idx > 0 and idx < #this.m_raw_list then
-               return this.m_raw_list[idx]
-       end
-       
-       return nil
-end
-
---------------------------------------------------------------------------------
-function filterlist.get_raw_index(this,listindex)
-       assert(this.m_processed_list ~= nil)
-       
-       if listindex ~= nil and listindex > 0 and
-               listindex <= #this.m_processed_list then
-               local entry = this.m_processed_list[listindex]
-               
-               for i,v in ipairs(this.m_raw_list) do
-               
-                       if this.m_compare_fct(v,entry) then
-                               return i
-                       end
-               end
-       end
-       
-       return 0
-end
-
---------------------------------------------------------------------------------
-function filterlist.get_current_index(this,listindex)
-       assert(this.m_processed_list ~= nil)
-       
-       if listindex ~= nil and listindex > 0 and
-               listindex <= #this.m_raw_list then
-               local entry = this.m_raw_list[listindex]
-               
-               for i,v in ipairs(this.m_processed_list) do
-               
-                       if this.m_compare_fct(v,entry) then
-                               return i
-                       end
-               end
-       end
-       
-       return 0
-end
-
---------------------------------------------------------------------------------
-function filterlist.process(this)
-       assert(this.m_raw_list ~= nil)
-
-       if this.m_sortmode == "none" and
-               this.m_filtercriteria == nil then
-               this.m_processed_list = this.m_raw_list
-               return
-       end
-       
-       this.m_processed_list = {}
-       
-       for k,v in pairs(this.m_raw_list) do
-               if this.m_filtercriteria == nil or
-                       this.m_filter_fct(v,this.m_filtercriteria) then
-                       table.insert(this.m_processed_list,v)
-               end
-       end
-       
-       if this.m_sortmode == "none" then
-               return
-       end
-       
-       if this.m_sort_list[this.m_sortmode] ~= nil and
-               type(this.m_sort_list[this.m_sortmode]) == "function" then
-               
-               this.m_sort_list[this.m_sortmode](this)
-       end
-end
-
---------------------------------------------------------------------------------
-function filterlist.size(this)
-       if this.m_processed_list == nil then
-               return 0
-       end
-       
-       return #this.m_processed_list
-end
-
---------------------------------------------------------------------------------
-function filterlist.uid_exists_raw(this,uid)
-       for i,v in ipairs(this.m_raw_list) do
-               if this.m_uid_match_fct(v,uid) then
-                       return true
-               end
-       end
-       return false
-end
-
---------------------------------------------------------------------------------
-function filterlist.raw_index_by_uid(this, uid)
-       local elementcount = 0
-       local elementidx = 0
-       for i,v in ipairs(this.m_raw_list) do
-               if this.m_uid_match_fct(v,uid) then
-                       elementcount = elementcount +1
-                       elementidx = i
-               end
-       end
-       
-       
-       -- If there are more elements than one with same name uid can't decide which
-       -- one is meant. This shouldn't be possible but just for sure.
-       if elementcount > 1 then
-               elementidx=0
-       end
-
-       return elementidx
-end
-
---------------------------------------------------------------------------------
--- COMMON helper functions                                                    --
---------------------------------------------------------------------------------
-
---------------------------------------------------------------------------------
-function compare_worlds(world1,world2)
-
-       if world1.path ~= world2.path then
-               return false
-       end
-       
-       if world1.name ~= world2.name then
-               return false
-       end
-       
-       if world1.gameid ~= world2.gameid then
-               return false
-       end
-
-       return true
-end
-
---------------------------------------------------------------------------------
-function sort_worlds_alphabetic(this)
-
-       table.sort(this.m_processed_list, function(a, b)
-               --fixes issue #857 (crash due to sorting nil in worldlist)
-               if a == nil or b == nil then
-                       if a == nil and b ~= nil then return false end
-                       if b == nil and a ~= nil then return true end
-                       return false
-               end
-               if a.name:lower() == b.name:lower() then
-                       return a.name < b.name
-               end
-               return a.name:lower() < b.name:lower()
-       end)
-end
-
---------------------------------------------------------------------------------
-function sort_mod_list(this)
-
-       table.sort(this.m_processed_list, function(a, b)
-               -- Show game mods at bottom
-               if a.typ ~= b.typ then
-                       return b.typ == "game_mod"
-               end
-               -- If in same or no modpack, sort by name
-               if a.modpack == b.modpack then
-                       if a.name:lower() == b.name:lower() then
-                               return a.name < b.name
-                       end
-                       return a.name:lower() < b.name:lower()
-               -- Else compare name to modpack name
-               else
-                       -- Always show modpack pseudo-mod on top of modpack mod list
-                       if a.name == b.modpack then
-                               return true
-                       elseif b.name == a.modpack then
-                               return false
-                       end
-                       
-                       local name_a = a.modpack or a.name
-                       local name_b = b.modpack or b.name
-                       if name_a:lower() == name_b:lower() then
-                               return  name_a < name_b
-                       end
-                       return name_a:lower() < name_b:lower()
-               end
-       end)
-end
diff --git a/builtin/forceloading.lua b/builtin/forceloading.lua
deleted file mode 100644 (file)
index 8489579..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
--- Prevent anyone else accessing those functions
-local forceload_block = minetest.forceload_block
-local forceload_free_block = minetest.forceload_free_block
-minetest.forceload_block = nil
-minetest.forceload_free_block = nil
-
-local blocks_forceloaded
-local total_forceloaded = 0
-
-local BLOCKSIZE = 16
-local function get_blockpos(pos)
-       return {
-               x = math.floor(pos.x/BLOCKSIZE),
-               y = math.floor(pos.y/BLOCKSIZE),
-               z = math.floor(pos.z/BLOCKSIZE)}
-end
-
-function minetest.forceload_block(pos)
-       local blockpos = get_blockpos(pos)
-       local hash = minetest.hash_node_position(blockpos)
-       if blocks_forceloaded[hash] ~= nil then
-               blocks_forceloaded[hash] = blocks_forceloaded[hash] + 1
-               return true
-       else
-               if total_forceloaded >= (tonumber(minetest.setting_get("max_forceloaded_blocks")) or 16) then
-                       return false
-               end
-               total_forceloaded = total_forceloaded+1
-               blocks_forceloaded[hash] = 1
-               forceload_block(blockpos)
-               return true
-       end
-end
-
-function minetest.forceload_free_block(pos)
-       local blockpos = get_blockpos(pos)
-       local hash = minetest.hash_node_position(blockpos)
-       if blocks_forceloaded[hash] == nil then return end
-       if blocks_forceloaded[hash] > 1 then
-               blocks_forceloaded[hash] = blocks_forceloaded[hash] - 1
-       else
-               total_forceloaded = total_forceloaded-1
-               blocks_forceloaded[hash] = nil
-               forceload_free_block(blockpos)
-       end
-end
-
--- Keep the forceloaded areas after restart
-local wpath = minetest.get_worldpath()
-local function read_file(filename)
-       local f = io.open(filename, "r")
-       if f==nil then return {} end
-       local t = f:read("*all")
-       f:close()
-       if t=="" or t==nil then return {} end
-       return minetest.deserialize(t)
-end
-
-local function write_file(filename, table)
-       local f = io.open(filename, "w")
-       f:write(minetest.serialize(table))
-       f:close()
-end
-
-blocks_forceloaded = read_file(wpath.."/force_loaded.txt")
-for _, __ in pairs(blocks_forceloaded) do
-       total_forceloaded = total_forceloaded + 1
-end
-
-minetest.after(5, function()
-       for hash, _ in pairs(blocks_forceloaded) do
-               local blockpos = minetest.get_position_from_hash(hash)
-               forceload_block(blockpos)
-       end
-end)
-
-minetest.register_on_shutdown(function()
-       write_file(wpath.."/force_loaded.txt", blocks_forceloaded)
-end)
diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua
new file mode 100644 (file)
index 0000000..b6cca60
--- /dev/null
@@ -0,0 +1,188 @@
+-- Minetest: builtin/auth.lua
+
+--
+-- Authentication handler
+--
+
+function minetest.string_to_privs(str, delim)
+       assert(type(str) == "string")
+       delim = delim or ','
+       privs = {}
+       for _, priv in pairs(string.split(str, delim)) do
+               privs[priv:trim()] = true
+       end
+       return privs
+end
+
+function minetest.privs_to_string(privs, delim)
+       assert(type(privs) == "table")
+       delim = delim or ','
+       list = {}
+       for priv, bool in pairs(privs) do
+               if bool then
+                       table.insert(list, priv)
+               end
+       end
+       return table.concat(list, delim)
+end
+
+assert(minetest.string_to_privs("a,b").b == true)
+assert(minetest.privs_to_string({a=true,b=true}) == "a,b")
+
+minetest.auth_file_path = minetest.get_worldpath().."/auth.txt"
+minetest.auth_table = {}
+
+local function read_auth_file()
+       local newtable = {}
+       local file, errmsg = io.open(minetest.auth_file_path, 'rb')
+       if not file then
+               minetest.log("info", minetest.auth_file_path.." could not be opened for reading ("..errmsg.."); assuming new world")
+               return
+       end
+       for line in file:lines() do
+               if line ~= "" then
+                       local name, password, privilegestring = string.match(line, "([^:]*):([^:]*):([^:]*)")
+                       if not name or not password or not privilegestring then
+                               error("Invalid line in auth.txt: "..dump(line))
+                       end
+                       local privileges = minetest.string_to_privs(privilegestring)
+                       newtable[name] = {password=password, privileges=privileges}
+               end
+       end
+       io.close(file)
+       minetest.auth_table = newtable
+       minetest.notify_authentication_modified()
+end
+
+local function save_auth_file()
+       local newtable = {}
+       -- Check table for validness before attempting to save
+       for name, stuff in pairs(minetest.auth_table) do
+               assert(type(name) == "string")
+               assert(name ~= "")
+               assert(type(stuff) == "table")
+               assert(type(stuff.password) == "string")
+               assert(type(stuff.privileges) == "table")
+       end
+       local file, errmsg = io.open(minetest.auth_file_path, 'w+b')
+       if not file then
+               error(minetest.auth_file_path.." could not be opened for writing: "..errmsg)
+       end
+       for name, stuff in pairs(minetest.auth_table) do
+               local privstring = minetest.privs_to_string(stuff.privileges)
+               file:write(name..":"..stuff.password..":"..privstring..'\n')
+       end
+       io.close(file)
+end
+
+read_auth_file()
+
+minetest.builtin_auth_handler = {
+       get_auth = function(name)
+               assert(type(name) == "string")
+               -- Figure out what password to use for a new player (singleplayer
+               -- always has an empty password, otherwise use default, which is
+               -- usually empty too)
+               local new_password_hash = ""
+               -- If not in authentication table, return nil
+               if not minetest.auth_table[name] then
+                       return nil
+               end
+               -- Figure out what privileges the player should have.
+               -- Take a copy of the privilege table
+               local privileges = {}
+               for priv, _ in pairs(minetest.auth_table[name].privileges) do
+                       privileges[priv] = true
+               end
+               -- If singleplayer, give all privileges except those marked as give_to_singleplayer = false
+               if minetest.is_singleplayer() then
+                       for priv, def in pairs(minetest.registered_privileges) do
+                               if def.give_to_singleplayer then
+                                       privileges[priv] = true
+                               end
+                       end
+               -- For the admin, give everything
+               elseif name == minetest.setting_get("name") then
+                       for priv, def in pairs(minetest.registered_privileges) do
+                               privileges[priv] = true
+                       end
+               end
+               -- All done
+               return {
+                       password = minetest.auth_table[name].password,
+                       privileges = privileges,
+               }
+       end,
+       create_auth = function(name, password)
+               assert(type(name) == "string")
+               assert(type(password) == "string")
+               minetest.log('info', "Built-in authentication handler adding player '"..name.."'")
+               minetest.auth_table[name] = {
+                       password = password,
+                       privileges = minetest.string_to_privs(minetest.setting_get("default_privs")),
+               }
+               save_auth_file()
+       end,
+       set_password = function(name, password)
+               assert(type(name) == "string")
+               assert(type(password) == "string")
+               if not minetest.auth_table[name] then
+                       minetest.builtin_auth_handler.create_auth(name, password)
+               else
+                       minetest.log('info', "Built-in authentication handler setting password of player '"..name.."'")
+                       minetest.auth_table[name].password = password
+                       save_auth_file()
+               end
+               return true
+       end,
+       set_privileges = function(name, privileges)
+               assert(type(name) == "string")
+               assert(type(privileges) == "table")
+               if not minetest.auth_table[name] then
+                       minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
+               end
+               minetest.auth_table[name].privileges = privileges
+               minetest.notify_authentication_modified(name)
+               save_auth_file()
+       end,
+       reload = function()
+               read_auth_file()
+               return true
+       end,
+}
+
+function minetest.register_authentication_handler(handler)
+       if minetest.registered_auth_handler then
+               error("Add-on authentication handler already registered by "..minetest.registered_auth_handler_modname)
+       end
+       minetest.registered_auth_handler = handler
+       minetest.registered_auth_handler_modname = minetest.get_current_modname()
+end
+
+function minetest.get_auth_handler()
+       if minetest.registered_auth_handler then
+               return minetest.registered_auth_handler
+       end
+       return minetest.builtin_auth_handler
+end
+
+function minetest.set_player_password(name, password)
+       if minetest.get_auth_handler().set_password then
+               minetest.get_auth_handler().set_password(name, password)
+       end
+end
+
+function minetest.set_player_privs(name, privs)
+       if minetest.get_auth_handler().set_privileges then
+               minetest.get_auth_handler().set_privileges(name, privs)
+       end
+end
+
+function minetest.auth_reload()
+       if minetest.get_auth_handler().reload then
+               return minetest.get_auth_handler().reload()
+       end
+       return false
+end
+
+
diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua
new file mode 100644 (file)
index 0000000..f8df83d
--- /dev/null
@@ -0,0 +1,725 @@
+-- Minetest: builtin/chatcommands.lua
+
+--
+-- Chat command handler
+--
+
+minetest.chatcommands = {}
+function minetest.register_chatcommand(cmd, def)
+       def = def or {}
+       def.params = def.params or ""
+       def.description = def.description or ""
+       def.privs = def.privs or {}
+       minetest.chatcommands[cmd] = def
+end
+
+minetest.register_on_chat_message(function(name, message)
+       local cmd, param = string.match(message, "^/([^ ]+) *(.*)")
+       if not param then
+               param = ""
+       end
+       local cmd_def = minetest.chatcommands[cmd]
+       if cmd_def then
+               local has_privs, missing_privs = minetest.check_player_privs(name, cmd_def.privs)
+               if has_privs then
+                       cmd_def.func(name, param)
+               else
+                       minetest.chat_send_player(name, "You don't have permission to run this command (missing privileges: "..table.concat(missing_privs, ", ")..")")
+               end
+               return true -- handled chat message
+       end
+       return false
+end)
+
+--
+-- Chat commands
+--
+minetest.register_chatcommand("me", {
+       params = "<action>",
+       description = "chat action (eg. /me orders a pizza)",
+       privs = {shout=true},
+       func = function(name, param)
+               minetest.chat_send_all("* " .. name .. " " .. param)
+       end,
+})
+
+minetest.register_chatcommand("help", {
+       privs = {},
+       params = "(nothing)/all/privs/<cmd>",
+       description = "Get help for commands or list privileges",
+       func = function(name, param)
+               local format_help_line = function(cmd, def)
+                       local msg = "/"..cmd
+                       if def.params and def.params ~= "" then msg = msg .. " " .. def.params end
+                       if def.description and def.description ~= "" then msg = msg .. ": " .. def.description end
+                       return msg
+               end
+               if param == "" then
+                       local msg = ""
+                       cmds = {}
+                       for cmd, def in pairs(minetest.chatcommands) do
+                               if minetest.check_player_privs(name, def.privs) then
+                                       table.insert(cmds, cmd)
+                               end
+                       end
+                       minetest.chat_send_player(name, "Available commands: "..table.concat(cmds, " "))
+                       minetest.chat_send_player(name, "Use '/help <cmd>' to get more information, or '/help all' to list everything.")
+               elseif param == "all" then
+                       minetest.chat_send_player(name, "Available commands:")
+                       for cmd, def in pairs(minetest.chatcommands) do
+                               if minetest.check_player_privs(name, def.privs) then
+                                       minetest.chat_send_player(name, format_help_line(cmd, def))
+                               end
+                       end
+               elseif param == "privs" then
+                       minetest.chat_send_player(name, "Available privileges:")
+                       for priv, def in pairs(minetest.registered_privileges) do
+                               minetest.chat_send_player(name, priv..": "..def.description)
+                       end
+               else
+                       local cmd = param
+                       def = minetest.chatcommands[cmd]
+                       if not def then
+                               minetest.chat_send_player(name, "Command not available: "..cmd)
+                       else
+                               minetest.chat_send_player(name, format_help_line(cmd, def))
+                       end
+               end
+       end,
+})
+minetest.register_chatcommand("privs", {
+       params = "<name>",
+       description = "print out privileges of player",
+       func = function(name, param)
+               if param == "" then
+                       param = name
+               else
+                       --[[if not minetest.check_player_privs(name, {privs=true}) then
+                               minetest.chat_send_player(name, "Privileges of "..param.." are hidden from you.")
+                               return
+                       end]]
+               end
+               minetest.chat_send_player(name, "Privileges of "..param..": "..minetest.privs_to_string(minetest.get_player_privs(param), ' '))
+       end,
+})
+minetest.register_chatcommand("grant", {
+       params = "<name> <privilege>|all",
+       description = "Give privilege to player",
+       privs = {},
+       func = function(name, param)
+               if not minetest.check_player_privs(name, {privs=true}) and
+                               not minetest.check_player_privs(name, {basic_privs=true}) then
+                       minetest.chat_send_player(name, "Your privileges are insufficient.")
+                       return
+               end
+               local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
+               if not grantname or not grantprivstr then
+                       minetest.chat_send_player(name, "Invalid parameters (see /help grant)")
+                       return
+               elseif not minetest.auth_table[grantname] then
+                       minetest.chat_send_player(name, "Player "..grantname.." does not exist.")
+                       return
+               end
+               local grantprivs = minetest.string_to_privs(grantprivstr)
+               if grantprivstr == "all" then
+                       grantprivs = minetest.registered_privileges
+               end
+               local privs = minetest.get_player_privs(grantname)
+               local privs_known = true
+               for priv, _ in pairs(grantprivs) do
+                       if priv ~= "interact" and priv ~= "shout" and priv ~= "interact_extra" and not minetest.check_player_privs(name, {privs=true}) then
+                               minetest.chat_send_player(name, "Your privileges are insufficient.")
+                               return
+                       end
+                       if not minetest.registered_privileges[priv] then
+                               minetest.chat_send_player(name, "Unknown privilege: "..priv)
+                               privs_known = false
+                       end
+                       privs[priv] = true
+               end
+               if not privs_known then
+                       return
+               end
+               minetest.set_player_privs(grantname, privs)
+               minetest.log(name..' granted ('..minetest.privs_to_string(grantprivs, ', ')..') privileges to '..grantname)
+               minetest.chat_send_player(name, "Privileges of "..grantname..": "..minetest.privs_to_string(minetest.get_player_privs(grantname), ' '))
+               if grantname ~= name then
+                       minetest.chat_send_player(grantname, name.." granted you privileges: "..minetest.privs_to_string(grantprivs, ' '))
+               end
+       end,
+})
+minetest.register_chatcommand("revoke", {
+       params = "<name> <privilege>|all",
+       description = "Remove privilege from player",
+       privs = {},
+       func = function(name, param)
+               if not minetest.check_player_privs(name, {privs=true}) and
+                               not minetest.check_player_privs(name, {basic_privs=true}) then
+                       minetest.chat_send_player(name, "Your privileges are insufficient.")
+                       return
+               end
+               local revokename, revokeprivstr = string.match(param, "([^ ]+) (.+)")
+               if not revokename or not revokeprivstr then
+                       minetest.chat_send_player(name, "Invalid parameters (see /help revoke)")
+                       return
+               elseif not minetest.auth_table[revokename] then
+                       minetest.chat_send_player(name, "Player "..revokename.." does not exist.")
+                       return
+               end
+               local revokeprivs = minetest.string_to_privs(revokeprivstr)
+               local privs = minetest.get_player_privs(revokename)
+               for priv, _ in pairs(revokeprivs) do
+                       if priv ~= "interact" and priv ~= "shout" and priv ~= "interact_extra" and not minetest.check_player_privs(name, {privs=true}) then
+                               minetest.chat_send_player(name, "Your privileges are insufficient.")
+                               return
+                       end
+               end
+               if revokeprivstr == "all" then
+                       privs = {}
+               else
+                       for priv, _ in pairs(revokeprivs) do
+                               privs[priv] = nil
+                       end
+               end
+               minetest.set_player_privs(revokename, privs)
+               minetest.log(name..' revoked ('..minetest.privs_to_string(revokeprivs, ', ')..') privileges from '..revokename)
+               minetest.chat_send_player(name, "Privileges of "..revokename..": "..minetest.privs_to_string(minetest.get_player_privs(revokename), ' '))
+               if revokename ~= name then
+                       minetest.chat_send_player(revokename, name.." revoked privileges from you: "..minetest.privs_to_string(revokeprivs, ' '))
+               end
+       end,
+})
+minetest.register_chatcommand("setpassword", {
+       params = "<name> <password>",
+       description = "set given password",
+       privs = {password=true},
+       func = function(name, param)
+               local toname, raw_password = string.match(param, "^([^ ]+) +(.+)$")
+               if not toname then
+                       toname = string.match(param, "^([^ ]+) *$")
+                       raw_password = nil
+               end
+               if not toname then
+                       minetest.chat_send_player(name, "Name field required")
+                       return
+               end
+               local actstr = "?"
+               if not raw_password then
+                       minetest.set_player_password(toname, "")
+                       actstr = "cleared"
+               else
+                       minetest.set_player_password(toname, minetest.get_password_hash(toname, raw_password))
+                       actstr = "set"
+               end
+               minetest.chat_send_player(name, "Password of player \""..toname.."\" "..actstr)
+               if toname ~= name then
+                       minetest.chat_send_player(toname, "Your password was "..actstr.." by "..name)
+               end
+       end,
+})
+minetest.register_chatcommand("clearpassword", {
+       params = "<name>",
+       description = "set empty password",
+       privs = {password=true},
+       func = function(name, param)
+               toname = param
+               if toname == "" then
+                       minetest.chat_send_player(name, "Name field required")
+                       return
+               end
+               minetest.set_player_password(toname, '')
+               minetest.chat_send_player(name, "Password of player \""..toname.."\" cleared")
+       end,
+})
+
+minetest.register_chatcommand("auth_reload", {
+       params = "",
+       description = "reload authentication data",
+       privs = {server=true},
+       func = function(name, param)
+               local done = minetest.auth_reload()
+               if done then
+                       minetest.chat_send_player(name, "Done.")
+               else
+                       minetest.chat_send_player(name, "Failed.")
+               end
+       end,
+})
+
+minetest.register_chatcommand("teleport", {
+       params = "<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>",
+       description = "teleport to given position",
+       privs = {teleport=true},
+       func = function(name, param)
+               -- Returns (pos, true) if found, otherwise (pos, false)
+               local function find_free_position_near(pos)
+                       local tries = {
+                               {x=1,y=0,z=0},
+                               {x=-1,y=0,z=0},
+                               {x=0,y=0,z=1},
+                               {x=0,y=0,z=-1},
+                       }
+                       for _, d in ipairs(tries) do
+                               local p = {x = pos.x+d.x, y = pos.y+d.y, z = pos.z+d.z}
+                               local n = minetest.get_node_or_nil(p)
+                               if n and n.name then
+                                       local def = minetest.registered_nodes[n.name]
+                                       if def and not def.walkable then
+                                               return p, true
+                                       end
+                               end
+                       end
+                       return pos, false
+               end
+
+               local teleportee = nil
+               local p = {}
+               p.x, p.y, p.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
+               p.x = tonumber(p.x)
+               p.y = tonumber(p.y)
+               p.z = tonumber(p.z)
+               teleportee = minetest.get_player_by_name(name)
+               if teleportee and p.x and p.y and p.z then
+                       minetest.chat_send_player(name, "Teleporting to ("..p.x..", "..p.y..", "..p.z..")")
+                       teleportee:setpos(p)
+                       return
+               end
+               
+               local teleportee = nil
+               local p = nil
+               local target_name = nil
+               target_name = string.match(param, "^([^ ]+)$")
+               teleportee = minetest.get_player_by_name(name)
+               if target_name then
+                       local target = minetest.get_player_by_name(target_name)
+                       if target then
+                               p = target:getpos()
+                       end
+               end
+               if teleportee and p then
+                       p = find_free_position_near(p)
+                       minetest.chat_send_player(name, "Teleporting to "..target_name.." at ("..p.x..", "..p.y..", "..p.z..")")
+                       teleportee:setpos(p)
+                       return
+               end
+               
+               if minetest.check_player_privs(name, {bring=true}) then
+                       local teleportee = nil
+                       local p = {}
+                       local teleportee_name = nil
+                       teleportee_name, p.x, p.y, p.z = string.match(param, "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
+                       p.x = tonumber(p.x)
+                       p.y = tonumber(p.y)
+                       p.z = tonumber(p.z)
+                       if teleportee_name then
+                               teleportee = minetest.get_player_by_name(teleportee_name)
+                       end
+                       if teleportee and p.x and p.y and p.z then
+                               minetest.chat_send_player(name, "Teleporting "..teleportee_name.." to ("..p.x..", "..p.y..", "..p.z..")")
+                               teleportee:setpos(p)
+                               return
+                       end
+                       
+                       local teleportee = nil
+                       local p = nil
+                       local teleportee_name = nil
+                       local target_name = nil
+                       teleportee_name, target_name = string.match(param, "^([^ ]+) +([^ ]+)$")
+                       if teleportee_name then
+                               teleportee = minetest.get_player_by_name(teleportee_name)
+                       end
+                       if target_name then
+                               local target = minetest.get_player_by_name(target_name)
+                               if target then
+                                       p = target:getpos()
+                               end
+                       end
+                       if teleportee and p then
+                               p = find_free_position_near(p)
+                               minetest.chat_send_player(name, "Teleporting "..teleportee_name.." to "..target_name.." at ("..p.x..", "..p.y..", "..p.z..")")
+                               teleportee:setpos(p)
+                               return
+                       end
+               end
+
+               minetest.chat_send_player(name, "Invalid parameters (\""..param.."\") or player not found (see /help teleport)")
+               return
+       end,
+})
+
+minetest.register_chatcommand("set", {
+       params = "[-n] <name> <value> | <name>",
+       description = "set or read server configuration setting",
+       privs = {server=true},
+       func = function(name, param)
+               local arg, setname, setvalue = string.match(param, "(-[n]) ([^ ]+) (.+)")
+               if arg and arg == "-n" and setname and setvalue then
+                       minetest.setting_set(setname, setvalue)
+                       minetest.chat_send_player(name, setname.." = "..setvalue)
+                       return
+               end
+               local setname, setvalue = string.match(param, "([^ ]+) (.+)")
+               if setname and setvalue then
+                       if not minetest.setting_get(setname) then
+                               minetest.chat_send_player(name, "Failed. Use '/set -n <name> <value>' to create a new setting.")
+                               return
+                       end
+                       minetest.setting_set(setname, setvalue)
+                       minetest.chat_send_player(name, setname.." = "..setvalue)
+                       return
+               end
+               local setname = string.match(param, "([^ ]+)")
+               if setname then
+                       local setvalue = minetest.setting_get(setname)
+                       if not setvalue then
+                               setvalue = "<not set>"
+                       end
+                       minetest.chat_send_player(name, setname.." = "..setvalue)
+                       return
+               end
+               minetest.chat_send_player(name, "Invalid parameters (see /help set)")
+       end,
+})
+
+minetest.register_chatcommand("mods", {
+       params = "",
+       description = "lists mods installed on the server",
+       privs = {},
+       func = function(name, param)
+               local response = ""
+               local modnames = minetest.get_modnames()
+               for i, mod in ipairs(modnames) do
+                       response = response .. mod
+                       -- Add space if not at the end
+                       if i ~= #modnames then
+                               response = response .. " "
+                       end
+               end
+               minetest.chat_send_player(name, response)
+       end,
+})
+
+local function handle_give_command(cmd, giver, receiver, stackstring)
+       minetest.log("action", giver.." invoked "..cmd..', stackstring="'
+                       ..stackstring..'"')
+       minetest.log(cmd..' invoked, stackstring="'..stackstring..'"')
+       local itemstack = ItemStack(stackstring)
+       if itemstack:is_empty() then
+               minetest.chat_send_player(giver, 'error: cannot give an empty item')
+               return
+       elseif not itemstack:is_known() then
+               minetest.chat_send_player(giver, 'error: cannot give an unknown item')
+               return
+       end
+       local receiverref = minetest.get_player_by_name(receiver)
+       if receiverref == nil then
+               minetest.chat_send_player(giver, receiver..' is not a known player')
+               return
+       end
+       local leftover = receiverref:get_inventory():add_item("main", itemstack)
+       if leftover:is_empty() then
+               partiality = ""
+       elseif leftover:get_count() == itemstack:get_count() then
+               partiality = "could not be "
+       else
+               partiality = "partially "
+       end
+       -- The actual item stack string may be different from what the "giver"
+       -- entered (e.g. big numbers are always interpreted as 2^16-1).
+       stackstring = itemstack:to_string()
+       if giver == receiver then
+               minetest.chat_send_player(giver, '"'..stackstring
+                       ..'" '..partiality..'added to inventory.');
+       else
+               minetest.chat_send_player(giver, '"'..stackstring
+                       ..'" '..partiality..'added to '..receiver..'\'s inventory.');
+               minetest.chat_send_player(receiver, '"'..stackstring
+                       ..'" '..partiality..'added to inventory.');
+       end
+end
+
+minetest.register_chatcommand("give", {
+       params = "<name> <itemstring>",
+       description = "give item to player",
+       privs = {give=true},
+       func = function(name, param)
+               local toname, itemstring = string.match(param, "^([^ ]+) +(.+)$")
+               if not toname or not itemstring then
+                       minetest.chat_send_player(name, "name and itemstring required")
+                       return
+               end
+               handle_give_command("/give", name, toname, itemstring)
+       end,
+})
+minetest.register_chatcommand("giveme", {
+       params = "<itemstring>",
+       description = "give item to yourself",
+       privs = {give=true},
+       func = function(name, param)
+               local itemstring = string.match(param, "(.+)$")
+               if not itemstring then
+                       minetest.chat_send_player(name, "itemstring required")
+                       return
+               end
+               handle_give_command("/giveme", name, name, itemstring)
+       end,
+})
+minetest.register_chatcommand("spawnentity", {
+       params = "<entityname>",
+       description = "spawn entity at your position",
+       privs = {give=true, interact=true},
+       func = function(name, param)
+               local entityname = string.match(param, "(.+)$")
+               if not entityname then
+                       minetest.chat_send_player(name, "entityname required")
+                       return
+               end
+               minetest.log("action", '/spawnentity invoked, entityname="'..entityname..'"')
+               local player = minetest.get_player_by_name(name)
+               if player == nil then
+                       minetest.log("error", "Unable to spawn entity, player is nil")
+                       return true -- Handled chat message
+               end
+               local p = player:getpos()
+               p.y = p.y + 1
+               minetest.add_entity(p, entityname)
+               minetest.chat_send_player(name, '"'..entityname
+                               ..'" spawned.');
+       end,
+})
+minetest.register_chatcommand("pulverize", {
+       params = "",
+       description = "delete item in hand",
+       privs = {},
+       func = function(name, param)
+               local player = minetest.get_player_by_name(name)
+               if player == nil then
+                       minetest.log("error", "Unable to pulverize, player is nil")
+                       return true -- Handled chat message
+               end
+               if player:get_wielded_item():is_empty() then
+                       minetest.chat_send_player(name, 'Unable to pulverize, no item in hand.')
+               else
+                       player:set_wielded_item(nil)
+                       minetest.chat_send_player(name, 'An item was pulverized.')
+               end
+       end,
+})
+
+-- Key = player name
+minetest.rollback_punch_callbacks = {}
+
+minetest.register_on_punchnode(function(pos, node, puncher)
+       local name = puncher:get_player_name()
+       if minetest.rollback_punch_callbacks[name] then
+               minetest.rollback_punch_callbacks[name](pos, node, puncher)
+               minetest.rollback_punch_callbacks[name] = nil
+       end
+end)
+
+minetest.register_chatcommand("rollback_check", {
+       params = "[<range>] [<seconds>] [limit]",
+       description = "check who has last touched a node or near it, "..
+                       "max. <seconds> ago (default range=0, seconds=86400=24h, limit=5)",
+       privs = {rollback=true},
+       func = function(name, param)
+               local range, seconds, limit =
+                       param:match("(%d+) *(%d*) *(%d*)")
+               range = tonumber(range) or 0
+               seconds = tonumber(seconds) or 86400
+               limit = tonumber(limit) or 5
+               if limit > 100 then
+                       minetest.chat_send_player(name, "That limit is too high!")
+                       return
+               end
+               minetest.chat_send_player(name, "Punch a node (range="..
+                               range..", seconds="..seconds.."s, limit="..limit..")")
+
+               minetest.rollback_punch_callbacks[name] = function(pos, node, puncher)
+                       local name = puncher:get_player_name()
+                       minetest.chat_send_player(name, "Checking "..minetest.pos_to_string(pos).."...")
+                       local actions = minetest.rollback_get_node_actions(pos, range, seconds, limit)
+                       local num_actions = #actions
+                       if num_actions == 0 then
+                               minetest.chat_send_player(name, "Nobody has touched the "..
+                                               "specified location in "..seconds.." seconds")
+                               return
+                       end
+                       local time = os.time()
+                       for i = num_actions, 1, -1 do
+                               local action = actions[i]
+                               minetest.chat_send_player(name,
+                                       ("%s %s %s -> %s %d seconds ago.")
+                                               :format(
+                                                       minetest.pos_to_string(action.pos),
+                                                       action.actor,
+                                                       action.oldnode.name,
+                                                       action.newnode.name,
+                                                       time - action.time))
+                       end
+               end
+       end,
+})
+
+minetest.register_chatcommand("rollback", {
+       params = "<player name> [<seconds>] | :<actor> [<seconds>]",
+       description = "revert actions of a player; default for <seconds> is 60",
+       privs = {rollback=true},
+       func = function(name, param)
+               local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)")
+               if not target_name then
+                       local player_name = nil
+                       player_name, seconds = string.match(param, "([^ ]+) *(%d*)")
+                       if not player_name then
+                               minetest.chat_send_player(name, "Invalid parameters. See /help rollback and /help rollback_check")
+                               return
+                       end
+                       target_name = "player:"..player_name
+               end
+               seconds = tonumber(seconds) or 60
+               minetest.chat_send_player(name, "Reverting actions of "..
+                               target_name.." since "..seconds.." seconds.")
+               local success, log = minetest.rollback_revert_actions_by(
+                               target_name, seconds)
+               if #log > 100 then
+                       minetest.chat_send_player(name, "(log is too long to show)")
+               else
+                       for _, line in pairs(log) do
+                               minetest.chat_send_player(name, line)
+                       end
+               end
+               if success then
+                       minetest.chat_send_player(name, "Reverting actions succeeded.")
+               else
+                       minetest.chat_send_player(name, "Reverting actions FAILED.")
+               end
+       end,
+})
+
+minetest.register_chatcommand("status", {
+       params = "",
+       description = "print server status line",
+       privs = {},
+       func = function(name, param)
+               minetest.chat_send_player(name, minetest.get_server_status())
+       end,
+})
+
+minetest.register_chatcommand("time", {
+       params = "<0...24000>",
+       description = "set time of day",
+       privs = {settime=true},
+       func = function(name, param)
+               if param == "" then
+                       minetest.chat_send_player(name, "Missing parameter")
+                       return
+               end
+               local newtime = tonumber(param)
+               if newtime == nil then
+                       minetest.chat_send_player(name, "Invalid time")
+               else
+                       minetest.set_timeofday((newtime % 24000) / 24000)
+                       minetest.chat_send_player(name, "Time of day changed.")
+                       minetest.log("action", name .. " sets time " .. newtime)
+               end
+       end,
+})
+
+minetest.register_chatcommand("shutdown", {
+       params = "",
+       description = "shutdown server",
+       privs = {server=true},
+       func = function(name, param)
+               minetest.log("action", name .. " shuts down server")
+               minetest.request_shutdown()
+               minetest.chat_send_all("*** Server shutting down (operator request).")
+       end,
+})
+
+minetest.register_chatcommand("ban", {
+       params = "<name>",
+       description = "ban IP of player",
+       privs = {ban=true},
+       func = function(name, param)
+               if param == "" then
+                       minetest.chat_send_player(name, "Ban list: " .. minetest.get_ban_list())
+                       return
+               end
+               if not minetest.get_player_by_name(param) then
+                       minetest.chat_send_player(name, "No such player")
+                       return
+               end
+               if not minetest.ban_player(param) then
+                       minetest.chat_send_player(name, "Failed to ban player")
+               else
+                       local desc = minetest.get_ban_description(param)
+                       minetest.chat_send_player(name, "Banned " .. desc .. ".")
+                       minetest.log("action", name .. " bans " .. desc .. ".")
+               end
+       end,
+})
+
+minetest.register_chatcommand("unban", {
+       params = "<name/ip>",
+       description = "remove IP ban",
+       privs = {ban=true},
+       func = function(name, param)
+               if not minetest.unban_player_or_ip(param) then
+                       minetest.chat_send_player(name, "Failed to unban player/IP")
+               else
+                       minetest.chat_send_player(name, "Unbanned " .. param)
+                       minetest.log("action", name .. " unbans " .. param)
+               end
+       end,
+})
+
+minetest.register_chatcommand("kick", {
+       params = "<name> [reason]",
+       description = "kick a player",
+       privs = {kick=true},
+       func = function(name, param)
+               local tokick, reason = string.match(param, "([^ ]+) (.+)")
+               if not tokick then
+                       tokick = param
+               end
+               if not minetest.kick_player(tokick, reason) then
+                       minetest.chat_send_player(name, "Failed to kick player " .. tokick)
+               else
+                       minetest.chat_send_player(name, "kicked " .. tokick)
+                       minetest.log("action", name .. " kicked " .. tokick)
+               end
+       end,
+})
+
+minetest.register_chatcommand("clearobjects", {
+       params = "",
+       description = "clear all objects in world",
+       privs = {server=true},
+       func = function(name, param)
+               minetest.log("action", name .. " clears all objects")
+               minetest.chat_send_all("Clearing all objects.  This may take long.  You may experience a timeout.  (by " .. name .. ")")
+               minetest.clear_objects()
+               minetest.log("action", "object clearing done")
+               minetest.chat_send_all("*** Cleared all objects.")
+       end,
+})
+
+minetest.register_chatcommand("msg", {
+       params = "<name> <message>",
+       description = "Send a private message",
+       privs = {shout=true},
+       func = function(name, param)
+               local found, _, sendto, message = param:find("^([^%s]+)%s(.+)$")
+               if found then
+                       if minetest.get_player_by_name(sendto) then
+                               minetest.log("action", "PM from "..name.." to "..sendto..": "..message)
+                               minetest.chat_send_player(sendto, "PM from "..name..": "..message)
+                               minetest.chat_send_player(name, "Message sent")
+                       else
+                               minetest.chat_send_player(name, "The player "..sendto.." is not online")
+                       end
+               else
+                       minetest.chat_send_player(name, "Invalid usage, see /help msg")
+               end
+       end,
+})
diff --git a/builtin/game/deprecated.lua b/builtin/game/deprecated.lua
new file mode 100644 (file)
index 0000000..d8b578d
--- /dev/null
@@ -0,0 +1,53 @@
+-- Minetest: builtin/deprecated.lua
+
+--
+-- Default material types
+--
+function digprop_err()
+       minetest.log("info", debug.traceback())
+       minetest.log("info", "WARNING: The minetest.digprop_* functions are obsolete and need to be replaced by item groups.")
+end
+
+minetest.digprop_constanttime = digprop_err
+minetest.digprop_stonelike = digprop_err
+minetest.digprop_dirtlike = digprop_err
+minetest.digprop_gravellike = digprop_err
+minetest.digprop_woodlike = digprop_err
+minetest.digprop_leaveslike = digprop_err
+minetest.digprop_glasslike = digprop_err
+
+minetest.node_metadata_inventory_move_allow_all = function()
+       minetest.log("info", "WARNING: minetest.node_metadata_inventory_move_allow_all is obsolete and does nothing.")
+end
+
+minetest.add_to_creative_inventory = function(itemstring)
+       minetest.log('info', "WARNING: minetest.add_to_creative_inventory: This function is deprecated and does nothing.")
+end
+
+--
+-- EnvRef
+--
+minetest.env = {}
+local envref_deprecation_message_printed = false
+setmetatable(minetest.env, {
+       __index = function(table, key)
+               if not envref_deprecation_message_printed then
+                       minetest.log("info", "WARNING: minetest.env:[...] is deprecated and should be replaced with minetest.[...]")
+                       envref_deprecation_message_printed = true
+               end
+               local func = minetest[key]
+               if type(func) == "function" then
+                       rawset(table, key, function(self, ...)
+                               return func(...)
+                       end)
+               else
+                       rawset(table, key, nil)
+               end
+               return rawget(table, key)
+       end
+})
+
+function minetest.rollback_get_last_node_actor(pos, range, seconds)
+       return minetest.rollback_get_node_actions(pos, range, seconds, 1)[1]
+end
+
diff --git a/builtin/game/detached_inventory.lua b/builtin/game/detached_inventory.lua
new file mode 100644 (file)
index 0000000..3757f13
--- /dev/null
@@ -0,0 +1,19 @@
+-- Minetest: builtin/detached_inventory.lua
+
+minetest.detached_inventories = {}
+
+function minetest.create_detached_inventory(name, callbacks)
+       local stuff = {}
+       stuff.name = name
+       if callbacks then
+               stuff.allow_move = callbacks.allow_move
+               stuff.allow_put = callbacks.allow_put
+               stuff.allow_take = callbacks.allow_take
+               stuff.on_move = callbacks.on_move
+               stuff.on_put = callbacks.on_put
+               stuff.on_take = callbacks.on_take
+       end
+       minetest.detached_inventories[name] = stuff
+       return minetest.create_detached_inventory_raw(name)
+end
+
diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua
new file mode 100644 (file)
index 0000000..93d1722
--- /dev/null
@@ -0,0 +1,217 @@
+-- Minetest: builtin/item.lua
+
+--
+-- Falling stuff
+--
+
+minetest.register_entity(":__builtin:falling_node", {
+       initial_properties = {
+               physical = true,
+               collide_with_objects = false,
+               collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
+               visual = "wielditem",
+               textures = {},
+               visual_size = {x=0.667, y=0.667},
+       },
+
+       node = {},
+
+       set_node = function(self, node)
+               self.node = node
+               local stack = ItemStack(node.name)
+               local itemtable = stack:to_table()
+               local itemname = nil
+               if itemtable then
+                       itemname = stack:to_table().name
+               end
+               local item_texture = nil
+               local item_type = ""
+               if minetest.registered_items[itemname] then
+                       item_texture = minetest.registered_items[itemname].inventory_image
+                       item_type = minetest.registered_items[itemname].type
+               end
+               prop = {
+                       is_visible = true,
+                       textures = {node.name},
+               }
+               self.object:set_properties(prop)
+       end,
+
+       get_staticdata = function(self)
+               return self.node.name
+       end,
+
+       on_activate = function(self, staticdata)
+               self.object:set_armor_groups({immortal=1})
+               --self.object:setacceleration({x=0, y=-10, z=0})
+               self:set_node({name=staticdata})
+       end,
+
+       on_step = function(self, dtime)
+               -- Set gravity
+               self.object:setacceleration({x=0, y=-10, z=0})
+               -- Turn to actual sand when collides to ground or just move
+               local pos = self.object:getpos()
+               local bcp = {x=pos.x, y=pos.y-0.7, z=pos.z} -- Position of bottom center point
+               local bcn = minetest.get_node(bcp)
+               local bcd = minetest.registered_nodes[bcn.name]
+               -- Note: walkable is in the node definition, not in item groups
+               if not bcd or
+                               (bcd.walkable or
+                               (minetest.get_item_group(self.node.name, "float") ~= 0 and
+                               bcd.liquidtype ~= "none")) then
+                       if bcd and bcd.leveled and
+                                       bcn.name == self.node.name then
+                               local addlevel = self.node.level
+                               if addlevel == nil or addlevel <= 0 then
+                                       addlevel = bcd.leveled
+                               end
+                               if minetest.add_node_level(bcp, addlevel) == 0 then
+                                       self.object:remove()
+                                       return
+                               end
+                       elseif bcd and bcd.buildable_to and
+                                       (minetest.get_item_group(self.node.name, "float") == 0 or
+                                       bcd.liquidtype == "none") then
+                               minetest.remove_node(bcp)
+                               return
+                       end
+                       local np = {x=bcp.x, y=bcp.y+1, z=bcp.z}
+                       -- Check what's here
+                       local n2 = minetest.get_node(np)
+                       -- If it's not air or liquid, remove node and replace it with
+                       -- it's drops
+                       if n2.name ~= "air" and (not minetest.registered_nodes[n2.name] or
+                                       minetest.registered_nodes[n2.name].liquidtype == "none") then
+                               local drops = minetest.get_node_drops(n2.name, "")
+                               minetest.remove_node(np)
+                               -- Add dropped items
+                               local _, dropped_item
+                               for _, dropped_item in ipairs(drops) do
+                                       minetest.add_item(np, dropped_item)
+                               end
+                               -- Run script hook
+                               local _, callback
+                               for _, callback in ipairs(minetest.registered_on_dignodes) do
+                                       callback(np, n2, nil)
+                               end
+                       end
+                       -- Create node and remove entity
+                       minetest.add_node(np, self.node)
+                       self.object:remove()
+                       nodeupdate(np)
+               else
+                       -- Do nothing
+               end
+       end
+})
+
+function spawn_falling_node(p, node)
+       obj = minetest.add_entity(p, "__builtin:falling_node")
+       obj:get_luaentity():set_node(node)
+end
+
+function drop_attached_node(p)
+       local nn = minetest.get_node(p).name
+       minetest.remove_node(p)
+       for _,item in ipairs(minetest.get_node_drops(nn, "")) do
+               local pos = {
+                       x = p.x + math.random()/2 - 0.25,
+                       y = p.y + math.random()/2 - 0.25,
+                       z = p.z + math.random()/2 - 0.25,
+               }
+               minetest.add_item(pos, item)
+       end
+end
+
+function check_attached_node(p, n)
+       local def = minetest.registered_nodes[n.name]
+       local d = {x=0, y=0, z=0}
+       if def.paramtype2 == "wallmounted" then
+               if n.param2 == 0 then
+                       d.y = 1
+               elseif n.param2 == 1 then
+                       d.y = -1
+               elseif n.param2 == 2 then
+                       d.x = 1
+               elseif n.param2 == 3 then
+                       d.x = -1
+               elseif n.param2 == 4 then
+                       d.z = 1
+               elseif n.param2 == 5 then
+                       d.z = -1
+               end
+       else
+               d.y = -1
+       end
+       local p2 = {x=p.x+d.x, y=p.y+d.y, z=p.z+d.z}
+       local nn = minetest.get_node(p2).name
+       local def2 = minetest.registered_nodes[nn]
+       if def2 and not def2.walkable then
+               return false
+       end
+       return true
+end
+
+--
+-- Some common functions
+--
+
+function nodeupdate_single(p, delay)
+       n = minetest.get_node(p)
+       if minetest.get_item_group(n.name, "falling_node") ~= 0 then
+               p_bottom = {x=p.x, y=p.y-1, z=p.z}
+               n_bottom = minetest.get_node(p_bottom)
+               -- Note: walkable is in the node definition, not in item groups
+               if minetest.registered_nodes[n_bottom.name] and
+                               (minetest.get_item_group(n.name, "float") == 0 or minetest.registered_nodes[n_bottom.name].liquidtype == "none") and
+                               (n.name ~= n_bottom.name or (minetest.registered_nodes[n_bottom.name].leveled and minetest.env:get_node_level(p_bottom) < minetest.env:get_node_max_level(p_bottom))) and
+                               (not minetest.registered_nodes[n_bottom.name].walkable or
+                                       minetest.registered_nodes[n_bottom.name].buildable_to) then
+                       if delay then
+                               minetest.after(0.1, nodeupdate_single, {x=p.x, y=p.y, z=p.z}, false)
+                       else
+                               n.level = minetest.env:get_node_level(p)
+                               minetest.remove_node(p)
+                               spawn_falling_node(p, n)
+                               nodeupdate(p)
+                       end
+               end
+       end
+       
+       if minetest.get_item_group(n.name, "attached_node") ~= 0 then
+               if not check_attached_node(p, n) then
+                       drop_attached_node(p)
+                       nodeupdate(p)
+               end
+       end
+end
+
+function nodeupdate(p, delay)
+       -- Round p to prevent falling entities to get stuck
+       p.x = math.floor(p.x+0.5)
+       p.y = math.floor(p.y+0.5)
+       p.z = math.floor(p.z+0.5)
+       
+       for x = -1,1 do
+       for y = -1,1 do
+       for z = -1,1 do
+               nodeupdate_single({x=p.x+x, y=p.y+y, z=p.z+z}, delay or not (x==0 and y==0 and z==0))
+       end
+       end
+       end
+end
+
+--
+-- Global callbacks
+--
+
+function on_placenode(p, node)
+       nodeupdate(p)
+end
+minetest.register_on_placenode(on_placenode)
+
+function on_dignode(p, node)
+       nodeupdate(p)
+end
+minetest.register_on_dignode(on_dignode)
diff --git a/builtin/game/features.lua b/builtin/game/features.lua
new file mode 100644 (file)
index 0000000..f3de3ba
--- /dev/null
@@ -0,0 +1,29 @@
+-- Minetest: builtin/features.lua
+
+minetest.features = {
+       glasslike_framed = true,
+       nodebox_as_selectionbox = true,
+       chat_send_player_param3 = true,
+       get_all_craft_recipes_works = true,
+       use_texture_alpha = true,
+       no_legacy_abms = true,
+}
+
+function minetest.has_feature(arg)
+       if type(arg) == "table" then
+               missing_features = {}
+               result = true
+               for ft, _ in pairs(arg) do
+                       if not minetest.features[ftr] then
+                               missing_features[ftr] = true
+                               result = false
+                       end
+               end
+               return result, missing_features
+       elseif type(arg) == "string" then
+               if not minetest.features[arg] then
+                       return false, {[arg]=true}
+               end
+               return true, {}
+       end
+end
diff --git a/builtin/game/forceloading.lua b/builtin/game/forceloading.lua
new file mode 100644 (file)
index 0000000..8489579
--- /dev/null
@@ -0,0 +1,79 @@
+-- Prevent anyone else accessing those functions
+local forceload_block = minetest.forceload_block
+local forceload_free_block = minetest.forceload_free_block
+minetest.forceload_block = nil
+minetest.forceload_free_block = nil
+
+local blocks_forceloaded
+local total_forceloaded = 0
+
+local BLOCKSIZE = 16
+local function get_blockpos(pos)
+       return {
+               x = math.floor(pos.x/BLOCKSIZE),
+               y = math.floor(pos.y/BLOCKSIZE),
+               z = math.floor(pos.z/BLOCKSIZE)}
+end
+
+function minetest.forceload_block(pos)
+       local blockpos = get_blockpos(pos)
+       local hash = minetest.hash_node_position(blockpos)
+       if blocks_forceloaded[hash] ~= nil then
+               blocks_forceloaded[hash] = blocks_forceloaded[hash] + 1
+               return true
+       else
+               if total_forceloaded >= (tonumber(minetest.setting_get("max_forceloaded_blocks")) or 16) then
+                       return false
+               end
+               total_forceloaded = total_forceloaded+1
+               blocks_forceloaded[hash] = 1
+               forceload_block(blockpos)
+               return true
+       end
+end
+
+function minetest.forceload_free_block(pos)
+       local blockpos = get_blockpos(pos)
+       local hash = minetest.hash_node_position(blockpos)
+       if blocks_forceloaded[hash] == nil then return end
+       if blocks_forceloaded[hash] > 1 then
+               blocks_forceloaded[hash] = blocks_forceloaded[hash] - 1
+       else
+               total_forceloaded = total_forceloaded-1
+               blocks_forceloaded[hash] = nil
+               forceload_free_block(blockpos)
+       end
+end
+
+-- Keep the forceloaded areas after restart
+local wpath = minetest.get_worldpath()
+local function read_file(filename)
+       local f = io.open(filename, "r")
+       if f==nil then return {} end
+       local t = f:read("*all")
+       f:close()
+       if t=="" or t==nil then return {} end
+       return minetest.deserialize(t)
+end
+
+local function write_file(filename, table)
+       local f = io.open(filename, "w")
+       f:write(minetest.serialize(table))
+       f:close()
+end
+
+blocks_forceloaded = read_file(wpath.."/force_loaded.txt")
+for _, __ in pairs(blocks_forceloaded) do
+       total_forceloaded = total_forceloaded + 1
+end
+
+minetest.after(5, function()
+       for hash, _ in pairs(blocks_forceloaded) do
+               local blockpos = minetest.get_position_from_hash(hash)
+               forceload_block(blockpos)
+       end
+end)
+
+minetest.register_on_shutdown(function()
+       write_file(wpath.."/force_loaded.txt", blocks_forceloaded)
+end)
diff --git a/builtin/game/init.lua b/builtin/game/init.lua
new file mode 100644 (file)
index 0000000..b878a26
--- /dev/null
@@ -0,0 +1,23 @@
+
+local scriptpath = minetest.get_builtin_path()..DIR_DELIM
+local commonpath = scriptpath.."common"..DIR_DELIM
+local gamepath = scriptpath.."game"..DIR_DELIM
+
+dofile(commonpath.."vector.lua")
+
+dofile(gamepath.."item.lua")
+dofile(gamepath.."register.lua")
+dofile(gamepath.."item_entity.lua")
+dofile(gamepath.."deprecated.lua")
+dofile(gamepath.."misc.lua")
+dofile(gamepath.."privileges.lua")
+dofile(gamepath.."auth.lua")
+dofile(gamepath.."chatcommands.lua")
+dofile(gamepath.."static_spawn.lua")
+dofile(gamepath.."detached_inventory.lua")
+dofile(gamepath.."falling.lua")
+dofile(gamepath.."features.lua")
+dofile(gamepath.."voxelarea.lua")
+dofile(gamepath.."forceloading.lua")
+dofile(gamepath.."statbars.lua")
+
diff --git a/builtin/game/item.lua b/builtin/game/item.lua
new file mode 100644 (file)
index 0000000..002c14f
--- /dev/null
@@ -0,0 +1,589 @@
+-- Minetest: builtin/item.lua
+
+local function copy_pointed_thing(pointed_thing)
+       return {
+               type  = pointed_thing.type,
+               above = vector.new(pointed_thing.above),
+               under = vector.new(pointed_thing.under),
+               ref   = pointed_thing.ref,
+       }
+end
+
+--
+-- Item definition helpers
+--
+
+function minetest.inventorycube(img1, img2, img3)
+       img2 = img2 or img1
+       img3 = img3 or img1
+       return "[inventorycube"
+                       .. "{" .. img1:gsub("%^", "&")
+                       .. "{" .. img2:gsub("%^", "&")
+                       .. "{" .. img3:gsub("%^", "&")
+end
+
+function minetest.get_pointed_thing_position(pointed_thing, above)
+       if pointed_thing.type == "node" then
+               if above then
+                       -- The position where a node would be placed
+                       return pointed_thing.above
+               else
+                       -- The position where a node would be dug
+                       return pointed_thing.under
+               end
+       elseif pointed_thing.type == "object" then
+               obj = pointed_thing.ref
+               if obj ~= nil then
+                       return obj:getpos()
+               else
+                       return nil
+               end
+       else
+               return nil
+       end
+end
+
+function minetest.dir_to_facedir(dir, is6d)
+       --account for y if requested
+       if is6d and math.abs(dir.y) > math.abs(dir.x) and math.abs(dir.y) > math.abs(dir.z) then
+
+               --from above
+               if dir.y < 0 then
+                       if math.abs(dir.x) > math.abs(dir.z) then
+                               if dir.x < 0 then
+                                       return 19
+                               else
+                                       return 13
+                               end
+                       else
+                               if dir.z < 0 then
+                                       return 10
+                               else
+                                       return 4
+                               end
+                       end
+
+               --from below
+               else
+                       if math.abs(dir.x) > math.abs(dir.z) then
+                               if dir.x < 0 then
+                                       return 15
+                               else
+                                       return 17
+                               end
+                       else
+                               if dir.z < 0 then
+                                       return 6
+                               else
+                                       return 8
+                               end
+                       end
+               end
+
+       --otherwise, place horizontally
+       elseif math.abs(dir.x) > math.abs(dir.z) then
+               if dir.x < 0 then
+                       return 3
+               else
+                       return 1
+               end
+       else
+               if dir.z < 0 then
+                       return 2
+               else
+                       return 0
+               end
+       end
+end
+
+function minetest.facedir_to_dir(facedir)
+       --a table of possible dirs
+       return ({{x=0, y=0, z=1},
+                                       {x=1, y=0, z=0},
+                                       {x=0, y=0, z=-1},
+                                       {x=-1, y=0, z=0},
+                                       {x=0, y=-1, z=0},
+                                       {x=0, y=1, z=0}})
+
+                                       --indexed into by a table of correlating facedirs
+                                       [({[0]=1, 2, 3, 4, 
+                                               5, 2, 6, 4,
+                                               6, 2, 5, 4,
+                                               1, 5, 3, 6,
+                                               1, 6, 3, 5,
+                                               1, 4, 3, 2})
+
+                                               --indexed into by the facedir in question
+                                               [facedir]]
+end
+
+function minetest.dir_to_wallmounted(dir)
+       if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then
+               if dir.y < 0 then
+                       return 1
+               else
+                       return 0
+               end
+       elseif math.abs(dir.x) > math.abs(dir.z) then
+               if dir.x < 0 then
+                       return 3
+               else
+                       return 2
+               end
+       else
+               if dir.z < 0 then
+                       return 5
+               else
+                       return 4
+               end
+       end
+end
+
+function minetest.get_node_drops(nodename, toolname)
+       local drop = ItemStack({name=nodename}):get_definition().drop
+       if drop == nil then
+               -- default drop
+               return {nodename}
+       elseif type(drop) == "string" then
+               -- itemstring drop
+               return {drop}
+       elseif drop.items == nil then
+               -- drop = {} to disable default drop
+               return {}
+       end
+
+       -- Extended drop table
+       local got_items = {}
+       local got_count = 0
+       local _, item, tool
+       for _, item in ipairs(drop.items) do
+               local good_rarity = true
+               local good_tool = true
+               if item.rarity ~= nil then
+                       good_rarity = item.rarity < 1 or math.random(item.rarity) == 1
+               end
+               if item.tools ~= nil then
+                       good_tool = false
+                       for _, tool in ipairs(item.tools) do
+                               if tool:sub(1, 1) == '~' then
+                                       good_tool = toolname:find(tool:sub(2)) ~= nil
+                               else
+                                       good_tool = toolname == tool
+                               end
+                               if good_tool then
+                                       break
+                               end
+                       end
+               end
+               if good_rarity and good_tool then
+                       got_count = got_count + 1
+                       for _, add_item in ipairs(item.items) do
+                               got_items[#got_items+1] = add_item
+                       end
+                       if drop.max_items ~= nil and got_count == drop.max_items then
+                               break
+                       end
+               end
+       end
+       return got_items
+end
+
+function minetest.item_place_node(itemstack, placer, pointed_thing, param2)
+       local item = itemstack:peek_item()
+       local def = itemstack:get_definition()
+       if def.type ~= "node" or pointed_thing.type ~= "node" then
+               return itemstack, false
+       end
+
+       local under = pointed_thing.under
+       local oldnode_under = minetest.get_node_or_nil(under)
+       local above = pointed_thing.above
+       local oldnode_above = minetest.get_node_or_nil(above)
+
+       if not oldnode_under or not oldnode_above then
+               minetest.log("info", placer:get_player_name() .. " tried to place"
+                       .. " node in unloaded position " .. minetest.pos_to_string(above))
+               return itemstack, false
+       end
+
+       local olddef_under = ItemStack({name=oldnode_under.name}):get_definition()
+       olddef_under = olddef_under or minetest.nodedef_default
+       local olddef_above = ItemStack({name=oldnode_above.name}):get_definition()
+       olddef_above = olddef_above or minetest.nodedef_default
+
+       if not olddef_above.buildable_to and not olddef_under.buildable_to then
+               minetest.log("info", placer:get_player_name() .. " tried to place"
+                       .. " node in invalid position " .. minetest.pos_to_string(above)
+                       .. ", replacing " .. oldnode_above.name)
+               return itemstack, false
+       end
+
+       -- Place above pointed node
+       local place_to = {x = above.x, y = above.y, z = above.z}
+
+       -- If node under is buildable_to, place into it instead (eg. snow)
+       if olddef_under.buildable_to then
+               minetest.log("info", "node under is buildable to")
+               place_to = {x = under.x, y = under.y, z = under.z}
+       end
+
+       if minetest.is_protected(place_to, placer:get_player_name()) then
+               minetest.log("action", placer:get_player_name()
+                               .. " tried to place " .. def.name
+                               .. " at protected position "
+                               .. minetest.pos_to_string(place_to))
+               minetest.record_protection_violation(place_to, placer:get_player_name())
+               return itemstack
+       end
+
+       minetest.log("action", placer:get_player_name() .. " places node "
+               .. def.name .. " at " .. minetest.pos_to_string(place_to))
+       
+       local oldnode = minetest.get_node(place_to)
+       local newnode = {name = def.name, param1 = 0, param2 = param2}
+
+       -- Calculate direction for wall mounted stuff like torches and signs
+       if def.paramtype2 == 'wallmounted' and not param2 then
+               local dir = {
+                       x = under.x - above.x,
+                       y = under.y - above.y,
+                       z = under.z - above.z
+               }
+               newnode.param2 = minetest.dir_to_wallmounted(dir)
+       -- Calculate the direction for furnaces and chests and stuff
+       elseif def.paramtype2 == 'facedir' and not param2 then
+               local placer_pos = placer:getpos()
+               if placer_pos then
+                       local dir = {
+                               x = above.x - placer_pos.x,
+                               y = above.y - placer_pos.y,
+                               z = above.z - placer_pos.z
+                       }
+                       newnode.param2 = minetest.dir_to_facedir(dir)
+                       minetest.log("action", "facedir: " .. newnode.param2)
+               end
+       end
+
+       -- Check if the node is attached and if it can be placed there
+       if minetest.get_item_group(def.name, "attached_node") ~= 0 and
+               not check_attached_node(place_to, newnode) then
+               minetest.log("action", "attached node " .. def.name ..
+                       " can not be placed at " .. minetest.pos_to_string(place_to))
+               return itemstack, false
+       end
+
+       -- Add node and update
+       minetest.add_node(place_to, newnode)
+
+       local take_item = true
+
+       -- Run callback
+       if def.after_place_node then
+               -- Deepcopy place_to and pointed_thing because callback can modify it
+               local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
+               local pointed_thing_copy = copy_pointed_thing(pointed_thing)
+               if def.after_place_node(place_to_copy, placer, itemstack,
+                               pointed_thing_copy) then
+                       take_item = false
+               end
+       end
+
+       -- Run script hook
+       local _, callback
+       for _, callback in ipairs(minetest.registered_on_placenodes) do
+               -- Deepcopy pos, node and pointed_thing because callback can modify them
+               local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
+               local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2}
+               local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2}
+               local pointed_thing_copy = copy_pointed_thing(pointed_thing)
+               if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then
+                       take_item = false
+               end
+       end
+
+       if take_item then
+               itemstack:take_item()
+       end
+       return itemstack, true
+end
+
+function minetest.item_place_object(itemstack, placer, pointed_thing)
+       local pos = minetest.get_pointed_thing_position(pointed_thing, true)
+       if pos ~= nil then
+               local item = itemstack:take_item()
+               minetest.add_item(pos, item)
+       end
+       return itemstack
+end
+
+function minetest.item_place(itemstack, placer, pointed_thing, param2)
+       -- Call on_rightclick if the pointed node defines it
+       if pointed_thing.type == "node" and placer and
+                       not placer:get_player_control().sneak then
+               local n = minetest.get_node(pointed_thing.under)
+               local nn = n.name
+               if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].on_rightclick then
+                       return minetest.registered_nodes[nn].on_rightclick(pointed_thing.under, n,
+                                       placer, itemstack, pointed_thing) or itemstack, false
+               end
+       end
+
+       if itemstack:get_definition().type == "node" then
+               return minetest.item_place_node(itemstack, placer, pointed_thing, param2)
+       end
+       return itemstack
+end
+
+function minetest.item_drop(itemstack, dropper, pos)
+       if dropper.get_player_name then
+               local v = dropper:get_look_dir()
+               local p = {x=pos.x+v.x, y=pos.y+1.5+v.y, z=pos.z+v.z}
+               local obj = minetest.add_item(p, itemstack)
+               if obj then
+                       v.x = v.x*2
+                       v.y = v.y*2 + 1
+                       v.z = v.z*2
+                       obj:setvelocity(v)
+               end
+       else
+               minetest.add_item(pos, itemstack)
+       end
+       return ItemStack("")
+end
+
+function minetest.item_eat(hp_change, replace_with_item)
+       return function(itemstack, user, pointed_thing)  -- closure
+               if itemstack:take_item() ~= nil then
+                       user:set_hp(user:get_hp() + hp_change)
+                       itemstack:add_item(replace_with_item) -- note: replace_with_item is optional
+               end
+               return itemstack
+       end
+end
+
+function minetest.node_punch(pos, node, puncher, pointed_thing)
+       -- Run script hook
+       for _, callback in ipairs(minetest.registered_on_punchnodes) do
+               -- Copy pos and node because callback can modify them
+               local pos_copy = vector.new(pos)
+               local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
+               local pointed_thing_copy = pointed_thing and copy_pointed_thing(pointed_thing) or nil
+               callback(pos_copy, node_copy, puncher, pointed_thing_copy)
+       end
+end
+
+function minetest.handle_node_drops(pos, drops, digger)
+       -- Add dropped items to object's inventory
+       if digger:get_inventory() then
+               local _, dropped_item
+               for _, dropped_item in ipairs(drops) do
+                       local left = digger:get_inventory():add_item("main", dropped_item)
+                       if not left:is_empty() then
+                               local p = {
+                                       x = pos.x + math.random()/2-0.25,
+                                       y = pos.y + math.random()/2-0.25,
+                                       z = pos.z + math.random()/2-0.25,
+                               }
+                               minetest.add_item(p, left)
+                       end
+               end
+       end
+end
+
+function minetest.node_dig(pos, node, digger)
+       local def = ItemStack({name=node.name}):get_definition()
+       if not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then
+               minetest.log("info", digger:get_player_name() .. " tried to dig "
+                       .. node.name .. " which is not diggable "
+                       .. minetest.pos_to_string(pos))
+               return
+       end
+
+       if minetest.is_protected(pos, digger:get_player_name()) then
+               minetest.log("action", digger:get_player_name()
+                               .. " tried to dig " .. node.name
+                               .. " at protected position "
+                               .. minetest.pos_to_string(pos))
+               minetest.record_protection_violation(pos, digger:get_player_name())
+               return
+       end
+
+       minetest.log('action', digger:get_player_name() .. " digs "
+               .. node.name .. " at " .. minetest.pos_to_string(pos))
+
+       local wielded = digger:get_wielded_item()
+       local drops = minetest.get_node_drops(node.name, wielded:get_name())
+       
+       local wdef = wielded:get_definition()
+       local tp = wielded:get_tool_capabilities()
+       local dp = minetest.get_dig_params(def.groups, tp)
+       if wdef and wdef.after_use then
+               wielded = wdef.after_use(wielded, digger, node, dp) or wielded
+       else
+               -- Wear out tool
+               if not minetest.setting_getbool("creative_mode") then
+                       wielded:add_wear(dp.wear)
+               end
+       end
+       digger:set_wielded_item(wielded)
+       
+       -- Handle drops
+       minetest.handle_node_drops(pos, drops, digger)
+
+       local oldmetadata = nil
+       if def.after_dig_node then
+               oldmetadata = minetest.get_meta(pos):to_table()
+       end
+
+       -- Remove node and update
+       minetest.remove_node(pos)
+       
+       -- Run callback
+       if def.after_dig_node then
+               -- Copy pos and node because callback can modify them
+               local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
+               local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
+               def.after_dig_node(pos_copy, node_copy, oldmetadata, digger)
+       end
+
+       -- Run script hook
+       local _, callback
+       for _, callback in ipairs(minetest.registered_on_dignodes) do
+               -- Copy pos and node because callback can modify them
+               local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
+               local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
+               callback(pos_copy, node_copy, digger)
+       end
+end
+
+-- This is used to allow mods to redefine minetest.item_place and so on
+-- NOTE: This is not the preferred way. Preferred way is to provide enough
+--       callbacks to not require redefining global functions. -celeron55
+local function redef_wrapper(table, name)
+       return function(...)
+               return table[name](...)
+       end
+end
+
+--
+-- Item definition defaults
+--
+
+minetest.nodedef_default = {
+       -- Item properties
+       type="node",
+       -- name intentionally not defined here
+       description = "",
+       groups = {},
+       inventory_image = "",
+       wield_image = "",
+       wield_scale = {x=1,y=1,z=1},
+       stack_max = 99,
+       usable = false,
+       liquids_pointable = false,
+       tool_capabilities = nil,
+       node_placement_prediction = nil,
+
+       -- Interaction callbacks
+       on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
+       on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop
+       on_use = nil,
+       can_dig = nil,
+
+       on_punch = redef_wrapper(minetest, 'node_punch'), -- minetest.node_punch
+       on_rightclick = nil,
+       on_dig = redef_wrapper(minetest, 'node_dig'), -- minetest.node_dig
+
+       on_receive_fields = nil,
+       
+       on_metadata_inventory_move = minetest.node_metadata_inventory_move_allow_all,
+       on_metadata_inventory_offer = minetest.node_metadata_inventory_offer_allow_all,
+       on_metadata_inventory_take = minetest.node_metadata_inventory_take_allow_all,
+
+       -- Node properties
+       drawtype = "normal",
+       visual_scale = 1.0,
+       -- Don't define these because otherwise the old tile_images and
+       -- special_materials wouldn't be read
+       --tiles ={""},
+       --special_tiles = {
+       --      {name="", backface_culling=true},
+       --      {name="", backface_culling=true},
+       --},
+       alpha = 255,
+       post_effect_color = {a=0, r=0, g=0, b=0},
+       paramtype = "none",
+       paramtype2 = "none",
+       is_ground_content = true,
+       sunlight_propagates = false,
+       walkable = true,
+       pointable = true,
+       diggable = true,
+       climbable = false,
+       buildable_to = false,
+       liquidtype = "none",
+       liquid_alternative_flowing = "",
+       liquid_alternative_source = "",
+       liquid_viscosity = 0,
+       drowning = 0,
+       light_source = 0,
+       damage_per_second = 0,
+       selection_box = {type="regular"},
+       legacy_facedir_simple = false,
+       legacy_wallmounted = false,
+}
+
+minetest.craftitemdef_default = {
+       type="craft",
+       -- name intentionally not defined here
+       description = "",
+       groups = {},
+       inventory_image = "",
+       wield_image = "",
+       wield_scale = {x=1,y=1,z=1},
+       stack_max = 99,
+       liquids_pointable = false,
+       tool_capabilities = nil,
+
+       -- Interaction callbacks
+       on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
+       on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop
+       on_use = nil,
+}
+
+minetest.tooldef_default = {
+       type="tool",
+       -- name intentionally not defined here
+       description = "",
+       groups = {},
+       inventory_image = "",
+       wield_image = "",
+       wield_scale = {x=1,y=1,z=1},
+       stack_max = 1,
+       liquids_pointable = false,
+       tool_capabilities = nil,
+
+       -- Interaction callbacks
+       on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
+       on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop
+       on_use = nil,
+}
+
+minetest.noneitemdef_default = {  -- This is used for the hand and unknown items
+       type="none",
+       -- name intentionally not defined here
+       description = "",
+       groups = {},
+       inventory_image = "",
+       wield_image = "",
+       wield_scale = {x=1,y=1,z=1},
+       stack_max = 99,
+       liquids_pointable = false,
+       tool_capabilities = nil,
+
+       -- Interaction callbacks
+       on_place = redef_wrapper(minetest, 'item_place'),
+       on_drop = nil,
+       on_use = nil,
+}
+
diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua
new file mode 100644 (file)
index 0000000..8150e6d
--- /dev/null
@@ -0,0 +1,123 @@
+-- Minetest: builtin/item_entity.lua
+
+function minetest.spawn_item(pos, item)
+       -- Take item in any format
+       local stack = ItemStack(item)
+       local obj = minetest.add_entity(pos, "__builtin:item")
+       obj:get_luaentity():set_item(stack:to_string())
+       return obj
+end
+
+minetest.register_entity(":__builtin:item", {
+       initial_properties = {
+               hp_max = 1,
+               physical = true,
+               collide_with_objects = false,
+               collisionbox = {-0.17,-0.17,-0.17, 0.17,0.17,0.17},
+               visual = "sprite",
+               visual_size = {x=0.5, y=0.5},
+               textures = {""},
+               spritediv = {x=1, y=1},
+               initial_sprite_basepos = {x=0, y=0},
+               is_visible = false,
+       },
+       
+       itemstring = '',
+       physical_state = true,
+
+       set_item = function(self, itemstring)
+               self.itemstring = itemstring
+               local stack = ItemStack(itemstring)
+               local itemtable = stack:to_table()
+               local itemname = nil
+               if itemtable then
+                       itemname = stack:to_table().name
+               end
+               local item_texture = nil
+               local item_type = ""
+               if minetest.registered_items[itemname] then
+                       item_texture = minetest.registered_items[itemname].inventory_image
+                       item_type = minetest.registered_items[itemname].type
+               end
+               prop = {
+                       is_visible = true,
+                       visual = "sprite",
+                       textures = {"unknown_item.png"}
+               }
+               if item_texture and item_texture ~= "" then
+                       prop.visual = "sprite"
+                       prop.textures = {item_texture}
+                       prop.visual_size = {x=0.50, y=0.50}
+               else
+                       prop.visual = "wielditem"
+                       prop.textures = {itemname}
+                       prop.visual_size = {x=0.20, y=0.20}
+                       prop.automatic_rotate = math.pi * 0.25
+               end
+               self.object:set_properties(prop)
+       end,
+
+       get_staticdata = function(self)
+               --return self.itemstring
+               return minetest.serialize({
+                       itemstring = self.itemstring,
+                       always_collect = self.always_collect,
+               })
+       end,
+
+       on_activate = function(self, staticdata)
+               if string.sub(staticdata, 1, string.len("return")) == "return" then
+                       local data = minetest.deserialize(staticdata)
+                       if data and type(data) == "table" then
+                               self.itemstring = data.itemstring
+                               self.always_collect = data.always_collect
+                       end
+               else
+                       self.itemstring = staticdata
+               end
+               self.object:set_armor_groups({immortal=1})
+               self.object:setvelocity({x=0, y=2, z=0})
+               self.object:setacceleration({x=0, y=-10, z=0})
+               self:set_item(self.itemstring)
+       end,
+
+       on_step = function(self, dtime)
+               local p = self.object:getpos()
+               p.y = p.y - 0.3
+               local nn = minetest.get_node(p).name
+               -- If node is not registered or node is walkably solid and resting on nodebox
+               local v = self.object:getvelocity()
+               if not minetest.registered_nodes[nn] or minetest.registered_nodes[nn].walkable and v.y == 0 then
+                       if self.physical_state then
+                               self.object:setvelocity({x=0,y=0,z=0})
+                               self.object:setacceleration({x=0, y=0, z=0})
+                               self.physical_state = false
+                               self.object:set_properties({
+                                       physical = false
+                               })
+                       end
+               else
+                       if not self.physical_state then
+                               self.object:setvelocity({x=0,y=0,z=0})
+                               self.object:setacceleration({x=0, y=-10, z=0})
+                               self.physical_state = true
+                               self.object:set_properties({
+                                       physical = true
+                               })
+                       end
+               end
+       end,
+
+       on_punch = function(self, hitter)
+               if self.itemstring ~= '' then
+                       local left = hitter:get_inventory():add_item("main", self.itemstring)
+                       if not left:is_empty() then
+                               self.itemstring = left:to_string()
+                               return
+                       end
+               end
+               self.itemstring = ''
+               self.object:remove()
+       end,
+})
+
diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua
new file mode 100644 (file)
index 0000000..82cc527
--- /dev/null
@@ -0,0 +1,134 @@
+-- Minetest: builtin/misc.lua
+
+--
+-- Misc. API functions
+--
+
+minetest.timers_to_add = {}
+minetest.timers = {}
+minetest.register_globalstep(function(dtime)
+       for _, timer in ipairs(minetest.timers_to_add) do
+               table.insert(minetest.timers, timer)
+       end
+       minetest.timers_to_add = {}
+       for index, timer in ipairs(minetest.timers) do
+               timer.time = timer.time - dtime
+               if timer.time <= 0 then
+                       timer.func(unpack(timer.args or {}))
+                       table.remove(minetest.timers,index)
+               end
+       end
+end)
+
+function minetest.after(time, func, ...)
+       assert(tonumber(time) and type(func) == "function",
+                       "Invalid minetest.after invocation")
+       table.insert(minetest.timers_to_add, {time=time, func=func, args={...}})
+end
+
+function minetest.check_player_privs(name, privs)
+       local player_privs = minetest.get_player_privs(name)
+       local missing_privileges = {}
+       for priv, val in pairs(privs) do
+               if val then
+                       if not player_privs[priv] then
+                               table.insert(missing_privileges, priv)
+                       end
+               end
+       end
+       if #missing_privileges > 0 then
+               return false, missing_privileges
+       end
+       return true, ""
+end
+
+local player_list = {}
+
+minetest.register_on_joinplayer(function(player)
+       player_list[player:get_player_name()] = player
+end)
+
+minetest.register_on_leaveplayer(function(player)
+       player_list[player:get_player_name()] = nil
+end)
+
+function minetest.get_connected_players()
+       local temp_table = {}
+       for index, value in pairs(player_list) do
+               if value:is_player_connected() then
+                       table.insert(temp_table, value)
+               end
+       end
+       return temp_table
+end
+
+function minetest.hash_node_position(pos)
+       return (pos.z+32768)*65536*65536 + (pos.y+32768)*65536 + pos.x+32768
+end
+
+function minetest.get_position_from_hash(hash)
+       local pos = {}
+       pos.x = (hash%65536) - 32768
+       hash = math.floor(hash/65536)
+       pos.y = (hash%65536) - 32768
+       hash = math.floor(hash/65536)
+       pos.z = (hash%65536) - 32768
+       return pos
+end
+
+function minetest.get_item_group(name, group)
+       if not minetest.registered_items[name] or not
+                       minetest.registered_items[name].groups[group] then
+               return 0
+       end
+       return minetest.registered_items[name].groups[group]
+end
+
+function minetest.get_node_group(name, group)
+       minetest.log("deprecated", "Deprecated usage of get_node_group, use get_item_group instead")
+       return minetest.get_item_group(name, group)
+end
+
+function minetest.string_to_pos(value)
+       local p = {}
+       p.x, p.y, p.z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
+       if p.x and p.y and p.z then
+               p.x = tonumber(p.x)
+               p.y = tonumber(p.y)
+               p.z = tonumber(p.z)
+               return p
+       end
+       local p = {}
+       p.x, p.y, p.z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$")
+       if p.x and p.y and p.z then
+               p.x = tonumber(p.x)
+               p.y = tonumber(p.y)
+               p.z = tonumber(p.z)
+               return p
+       end
+       return nil
+end
+
+assert(minetest.string_to_pos("10.0, 5, -2").x == 10)
+assert(minetest.string_to_pos("( 10.0, 5, -2)").z == -2)
+assert(minetest.string_to_pos("asd, 5, -2)") == nil)
+
+function minetest.setting_get_pos(name)
+       local value = minetest.setting_get(name)
+       if not value then
+               return nil
+       end
+       return minetest.string_to_pos(value)
+end
+
+-- To be overriden by protection mods
+function minetest.is_protected(pos, name)
+       return false
+end
+
+function minetest.record_protection_violation(pos, name)
+       for _, func in pairs(minetest.registered_on_protection_violation) do
+               func(pos, name)
+       end
+end
+
diff --git a/builtin/game/privileges.lua b/builtin/game/privileges.lua
new file mode 100644 (file)
index 0000000..244aa45
--- /dev/null
@@ -0,0 +1,53 @@
+-- Minetest: builtin/privileges.lua
+
+--
+-- Privileges
+--
+
+minetest.registered_privileges = {}
+
+function minetest.register_privilege(name, param)
+       local function fill_defaults(def)
+               if def.give_to_singleplayer == nil then
+                       def.give_to_singleplayer = true
+               end
+               if def.description == nil then
+                       def.description = "(no description)"
+               end
+       end
+       local def = {}
+       if type(param) == "table" then
+               def = param
+       else
+               def = {description = param}
+       end
+       fill_defaults(def)
+       minetest.registered_privileges[name] = def
+end
+
+minetest.register_privilege("interact", "Can interact with things and modify the world")
+minetest.register_privilege("teleport", "Can use /teleport command")
+minetest.register_privilege("bring", "Can teleport other players")
+minetest.register_privilege("settime", "Can use /time")
+minetest.register_privilege("privs", "Can modify privileges")
+minetest.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges")
+minetest.register_privilege("server", "Can do server maintenance stuff")
+minetest.register_privilege("shout", "Can speak in chat")
+minetest.register_privilege("ban", "Can ban and unban players")
+minetest.register_privilege("kick", "Can kick players")
+minetest.register_privilege("give", "Can use /give and /giveme")
+minetest.register_privilege("password", "Can use /setpassword and /clearpassword")
+minetest.register_privilege("fly", {
+       description = "Can fly using the free_move mode",
+       give_to_singleplayer = false,
+})
+minetest.register_privilege("fast", {
+       description = "Can walk fast using the fast_move mode",
+       give_to_singleplayer = false,
+})
+minetest.register_privilege("noclip", {
+       description = "Can fly through walls",
+       give_to_singleplayer = false,
+})
+minetest.register_privilege("rollback", "Can use the rollback functionality")
+
diff --git a/builtin/game/register.lua b/builtin/game/register.lua
new file mode 100644 (file)
index 0000000..99c5115
--- /dev/null
@@ -0,0 +1,410 @@
+-- Minetest: builtin/misc_register.lua
+
+--
+-- Make raw registration functions inaccessible to anyone except this file
+--
+
+local register_item_raw = minetest.register_item_raw
+minetest.register_item_raw = nil
+
+local register_alias_raw = minetest.register_alias_raw
+minetest.register_item_raw = nil
+
+--
+-- Item / entity / ABM registration functions
+--
+
+minetest.registered_abms = {}
+minetest.registered_entities = {}
+minetest.registered_items = {}
+minetest.registered_nodes = {}
+minetest.registered_craftitems = {}
+minetest.registered_tools = {}
+minetest.registered_aliases = {}
+
+-- For tables that are indexed by item name:
+-- If table[X] does not exist, default to table[minetest.registered_aliases[X]]
+local alias_metatable = {
+       __index = function(t, name)
+               return rawget(t, minetest.registered_aliases[name])
+       end
+}
+setmetatable(minetest.registered_items, alias_metatable)
+setmetatable(minetest.registered_nodes, alias_metatable)
+setmetatable(minetest.registered_craftitems, alias_metatable)
+setmetatable(minetest.registered_tools, alias_metatable)
+
+-- These item names may not be used because they would interfere
+-- with legacy itemstrings
+local forbidden_item_names = {
+       MaterialItem = true,
+       MaterialItem2 = true,
+       MaterialItem3 = true,
+       NodeItem = true,
+       node = true,
+       CraftItem = true,
+       craft = true,
+       MBOItem = true,
+       ToolItem = true,
+       tool = true,
+}
+
+local function check_modname_prefix(name)
+       if name:sub(1,1) == ":" then
+               -- Escape the modname prefix enforcement mechanism
+               return name:sub(2)
+       else
+               -- Modname prefix enforcement
+               local expected_prefix = minetest.get_current_modname() .. ":"
+               if name:sub(1, #expected_prefix) ~= expected_prefix then
+                       error("Name " .. name .. " does not follow naming conventions: " ..
+                               "\"modname:\" or \":\" prefix required")
+               end
+               local subname = name:sub(#expected_prefix+1)
+               if subname:find("[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_]") then
+                       error("Name " .. name .. " does not follow naming conventions: " ..
+                               "contains unallowed characters")
+               end
+               return name
+       end
+end
+
+function minetest.register_abm(spec)
+       -- Add to minetest.registered_abms
+       minetest.registered_abms[#minetest.registered_abms+1] = spec
+end
+
+function minetest.register_entity(name, prototype)
+       -- Check name
+       if name == nil then
+               error("Unable to register entity: Name is nil")
+       end
+       name = check_modname_prefix(tostring(name))
+
+       prototype.name = name
+       prototype.__index = prototype  -- so that it can be used as a metatable
+
+       -- Add to minetest.registered_entities
+       minetest.registered_entities[name] = prototype
+end
+
+function minetest.register_item(name, itemdef)
+       -- Check name
+       if name == nil then
+               error("Unable to register item: Name is nil")
+       end
+       name = check_modname_prefix(tostring(name))
+       if forbidden_item_names[name] then
+               error("Unable to register item: Name is forbidden: " .. name)
+       end
+       itemdef.name = name
+
+       -- Apply defaults and add to registered_* table
+       if itemdef.type == "node" then
+               -- Use the nodebox as selection box if it's not set manually
+               if itemdef.drawtype == "nodebox" and not itemdef.selection_box then
+                       itemdef.selection_box = itemdef.node_box
+               elseif itemdef.drawtype == "fencelike" and not itemdef.selection_box then
+                       itemdef.selection_box = {
+                               type = "fixed",
+                               fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8},
+                       }
+               end
+               setmetatable(itemdef, {__index = minetest.nodedef_default})
+               minetest.registered_nodes[itemdef.name] = itemdef
+       elseif itemdef.type == "craft" then
+               setmetatable(itemdef, {__index = minetest.craftitemdef_default})
+               minetest.registered_craftitems[itemdef.name] = itemdef
+       elseif itemdef.type == "tool" then
+               setmetatable(itemdef, {__index = minetest.tooldef_default})
+               minetest.registered_tools[itemdef.name] = itemdef
+       elseif itemdef.type == "none" then
+               setmetatable(itemdef, {__index = minetest.noneitemdef_default})
+       else
+               error("Unable to register item: Type is invalid: " .. dump(itemdef))
+       end
+
+       -- Flowing liquid uses param2
+       if itemdef.type == "node" and itemdef.liquidtype == "flowing" then
+               itemdef.paramtype2 = "flowingliquid"
+       end
+
+       -- BEGIN Legacy stuff
+       if itemdef.cookresult_itemstring ~= nil and itemdef.cookresult_itemstring ~= "" then
+               minetest.register_craft({
+                       type="cooking",
+                       output=itemdef.cookresult_itemstring,
+                       recipe=itemdef.name,
+                       cooktime=itemdef.furnace_cooktime
+               })
+       end
+       if itemdef.furnace_burntime ~= nil and itemdef.furnace_burntime >= 0 then
+               minetest.register_craft({
+                       type="fuel",
+                       recipe=itemdef.name,
+                       burntime=itemdef.furnace_burntime
+               })
+       end
+       -- END Legacy stuff
+
+       -- Disable all further modifications
+       getmetatable(itemdef).__newindex = {}
+
+       --minetest.log("Registering item: " .. itemdef.name)
+       minetest.registered_items[itemdef.name] = itemdef
+       minetest.registered_aliases[itemdef.name] = nil
+       register_item_raw(itemdef)
+end
+
+function minetest.register_node(name, nodedef)
+       nodedef.type = "node"
+       minetest.register_item(name, nodedef)
+end
+
+function minetest.register_craftitem(name, craftitemdef)
+       craftitemdef.type = "craft"
+
+       -- BEGIN Legacy stuff
+       if craftitemdef.inventory_image == nil and craftitemdef.image ~= nil then
+               craftitemdef.inventory_image = craftitemdef.image
+       end
+       -- END Legacy stuff
+
+       minetest.register_item(name, craftitemdef)
+end
+
+function minetest.register_tool(name, tooldef)
+       tooldef.type = "tool"
+       tooldef.stack_max = 1
+
+       -- BEGIN Legacy stuff
+       if tooldef.inventory_image == nil and tooldef.image ~= nil then
+               tooldef.inventory_image = tooldef.image
+       end
+       if tooldef.tool_capabilities == nil and
+          (tooldef.full_punch_interval ~= nil or
+           tooldef.basetime ~= nil or
+           tooldef.dt_weight ~= nil or
+           tooldef.dt_crackiness ~= nil or
+           tooldef.dt_crumbliness ~= nil or
+           tooldef.dt_cuttability ~= nil or
+           tooldef.basedurability ~= nil or
+           tooldef.dd_weight ~= nil or
+           tooldef.dd_crackiness ~= nil or
+           tooldef.dd_crumbliness ~= nil or
+           tooldef.dd_cuttability ~= nil) then
+               tooldef.tool_capabilities = {
+                       full_punch_interval = tooldef.full_punch_interval,
+                       basetime = tooldef.basetime,
+                       dt_weight = tooldef.dt_weight,
+                       dt_crackiness = tooldef.dt_crackiness,
+                       dt_crumbliness = tooldef.dt_crumbliness,
+                       dt_cuttability = tooldef.dt_cuttability,
+                       basedurability = tooldef.basedurability,
+                       dd_weight = tooldef.dd_weight,
+                       dd_crackiness = tooldef.dd_crackiness,
+                       dd_crumbliness = tooldef.dd_crumbliness,
+                       dd_cuttability = tooldef.dd_cuttability,
+               }
+       end
+       -- END Legacy stuff
+
+       minetest.register_item(name, tooldef)
+end
+
+function minetest.register_alias(name, convert_to)
+       if forbidden_item_names[name] then
+               error("Unable to register alias: Name is forbidden: " .. name)
+       end
+       if minetest.registered_items[name] ~= nil then
+               minetest.log("WARNING: Not registering alias, item with same name" ..
+                       " is already defined: " .. name .. " -> " .. convert_to)
+       else
+               --minetest.log("Registering alias: " .. name .. " -> " .. convert_to)
+               minetest.registered_aliases[name] = convert_to
+               register_alias_raw(name, convert_to)
+       end
+end
+
+local register_biome_raw = minetest.register_biome
+minetest.registered_biomes = {}
+function minetest.register_biome(biome)
+       minetest.registered_biomes[biome.name] = biome
+       register_biome_raw(biome)
+end
+
+function minetest.on_craft(itemstack, player, old_craft_list, craft_inv)
+       for _, func in ipairs(minetest.registered_on_crafts) do
+               itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack
+       end
+       return itemstack
+end
+
+function minetest.craft_predict(itemstack, player, old_craft_list, craft_inv)
+       for _, func in ipairs(minetest.registered_craft_predicts) do
+               itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack
+       end
+       return itemstack
+end
+
+-- Alias the forbidden item names to "" so they can't be
+-- created via itemstrings (e.g. /give)
+local name
+for name in pairs(forbidden_item_names) do
+       minetest.registered_aliases[name] = ""
+       register_alias_raw(name, "")
+end
+
+
+-- Deprecated:
+-- Aliases for minetest.register_alias (how ironic...)
+--minetest.alias_node = minetest.register_alias
+--minetest.alias_tool = minetest.register_alias
+--minetest.alias_craftitem = minetest.register_alias
+
+--
+-- Built-in node definitions. Also defined in C.
+--
+
+minetest.register_item(":unknown", {
+       type = "none",
+       description = "Unknown Item",
+       inventory_image = "unknown_item.png",
+       on_place = minetest.item_place,
+       on_drop = minetest.item_drop,
+       groups = {not_in_creative_inventory=1},
+       diggable = true,
+})
+
+minetest.register_node(":air", {
+       description = "Air (you hacker you!)",
+       inventory_image = "unknown_node.png",
+       wield_image = "unknown_node.png",
+       drawtype = "airlike",
+       paramtype = "light",
+       sunlight_propagates = true,
+       walkable = false,
+       pointable = false,
+       diggable = false,
+       buildable_to = true,
+       air_equivalent = true,
+       drop = "",
+       groups = {not_in_creative_inventory=1},
+})
+
+minetest.register_node(":ignore", {
+       description = "Ignore (you hacker you!)",
+       inventory_image = "unknown_node.png",
+       wield_image = "unknown_node.png",
+       drawtype = "airlike",
+       paramtype = "none",
+       sunlight_propagates = false,
+       walkable = false,
+       pointable = false,
+       diggable = false,
+       buildable_to = true, -- A way to remove accidentally placed ignores
+       air_equivalent = true,
+       drop = "",
+       groups = {not_in_creative_inventory=1},
+})
+
+-- The hand (bare definition)
+minetest.register_item(":", {
+       type = "none",
+       groups = {not_in_creative_inventory=1},
+})
+
+
+function minetest.override_item(name, redefinition)
+       if redefinition.name ~= nil then
+               error("Attempt to redefine name of "..name.." to "..dump(redefinition.name), 2)
+       end
+       if redefinition.type ~= nil then
+               error("Attempt to redefine type of "..name.." to "..dump(redefinition.type), 2)
+       end
+       local item = minetest.registered_items[name]
+       if not item then
+               error("Attempt to override non-existent item "..name, 2)
+       end
+       for k, v in pairs(redefinition) do
+               rawset(item, k, v)
+       end
+       register_item_raw(item)
+end
+
+
+function minetest.run_callbacks(callbacks, mode, ...)
+       assert(type(callbacks) == "table")
+       local cb_len = #callbacks
+       if cb_len == 0 then
+               if mode == 2 or mode == 3 then
+                       return true
+               elseif mode == 4 or mode == 5 then
+                       return false
+               end
+       end
+       local ret = nil
+       for i = 1, cb_len do
+               local cb_ret = callbacks[i](...)
+
+               if mode == 0 and i == 1 then
+                       ret = cb_ret
+               elseif mode == 1 and i == cb_len then
+                       ret = cb_ret
+               elseif mode == 2 then
+                       if not cb_ret or i == 1 then
+                               ret = cb_ret
+                       end
+               elseif mode == 3 then
+                       if cb_ret then
+                               return cb_ret
+                       end
+                       ret = cb_ret
+               elseif mode == 4 then
+                       if (cb_ret and not ret) or i == 1 then
+                               ret = cb_ret
+                       end
+               elseif mode == 5 and cb_ret then
+                       return cb_ret
+               end
+       end
+       return ret
+end
+
+--
+-- Callback registration
+--
+
+local function make_registration()
+       local t = {}
+       local registerfunc = function(func) table.insert(t, func) end
+       return t, registerfunc
+end
+
+local function make_registration_reverse()
+       local t = {}
+       local registerfunc = function(func) table.insert(t, 1, func) end
+       return t, registerfunc
+end
+
+minetest.registered_on_chat_messages, minetest.register_on_chat_message = make_registration()
+minetest.registered_globalsteps, minetest.register_globalstep = make_registration()
+minetest.registered_playerevents, minetest.register_playerevent = make_registration()
+minetest.registered_on_mapgen_inits, minetest.register_on_mapgen_init = make_registration()
+minetest.registered_on_shutdown, minetest.register_on_shutdown = make_registration()
+minetest.registered_on_punchnodes, minetest.register_on_punchnode = make_registration()
+minetest.registered_on_placenodes, minetest.register_on_placenode = make_registration()
+minetest.registered_on_dignodes, minetest.register_on_dignode = make_registration()
+minetest.registered_on_generateds, minetest.register_on_generated = make_registration()
+minetest.registered_on_newplayers, minetest.register_on_newplayer = make_registration()
+minetest.registered_on_dieplayers, minetest.register_on_dieplayer = make_registration()
+minetest.registered_on_respawnplayers, minetest.register_on_respawnplayer = make_registration()
+minetest.registered_on_prejoinplayers, minetest.register_on_prejoinplayer = make_registration()
+minetest.registered_on_joinplayers, minetest.register_on_joinplayer = make_registration()
+minetest.registered_on_leaveplayers, minetest.register_on_leaveplayer = make_registration()
+minetest.registered_on_player_receive_fields, minetest.register_on_player_receive_fields = make_registration_reverse()
+minetest.registered_on_cheats, minetest.register_on_cheat = make_registration()
+minetest.registered_on_crafts, minetest.register_on_craft = make_registration()
+minetest.registered_craft_predicts, minetest.register_craft_predict = make_registration()
+minetest.registered_on_protection_violation, minetest.register_on_protection_violation = make_registration()
+
diff --git a/builtin/game/statbars.lua b/builtin/game/statbars.lua
new file mode 100644 (file)
index 0000000..ca656a9
--- /dev/null
@@ -0,0 +1,160 @@
+
+local health_bar_definition =
+{
+       hud_elem_type = "statbar",
+       position = { x=0.5, y=1 },
+       text = "heart.png",
+       number = 20,
+       direction = 0,
+       size = { x=24, y=24 },
+       offset = { x=(-10*24)-25, y=-(48+24+10)},
+}
+
+local breath_bar_definition =
+{
+       hud_elem_type = "statbar",
+       position = { x=0.5, y=1 },
+       text = "bubble.png",
+       number = 20,
+       direction = 0,
+       size = { x=24, y=24 },
+       offset = {x=25,y=-(48+24+10)},
+}
+
+local hud_ids = {}
+
+local function initialize_builtin_statbars(player)
+
+       if not player:is_player() then
+               return
+       end
+
+       local name = player:get_player_name()
+
+       if name == "" then
+               return
+       end
+
+       if (hud_ids[name] == nil) then
+               hud_ids[name] = {}
+       end
+
+       if player:hud_get_flags().healthbar then
+               if hud_ids[name].id_healthbar == nil then
+                       health_bar_definition.number = player:get_hp()
+                       hud_ids[name].id_healthbar  = player:hud_add(health_bar_definition)
+               end
+       else
+               if hud_ids[name].id_healthbar ~= nil then
+                       player:hud_remove(hud_ids[name].id_healthbar)
+                       hud_ids[name].id_healthbar = nil
+               end
+       end
+
+       if (player:get_breath() < 11) then
+               if player:hud_get_flags().breathbar then
+                       if hud_ids[name].id_breathbar == nil then
+                               hud_ids[name].id_breathbar = player:hud_add(breath_bar_definition)
+                       end
+               else
+                       if hud_ids[name].id_breathbar ~= nil then
+                               player:hud_remove(hud_ids[name].id_breathbar)
+                               hud_ids[name].id_breathbar = nil
+                       end
+               end
+       elseif hud_ids[name].id_breathbar ~= nil then
+               player:hud_remove(hud_ids[name].id_breathbar)
+               hud_ids[name].id_breathbar = nil
+       end
+end
+
+local function cleanup_builtin_statbars(player)
+
+       if not player:is_player() then
+               return
+       end
+
+       local name = player:get_player_name()
+
+       if name == "" then
+               return
+       end
+
+       hud_ids[name] = nil
+end
+
+local function player_event_handler(player,eventname)
+       assert(player:is_player())
+
+       local name = player:get_player_name()
+
+       if name == "" then
+               return
+       end
+
+       if eventname == "health_changed" then
+               initialize_builtin_statbars(player)
+
+               if hud_ids[name].id_healthbar ~= nil then
+                       player:hud_change(hud_ids[name].id_healthbar,"number",player:get_hp())
+                       return true
+               end
+       end
+
+       if eventname == "breath_changed" then
+               initialize_builtin_statbars(player)
+
+               if hud_ids[name].id_breathbar ~= nil then
+                       player:hud_change(hud_ids[name].id_breathbar,"number",player:get_breath()*2)
+                       return true
+               end
+       end
+
+       if eventname == "hud_changed" then
+               initialize_builtin_statbars(player)
+               return true
+       end
+
+       return false
+end
+
+function minetest.hud_replace_builtin(name, definition)
+
+       if definition == nil or
+               type(definition) ~= "table" or
+               definition.hud_elem_type ~= "statbar" then
+               return false
+       end
+
+       if name == "health" then
+               health_bar_definition = definition
+
+               for name,ids in pairs(hud_ids) do
+                       local player = minetest.get_player_by_name(name)
+                       if  player and hud_ids[name].id_healthbar then
+                               player:hud_remove(hud_ids[name].id_healthbar)
+                               initialize_builtin_statbars(player)
+                       end
+               end
+               return true
+       end
+
+       if name == "breath" then
+               breath_bar_definition = definition
+
+               for name,ids in pairs(hud_ids) do
+                       local player = minetest.get_player_by_name(name)
+                       if  player and hud_ids[name].id_breathbar then
+                               player:hud_remove(hud_ids[name].id_breathbar)
+                               initialize_builtin_statbars(player)
+                       end
+               end
+               return true
+       end
+
+       return false
+end
+
+minetest.register_on_joinplayer(initialize_builtin_statbars)
+minetest.register_on_leaveplayer(cleanup_builtin_statbars)
+minetest.register_playerevent(player_event_handler)
diff --git a/builtin/game/static_spawn.lua b/builtin/game/static_spawn.lua
new file mode 100644 (file)
index 0000000..e8c107d
--- /dev/null
@@ -0,0 +1,33 @@
+-- Minetest: builtin/static_spawn.lua
+
+local function warn_invalid_static_spawnpoint()
+       if minetest.setting_get("static_spawnpoint") and
+                       not minetest.setting_get_pos("static_spawnpoint") then
+               minetest.log('error', "The static_spawnpoint setting is invalid: \""..
+                               minetest.setting_get("static_spawnpoint").."\"")
+       end
+end
+
+warn_invalid_static_spawnpoint()
+
+local function put_player_in_spawn(obj)
+       warn_invalid_static_spawnpoint()
+       local static_spawnpoint = minetest.setting_get_pos("static_spawnpoint")
+       if not static_spawnpoint then
+               return false
+       end
+       minetest.log('action', "Moving "..obj:get_player_name()..
+                       " to static spawnpoint at "..
+                       minetest.pos_to_string(static_spawnpoint))
+       obj:setpos(static_spawnpoint)
+       return true
+end
+
+minetest.register_on_newplayer(function(obj)
+       put_player_in_spawn(obj)
+end)
+
+minetest.register_on_respawnplayer(function(obj)
+       return put_player_in_spawn(obj)
+end)
+
diff --git a/builtin/game/voxelarea.lua b/builtin/game/voxelarea.lua
new file mode 100644 (file)
index 0000000..93bbf73
--- /dev/null
@@ -0,0 +1,103 @@
+VoxelArea = {
+       MinEdge = {x=1, y=1, z=1},
+       MaxEdge = {x=0, y=0, z=0},
+       ystride = 0,
+       zstride = 0,
+}
+
+function VoxelArea:new(o)
+       o = o or {}
+       setmetatable(o, self)
+       self.__index = self
+
+       local e = o:getExtent()
+       o.ystride = e.x
+       o.zstride = e.x * e.y
+
+       return o
+end
+
+function VoxelArea:getExtent()
+       return {
+               x = self.MaxEdge.x - self.MinEdge.x + 1,
+               y = self.MaxEdge.y - self.MinEdge.y + 1,
+               z = self.MaxEdge.z - self.MinEdge.z + 1,
+       }
+end
+
+function VoxelArea:getVolume()
+       local e = self:getExtent()
+       return e.x * e.y * e.z
+end
+
+function VoxelArea:index(x, y, z)
+       local i = (z - self.MinEdge.z) * self.zstride +
+                         (y - self.MinEdge.y) * self.ystride +
+                         (x - self.MinEdge.x) + 1
+       return math.floor(i)
+end
+
+function VoxelArea:indexp(p)
+       local i = (p.z - self.MinEdge.z) * self.zstride +
+                         (p.y - self.MinEdge.y) * self.ystride +
+                         (p.x - self.MinEdge.x) + 1
+       return math.floor(i)
+end
+
+function VoxelArea:position(i)
+       local p = {}
+       i = i - 1
+
+       p.z = math.floor(i / self.zstride) + self.MinEdge.z
+       i = i % self.zstride
+
+       p.y = math.floor(i / self.ystride) + self.MinEdge.y
+       i = i % self.ystride
+
+       p.x = math.floor(i) + self.MinEdge.x
+
+       return p
+end
+
+function VoxelArea:contains(x, y, z)
+       return (x >= self.MinEdge.x) and (x <= self.MaxEdge.x) and
+                  (y >= self.MinEdge.y) and (y <= self.MaxEdge.y) and
+                  (z >= self.MinEdge.z) and (z <= self.MaxEdge.z)
+end
+
+function VoxelArea:containsp(p)
+       return (p.x >= self.MinEdge.x) and (p.x <= self.MaxEdge.x) and
+                  (p.y >= self.MinEdge.y) and (p.y <= self.MaxEdge.y) and
+                  (p.z >= self.MinEdge.z) and (p.z <= self.MaxEdge.z)
+end
+
+function VoxelArea:containsi(i)
+       return (i >= 1) and (i <= self:getVolume())
+end
+
+function VoxelArea:iter(minx, miny, minz, maxx, maxy, maxz)
+       local i = self:index(minx, miny, minz) - 1
+       local last = self:index(maxx, maxy, maxz)
+       local ystride = self.ystride
+       local zstride = self.zstride
+       local yoff = (last+1) % ystride
+       local zoff = (last+1) % zstride
+       local ystridediff = (i - last) % ystride
+       local zstridediff = (i - last) % zstride
+       return function()
+               i = i + 1
+               if i % zstride == zoff then
+                       i = i + zstridediff
+               elseif i % ystride == yoff then
+                       i = i + ystridediff
+               end
+               if i <= last then
+                       return i
+               end
+       end
+end
+
+function VoxelArea:iterp(minp, maxp)
+       return self:iter(minp.x, minp.y, minp.z, maxp.x, maxp.y, maxp.z)
+end
diff --git a/builtin/gamemgr.lua b/builtin/gamemgr.lua
deleted file mode 100644 (file)
index c99c2de..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
---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;" .. fgettext("Game Name") .. "]"..
-               "field[4.5,2.4;6,0.5;te_game_name;;]" ..
-               "button[5,4.2;2.6,0.5;new_game_confirm;" .. fgettext("Create") .. "]" ..
-               "button[7.5,4.2;2.8,0.5;new_game_cancel;" .. fgettext("Cancel") .. "]"
-
-       return retval
-end
-
---------------------------------------------------------------------------------
-function gamemgr.handle_games_buttons(fields)
-       if fields["gamelist"] ~= nil then
-               local event = engine.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")
-               
-               local mod = modmgr.get_global_mod(modindex)
-               if mod ~= nil then
-                       
-                       local sourcepath = mod.path
-                       
-                       if not gamemgr.add_mod(current_game,sourcepath) then
-                               gamedata.errormessage =
-                                       fgettext("Gamemgr: Unable to copy mod \"$1\" to game \"$2\"", mod.name, current_game.id)
-                       end
-               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)
-               
-               return engine.copy_dir(sourcepath,gamespec.gamemods_path .. DIR_DELIM .. modname);
-       end
-       
-       return false
-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
-
-                       if game_mods[modindex].path:sub(0,gamespec.gamemods_path:len()) 
-                                       == gamespec.gamemods_path then
-                               engine.delete_dir(game_mods[modindex].path)
-                       end
-               end
-       end
-end
-
---------------------------------------------------------------------------------
-function gamemgr.find_by_gameid(gameid)
-       for i=1,#gamemgr.games,1 do             
-               if gamemgr.games[i].id == gameid then
-                       return gamemgr.games[i], i
-               end
-       end
-       return nil, nil
-end
-
---------------------------------------------------------------------------------
-function gamemgr.get_game_mods(gamespec, retval)
-       if gamespec ~= nil and
-               gamespec.gamemods_path ~= nil and
-               gamespec.gamemods_path ~= "" then
-               get_mods(gamespec.gamemods_path, retval)
-       end
-end
-
---------------------------------------------------------------------------------
-function gamemgr.get_game_modlist(gamespec)
-       local retval = ""
-       local game_mods = {}
-       gamemgr.get_game_mods(gamespec, game_mods)
-       for i=1,#game_mods,1 do
-               if retval ~= "" then
-                       retval = retval..","
-               end
-               retval = retval .. game_mods[i].name
-       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;" .. fgettext("GAMES") .. "]" ..
-               "label[1,-0.25;" .. fgettext("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;" ..
-                               engine.formspec_escape(current_game.menuicon_path) .. "]"
-               end
-               
-               retval = retval ..
-                       "field[8,-0.25;6,2;;" .. current_game.name .. ";]"..
-                       "label[6,1.4;" .. fgettext("Mods:") .."]" ..
-                       "button[9.7,1.5;2,0.2;btn_game_mgr_edit_game;" .. fgettext("edit game") .. "]" ..
-                       "textlist[6,2;5.5,3.3;game_mgr_modlist;"
-                       .. gamemgr.get_game_modlist(current_game) ..";0]" ..
-                       "button[1,4.75;3.2,0.5;btn_game_mgr_new_game;" .. fgettext("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;" .. fgettext("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;" ..
-                               engine.formspec_escape(current_game.menuicon_path) .. "]"
-               end
-               
-               retval = retval .. 
-                       "textlist[0.5,0.5;4.5,4.3;mods_current;"
-                       .. gamemgr.get_game_modlist(current_game) ..";0]"
-                       
-                       
-               retval = retval .. 
-                       "textlist[7,0.5;4.5,4.3;mods_available;"
-                       .. modmgr.render_modlist() .. ";0]"
-
-               retval = retval ..
-                       "button[0.55,4.95;4.7,0.5;btn_remove_mod_from_game;" .. fgettext("Remove selected mod") .."]"
-                       
-               retval = retval ..
-                       "button[7.05,4.95;4.7,0.5;btn_add_mod_to_game;" .. fgettext("<<-- 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
diff --git a/builtin/init.lua b/builtin/init.lua
new file mode 100644 (file)
index 0000000..9969111
--- /dev/null
@@ -0,0 +1,34 @@
+--
+-- This file contains built-in stuff in Minetest implemented in Lua.
+--
+-- It is always loaded and executed after registration of the C API,
+-- before loading and running any mods.
+--
+
+local core = minetest or engine
+minetest = core
+
+-- Initialize some very basic things
+print = core.debug
+math.randomseed(os.time())
+os.setlocale("C", "numeric")
+
+-- Load other files
+local scriptdir = core.get_builtin_path()..DIR_DELIM
+local gamepath = scriptdir.."game"..DIR_DELIM
+local commonpath = scriptdir.."common"..DIR_DELIM
+local asyncpath = scriptdir.."async"..DIR_DELIM
+
+dofile(commonpath.."serialize.lua")
+dofile(commonpath.."misc_helpers.lua")
+
+if INIT == "game" then
+       dofile(gamepath.."init.lua")
+elseif INIT == "mainmenu" then
+       dofile(core.get_mainmenu_path()..DIR_DELIM.."init.lua")
+elseif INIT == "async" then
+       dofile(asyncpath.."init.lua")
+else
+       error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT)))
+end
+
diff --git a/builtin/item.lua b/builtin/item.lua
deleted file mode 100644 (file)
index 002c14f..0000000
+++ /dev/null
@@ -1,589 +0,0 @@
--- Minetest: builtin/item.lua
-
-local function copy_pointed_thing(pointed_thing)
-       return {
-               type  = pointed_thing.type,
-               above = vector.new(pointed_thing.above),
-               under = vector.new(pointed_thing.under),
-               ref   = pointed_thing.ref,
-       }
-end
-
---
--- Item definition helpers
---
-
-function minetest.inventorycube(img1, img2, img3)
-       img2 = img2 or img1
-       img3 = img3 or img1
-       return "[inventorycube"
-                       .. "{" .. img1:gsub("%^", "&")
-                       .. "{" .. img2:gsub("%^", "&")
-                       .. "{" .. img3:gsub("%^", "&")
-end
-
-function minetest.get_pointed_thing_position(pointed_thing, above)
-       if pointed_thing.type == "node" then
-               if above then
-                       -- The position where a node would be placed
-                       return pointed_thing.above
-               else
-                       -- The position where a node would be dug
-                       return pointed_thing.under
-               end
-       elseif pointed_thing.type == "object" then
-               obj = pointed_thing.ref
-               if obj ~= nil then
-                       return obj:getpos()
-               else
-                       return nil
-               end
-       else
-               return nil
-       end
-end
-
-function minetest.dir_to_facedir(dir, is6d)
-       --account for y if requested
-       if is6d and math.abs(dir.y) > math.abs(dir.x) and math.abs(dir.y) > math.abs(dir.z) then
-
-               --from above
-               if dir.y < 0 then
-                       if math.abs(dir.x) > math.abs(dir.z) then
-                               if dir.x < 0 then
-                                       return 19
-                               else
-                                       return 13
-                               end
-                       else
-                               if dir.z < 0 then
-                                       return 10
-                               else
-                                       return 4
-                               end
-                       end
-
-               --from below
-               else
-                       if math.abs(dir.x) > math.abs(dir.z) then
-                               if dir.x < 0 then
-                                       return 15
-                               else
-                                       return 17
-                               end
-                       else
-                               if dir.z < 0 then
-                                       return 6
-                               else
-                                       return 8
-                               end
-                       end
-               end
-
-       --otherwise, place horizontally
-       elseif math.abs(dir.x) > math.abs(dir.z) then
-               if dir.x < 0 then
-                       return 3
-               else
-                       return 1
-               end
-       else
-               if dir.z < 0 then
-                       return 2
-               else
-                       return 0
-               end
-       end
-end
-
-function minetest.facedir_to_dir(facedir)
-       --a table of possible dirs
-       return ({{x=0, y=0, z=1},
-                                       {x=1, y=0, z=0},
-                                       {x=0, y=0, z=-1},
-                                       {x=-1, y=0, z=0},
-                                       {x=0, y=-1, z=0},
-                                       {x=0, y=1, z=0}})
-
-                                       --indexed into by a table of correlating facedirs
-                                       [({[0]=1, 2, 3, 4, 
-                                               5, 2, 6, 4,
-                                               6, 2, 5, 4,
-                                               1, 5, 3, 6,
-                                               1, 6, 3, 5,
-                                               1, 4, 3, 2})
-
-                                               --indexed into by the facedir in question
-                                               [facedir]]
-end
-
-function minetest.dir_to_wallmounted(dir)
-       if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then
-               if dir.y < 0 then
-                       return 1
-               else
-                       return 0
-               end
-       elseif math.abs(dir.x) > math.abs(dir.z) then
-               if dir.x < 0 then
-                       return 3
-               else
-                       return 2
-               end
-       else
-               if dir.z < 0 then
-                       return 5
-               else
-                       return 4
-               end
-       end
-end
-
-function minetest.get_node_drops(nodename, toolname)
-       local drop = ItemStack({name=nodename}):get_definition().drop
-       if drop == nil then
-               -- default drop
-               return {nodename}
-       elseif type(drop) == "string" then
-               -- itemstring drop
-               return {drop}
-       elseif drop.items == nil then
-               -- drop = {} to disable default drop
-               return {}
-       end
-
-       -- Extended drop table
-       local got_items = {}
-       local got_count = 0
-       local _, item, tool
-       for _, item in ipairs(drop.items) do
-               local good_rarity = true
-               local good_tool = true
-               if item.rarity ~= nil then
-                       good_rarity = item.rarity < 1 or math.random(item.rarity) == 1
-               end
-               if item.tools ~= nil then
-                       good_tool = false
-                       for _, tool in ipairs(item.tools) do
-                               if tool:sub(1, 1) == '~' then
-                                       good_tool = toolname:find(tool:sub(2)) ~= nil
-                               else
-                                       good_tool = toolname == tool
-                               end
-                               if good_tool then
-                                       break
-                               end
-                       end
-               end
-               if good_rarity and good_tool then
-                       got_count = got_count + 1
-                       for _, add_item in ipairs(item.items) do
-                               got_items[#got_items+1] = add_item
-                       end
-                       if drop.max_items ~= nil and got_count == drop.max_items then
-                               break
-                       end
-               end
-       end
-       return got_items
-end
-
-function minetest.item_place_node(itemstack, placer, pointed_thing, param2)
-       local item = itemstack:peek_item()
-       local def = itemstack:get_definition()
-       if def.type ~= "node" or pointed_thing.type ~= "node" then
-               return itemstack, false
-       end
-
-       local under = pointed_thing.under
-       local oldnode_under = minetest.get_node_or_nil(under)
-       local above = pointed_thing.above
-       local oldnode_above = minetest.get_node_or_nil(above)
-
-       if not oldnode_under or not oldnode_above then
-               minetest.log("info", placer:get_player_name() .. " tried to place"
-                       .. " node in unloaded position " .. minetest.pos_to_string(above))
-               return itemstack, false
-       end
-
-       local olddef_under = ItemStack({name=oldnode_under.name}):get_definition()
-       olddef_under = olddef_under or minetest.nodedef_default
-       local olddef_above = ItemStack({name=oldnode_above.name}):get_definition()
-       olddef_above = olddef_above or minetest.nodedef_default
-
-       if not olddef_above.buildable_to and not olddef_under.buildable_to then
-               minetest.log("info", placer:get_player_name() .. " tried to place"
-                       .. " node in invalid position " .. minetest.pos_to_string(above)
-                       .. ", replacing " .. oldnode_above.name)
-               return itemstack, false
-       end
-
-       -- Place above pointed node
-       local place_to = {x = above.x, y = above.y, z = above.z}
-
-       -- If node under is buildable_to, place into it instead (eg. snow)
-       if olddef_under.buildable_to then
-               minetest.log("info", "node under is buildable to")
-               place_to = {x = under.x, y = under.y, z = under.z}
-       end
-
-       if minetest.is_protected(place_to, placer:get_player_name()) then
-               minetest.log("action", placer:get_player_name()
-                               .. " tried to place " .. def.name
-                               .. " at protected position "
-                               .. minetest.pos_to_string(place_to))
-               minetest.record_protection_violation(place_to, placer:get_player_name())
-               return itemstack
-       end
-
-       minetest.log("action", placer:get_player_name() .. " places node "
-               .. def.name .. " at " .. minetest.pos_to_string(place_to))
-       
-       local oldnode = minetest.get_node(place_to)
-       local newnode = {name = def.name, param1 = 0, param2 = param2}
-
-       -- Calculate direction for wall mounted stuff like torches and signs
-       if def.paramtype2 == 'wallmounted' and not param2 then
-               local dir = {
-                       x = under.x - above.x,
-                       y = under.y - above.y,
-                       z = under.z - above.z
-               }
-               newnode.param2 = minetest.dir_to_wallmounted(dir)
-       -- Calculate the direction for furnaces and chests and stuff
-       elseif def.paramtype2 == 'facedir' and not param2 then
-               local placer_pos = placer:getpos()
-               if placer_pos then
-                       local dir = {
-                               x = above.x - placer_pos.x,
-                               y = above.y - placer_pos.y,
-                               z = above.z - placer_pos.z
-                       }
-                       newnode.param2 = minetest.dir_to_facedir(dir)
-                       minetest.log("action", "facedir: " .. newnode.param2)
-               end
-       end
-
-       -- Check if the node is attached and if it can be placed there
-       if minetest.get_item_group(def.name, "attached_node") ~= 0 and
-               not check_attached_node(place_to, newnode) then
-               minetest.log("action", "attached node " .. def.name ..
-                       " can not be placed at " .. minetest.pos_to_string(place_to))
-               return itemstack, false
-       end
-
-       -- Add node and update
-       minetest.add_node(place_to, newnode)
-
-       local take_item = true
-
-       -- Run callback
-       if def.after_place_node then
-               -- Deepcopy place_to and pointed_thing because callback can modify it
-               local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
-               local pointed_thing_copy = copy_pointed_thing(pointed_thing)
-               if def.after_place_node(place_to_copy, placer, itemstack,
-                               pointed_thing_copy) then
-                       take_item = false
-               end
-       end
-
-       -- Run script hook
-       local _, callback
-       for _, callback in ipairs(minetest.registered_on_placenodes) do
-               -- Deepcopy pos, node and pointed_thing because callback can modify them
-               local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
-               local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2}
-               local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2}
-               local pointed_thing_copy = copy_pointed_thing(pointed_thing)
-               if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then
-                       take_item = false
-               end
-       end
-
-       if take_item then
-               itemstack:take_item()
-       end
-       return itemstack, true
-end
-
-function minetest.item_place_object(itemstack, placer, pointed_thing)
-       local pos = minetest.get_pointed_thing_position(pointed_thing, true)
-       if pos ~= nil then
-               local item = itemstack:take_item()
-               minetest.add_item(pos, item)
-       end
-       return itemstack
-end
-
-function minetest.item_place(itemstack, placer, pointed_thing, param2)
-       -- Call on_rightclick if the pointed node defines it
-       if pointed_thing.type == "node" and placer and
-                       not placer:get_player_control().sneak then
-               local n = minetest.get_node(pointed_thing.under)
-               local nn = n.name
-               if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].on_rightclick then
-                       return minetest.registered_nodes[nn].on_rightclick(pointed_thing.under, n,
-                                       placer, itemstack, pointed_thing) or itemstack, false
-               end
-       end
-
-       if itemstack:get_definition().type == "node" then
-               return minetest.item_place_node(itemstack, placer, pointed_thing, param2)
-       end
-       return itemstack
-end
-
-function minetest.item_drop(itemstack, dropper, pos)
-       if dropper.get_player_name then
-               local v = dropper:get_look_dir()
-               local p = {x=pos.x+v.x, y=pos.y+1.5+v.y, z=pos.z+v.z}
-               local obj = minetest.add_item(p, itemstack)
-               if obj then
-                       v.x = v.x*2
-                       v.y = v.y*2 + 1
-                       v.z = v.z*2
-                       obj:setvelocity(v)
-               end
-       else
-               minetest.add_item(pos, itemstack)
-       end
-       return ItemStack("")
-end
-
-function minetest.item_eat(hp_change, replace_with_item)
-       return function(itemstack, user, pointed_thing)  -- closure
-               if itemstack:take_item() ~= nil then
-                       user:set_hp(user:get_hp() + hp_change)
-                       itemstack:add_item(replace_with_item) -- note: replace_with_item is optional
-               end
-               return itemstack
-       end
-end
-
-function minetest.node_punch(pos, node, puncher, pointed_thing)
-       -- Run script hook
-       for _, callback in ipairs(minetest.registered_on_punchnodes) do
-               -- Copy pos and node because callback can modify them
-               local pos_copy = vector.new(pos)
-               local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
-               local pointed_thing_copy = pointed_thing and copy_pointed_thing(pointed_thing) or nil
-               callback(pos_copy, node_copy, puncher, pointed_thing_copy)
-       end
-end
-
-function minetest.handle_node_drops(pos, drops, digger)
-       -- Add dropped items to object's inventory
-       if digger:get_inventory() then
-               local _, dropped_item
-               for _, dropped_item in ipairs(drops) do
-                       local left = digger:get_inventory():add_item("main", dropped_item)
-                       if not left:is_empty() then
-                               local p = {
-                                       x = pos.x + math.random()/2-0.25,
-                                       y = pos.y + math.random()/2-0.25,
-                                       z = pos.z + math.random()/2-0.25,
-                               }
-                               minetest.add_item(p, left)
-                       end
-               end
-       end
-end
-
-function minetest.node_dig(pos, node, digger)
-       local def = ItemStack({name=node.name}):get_definition()
-       if not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then
-               minetest.log("info", digger:get_player_name() .. " tried to dig "
-                       .. node.name .. " which is not diggable "
-                       .. minetest.pos_to_string(pos))
-               return
-       end
-
-       if minetest.is_protected(pos, digger:get_player_name()) then
-               minetest.log("action", digger:get_player_name()
-                               .. " tried to dig " .. node.name
-                               .. " at protected position "
-                               .. minetest.pos_to_string(pos))
-               minetest.record_protection_violation(pos, digger:get_player_name())
-               return
-       end
-
-       minetest.log('action', digger:get_player_name() .. " digs "
-               .. node.name .. " at " .. minetest.pos_to_string(pos))
-
-       local wielded = digger:get_wielded_item()
-       local drops = minetest.get_node_drops(node.name, wielded:get_name())
-       
-       local wdef = wielded:get_definition()
-       local tp = wielded:get_tool_capabilities()
-       local dp = minetest.get_dig_params(def.groups, tp)
-       if wdef and wdef.after_use then
-               wielded = wdef.after_use(wielded, digger, node, dp) or wielded
-       else
-               -- Wear out tool
-               if not minetest.setting_getbool("creative_mode") then
-                       wielded:add_wear(dp.wear)
-               end
-       end
-       digger:set_wielded_item(wielded)
-       
-       -- Handle drops
-       minetest.handle_node_drops(pos, drops, digger)
-
-       local oldmetadata = nil
-       if def.after_dig_node then
-               oldmetadata = minetest.get_meta(pos):to_table()
-       end
-
-       -- Remove node and update
-       minetest.remove_node(pos)
-       
-       -- Run callback
-       if def.after_dig_node then
-               -- Copy pos and node because callback can modify them
-               local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
-               local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
-               def.after_dig_node(pos_copy, node_copy, oldmetadata, digger)
-       end
-
-       -- Run script hook
-       local _, callback
-       for _, callback in ipairs(minetest.registered_on_dignodes) do
-               -- Copy pos and node because callback can modify them
-               local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
-               local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
-               callback(pos_copy, node_copy, digger)
-       end
-end
-
--- This is used to allow mods to redefine minetest.item_place and so on
--- NOTE: This is not the preferred way. Preferred way is to provide enough
---       callbacks to not require redefining global functions. -celeron55
-local function redef_wrapper(table, name)
-       return function(...)
-               return table[name](...)
-       end
-end
-
---
--- Item definition defaults
---
-
-minetest.nodedef_default = {
-       -- Item properties
-       type="node",
-       -- name intentionally not defined here
-       description = "",
-       groups = {},
-       inventory_image = "",
-       wield_image = "",
-       wield_scale = {x=1,y=1,z=1},
-       stack_max = 99,
-       usable = false,
-       liquids_pointable = false,
-       tool_capabilities = nil,
-       node_placement_prediction = nil,
-
-       -- Interaction callbacks
-       on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
-       on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop
-       on_use = nil,
-       can_dig = nil,
-
-       on_punch = redef_wrapper(minetest, 'node_punch'), -- minetest.node_punch
-       on_rightclick = nil,
-       on_dig = redef_wrapper(minetest, 'node_dig'), -- minetest.node_dig
-
-       on_receive_fields = nil,
-       
-       on_metadata_inventory_move = minetest.node_metadata_inventory_move_allow_all,
-       on_metadata_inventory_offer = minetest.node_metadata_inventory_offer_allow_all,
-       on_metadata_inventory_take = minetest.node_metadata_inventory_take_allow_all,
-
-       -- Node properties
-       drawtype = "normal",
-       visual_scale = 1.0,
-       -- Don't define these because otherwise the old tile_images and
-       -- special_materials wouldn't be read
-       --tiles ={""},
-       --special_tiles = {
-       --      {name="", backface_culling=true},
-       --      {name="", backface_culling=true},
-       --},
-       alpha = 255,
-       post_effect_color = {a=0, r=0, g=0, b=0},
-       paramtype = "none",
-       paramtype2 = "none",
-       is_ground_content = true,
-       sunlight_propagates = false,
-       walkable = true,
-       pointable = true,
-       diggable = true,
-       climbable = false,
-       buildable_to = false,
-       liquidtype = "none",
-       liquid_alternative_flowing = "",
-       liquid_alternative_source = "",
-       liquid_viscosity = 0,
-       drowning = 0,
-       light_source = 0,
-       damage_per_second = 0,
-       selection_box = {type="regular"},
-       legacy_facedir_simple = false,
-       legacy_wallmounted = false,
-}
-
-minetest.craftitemdef_default = {
-       type="craft",
-       -- name intentionally not defined here
-       description = "",
-       groups = {},
-       inventory_image = "",
-       wield_image = "",
-       wield_scale = {x=1,y=1,z=1},
-       stack_max = 99,
-       liquids_pointable = false,
-       tool_capabilities = nil,
-
-       -- Interaction callbacks
-       on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
-       on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop
-       on_use = nil,
-}
-
-minetest.tooldef_default = {
-       type="tool",
-       -- name intentionally not defined here
-       description = "",
-       groups = {},
-       inventory_image = "",
-       wield_image = "",
-       wield_scale = {x=1,y=1,z=1},
-       stack_max = 1,
-       liquids_pointable = false,
-       tool_capabilities = nil,
-
-       -- Interaction callbacks
-       on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place
-       on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop
-       on_use = nil,
-}
-
-minetest.noneitemdef_default = {  -- This is used for the hand and unknown items
-       type="none",
-       -- name intentionally not defined here
-       description = "",
-       groups = {},
-       inventory_image = "",
-       wield_image = "",
-       wield_scale = {x=1,y=1,z=1},
-       stack_max = 99,
-       liquids_pointable = false,
-       tool_capabilities = nil,
-
-       -- Interaction callbacks
-       on_place = redef_wrapper(minetest, 'item_place'),
-       on_drop = nil,
-       on_use = nil,
-}
-
diff --git a/builtin/item_entity.lua b/builtin/item_entity.lua
deleted file mode 100644 (file)
index 0dcc2dc..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
--- Minetest: builtin/item_entity.lua
-
-function minetest.spawn_item(pos, item)
-       -- Take item in any format
-       local stack = ItemStack(item)
-       local obj = minetest.add_entity(pos, "__builtin:item")
-       obj:get_luaentity():set_item(stack:to_string())
-       return obj
-end
-
-minetest.register_entity("__builtin:item", {
-       initial_properties = {
-               hp_max = 1,
-               physical = true,
-               collide_with_objects = false,
-               collisionbox = {-0.17,-0.17,-0.17, 0.17,0.17,0.17},
-               visual = "sprite",
-               visual_size = {x=0.5, y=0.5},
-               textures = {""},
-               spritediv = {x=1, y=1},
-               initial_sprite_basepos = {x=0, y=0},
-               is_visible = false,
-       },
-       
-       itemstring = '',
-       physical_state = true,
-
-       set_item = function(self, itemstring)
-               self.itemstring = itemstring
-               local stack = ItemStack(itemstring)
-               local itemtable = stack:to_table()
-               local itemname = nil
-               if itemtable then
-                       itemname = stack:to_table().name
-               end
-               local item_texture = nil
-               local item_type = ""
-               if minetest.registered_items[itemname] then
-                       item_texture = minetest.registered_items[itemname].inventory_image
-                       item_type = minetest.registered_items[itemname].type
-               end
-               prop = {
-                       is_visible = true,
-                       visual = "sprite",
-                       textures = {"unknown_item.png"}
-               }
-               if item_texture and item_texture ~= "" then
-                       prop.visual = "sprite"
-                       prop.textures = {item_texture}
-                       prop.visual_size = {x=0.50, y=0.50}
-               else
-                       prop.visual = "wielditem"
-                       prop.textures = {itemname}
-                       prop.visual_size = {x=0.20, y=0.20}
-                       prop.automatic_rotate = math.pi * 0.25
-               end
-               self.object:set_properties(prop)
-       end,
-
-       get_staticdata = function(self)
-               --return self.itemstring
-               return minetest.serialize({
-                       itemstring = self.itemstring,
-                       always_collect = self.always_collect,
-               })
-       end,
-
-       on_activate = function(self, staticdata)
-               if string.sub(staticdata, 1, string.len("return")) == "return" then
-                       local data = minetest.deserialize(staticdata)
-                       if data and type(data) == "table" then
-                               self.itemstring = data.itemstring
-                               self.always_collect = data.always_collect
-                       end
-               else
-                       self.itemstring = staticdata
-               end
-               self.object:set_armor_groups({immortal=1})
-               self.object:setvelocity({x=0, y=2, z=0})
-               self.object:setacceleration({x=0, y=-10, z=0})
-               self:set_item(self.itemstring)
-       end,
-
-       on_step = function(self, dtime)
-               local p = self.object:getpos()
-               p.y = p.y - 0.3
-               local nn = minetest.get_node(p).name
-               -- If node is not registered or node is walkably solid and resting on nodebox
-               local v = self.object:getvelocity()
-               if not minetest.registered_nodes[nn] or minetest.registered_nodes[nn].walkable and v.y == 0 then
-                       if self.physical_state then
-                               self.object:setvelocity({x=0,y=0,z=0})
-                               self.object:setacceleration({x=0, y=0, z=0})
-                               self.physical_state = false
-                               self.object:set_properties({
-                                       physical = false
-                               })
-                       end
-               else
-                       if not self.physical_state then
-                               self.object:setvelocity({x=0,y=0,z=0})
-                               self.object:setacceleration({x=0, y=-10, z=0})
-                               self.physical_state = true
-                               self.object:set_properties({
-                                       physical = true
-                               })
-                       end
-               end
-       end,
-
-       on_punch = function(self, hitter)
-               if self.itemstring ~= '' then
-                       local left = hitter:get_inventory():add_item("main", self.itemstring)
-                       if not left:is_empty() then
-                               self.itemstring = left:to_string()
-                               return
-                       end
-               end
-               self.itemstring = ''
-               self.object:remove()
-       end,
-})
-
diff --git a/builtin/mainmenu.lua b/builtin/mainmenu.lua
deleted file mode 100644 (file)
index 67e0113..0000000
+++ /dev/null
@@ -1,1340 +0,0 @@
-print = engine.debug
-math.randomseed(os.time())
-os.setlocale("C", "numeric")
-
-local scriptpath = engine.get_scriptdir()
-
-mt_color_grey  = "#AAAAAA"
-mt_color_blue  = "#0000DD"
-mt_color_green = "#00DD00"
-mt_color_dark_green = "#003300"
-
---for all other colors ask sfan5 to complete his worK!
-
-dofile(scriptpath .. DIR_DELIM .. "misc_helpers.lua")
-dofile(scriptpath .. DIR_DELIM .. "filterlist.lua")
-dofile(scriptpath .. DIR_DELIM .. "modmgr.lua")
-dofile(scriptpath .. DIR_DELIM .. "modstore.lua")
-dofile(scriptpath .. DIR_DELIM .. "gamemgr.lua")
-dofile(scriptpath .. DIR_DELIM .. "mm_textures.lua")
-dofile(scriptpath .. DIR_DELIM .. "mm_menubar.lua")
-dofile(scriptpath .. DIR_DELIM .. "async_event.lua")
-
-menu = {}
-local tabbuilder = {}
-local worldlist = nil
-
---------------------------------------------------------------------------------
-local function filter_texture_pack_list(list)
-       retval = {"None"}
-       for _,i in ipairs(list) do
-               if i~="base" then
-                       table.insert(retval, i)
-               end
-       end
-       return retval
-end
-
---------------------------------------------------------------------------------
-function menu.render_favorite(spec,render_details)
-       local text = ""
-
-       if spec.name ~= nil then
-               text = text .. engine.formspec_escape(spec.name:trim())
-
---             if spec.description ~= nil and
---                     engine.formspec_escape(spec.description):trim() ~= "" then
---                     text = text .. " (" .. engine.formspec_escape(spec.description) .. ")"
---             end
-       else
-               if spec.address ~= nil then
-                       text = text .. spec.address:trim()
-
-                       if spec.port ~= nil then
-                               text = text .. ":" .. spec.port
-                       end
-               end
-       end
-
-       if not render_details then
-               return text
-       end
-
-       local details = ""
-       if spec.password == true then
-               details = details .. "*"
-       else
-               details = details .. "_"
-       end
-
-       if spec.creative then
-               details = details .. "C"
-       else
-               details = details .. "_"
-       end
-
-       if spec.damage then
-               details = details .. "D"
-       else
-               details = details .. "_"
-       end
-
-       if spec.pvp then
-               details = details .. "P"
-       else
-               details = details .. "_"
-       end
-       details = details .. " "
-
-       local playercount = ""
-
-       if spec.clients ~= nil and
-               spec.clients_max ~= nil then
-               playercount = string.format("%03d",spec.clients) .. "/" ..
-                                               string.format("%03d",spec.clients_max) .. " "
-       end
-
-       return playercount .. engine.formspec_escape(details) ..  text
-end
-
---------------------------------------------------------------------------------
-os.tempfolder = function()
-       local filetocheck = os.tmpname()
-       os.remove(filetocheck)
-
-       local randname = "MTTempModFolder_" .. math.random(0,10000)
-       if DIR_DELIM == "\\" then
-               local tempfolder = os.getenv("TEMP")
-               return tempfolder .. filetocheck
-       else
-               local backstring = filetocheck:reverse()
-               return filetocheck:sub(0,filetocheck:len()-backstring:find(DIR_DELIM)+1) ..randname
-       end
-
-end
-
---------------------------------------------------------------------------------
-function text2textlist(xpos,ypos,width,height,tl_name,textlen,text,transparency)
-       local textlines = engine.splittext(text,textlen)
-       
-       local retval = "textlist[" .. xpos .. "," .. ypos .. ";"
-                                                               .. width .. "," .. height .. ";"
-                                                               .. tl_name .. ";"
-       
-       for i=1, #textlines, 1 do
-               textlines[i] = textlines[i]:gsub("\r","")
-               retval = retval .. engine.formspec_escape(textlines[i]) .. ","
-       end
-       
-       retval = retval .. ";0;"
-       
-       if transparency then
-               retval = retval .. "true"
-       end
-       
-       retval = retval .. "]"
-
-       return retval
-end
-
---------------------------------------------------------------------------------
-function init_globals()
-       --init gamedata
-       gamedata.worldindex = 0
-
-       worldlist = filterlist.create(
-                                       engine.get_worlds,
-                                       compare_worlds,
-                                       function(element,uid)
-                                               if element.name == uid then
-                                                       return true
-                                               end
-                                               return false
-                                       end, --unique id compare fct
-                                       function(element,gameid)
-                                               if element.gameid == gameid then
-                                                       return true
-                                               end
-                                               return false
-                                       end --filter fct
-                                       )
-
-       filterlist.add_sort_mechanism(worldlist,"alphabetic",sort_worlds_alphabetic)
-       filterlist.set_sortmode(worldlist,"alphabetic")
-end
-
---------------------------------------------------------------------------------
-function update_menu()
-
-       local formspec
-
-       -- handle errors
-       if gamedata.errormessage ~= nil then
-               formspec = "size[12,5.2,true]" ..
-                       "textarea[1,2;10,2;;ERROR: " ..
-                       engine.formspec_escape(gamedata.errormessage) ..
-                       ";]"..
-                       "button[4.5,4.2;3,0.5;btn_error_confirm;" .. fgettext("Ok") .. "]"
-       else
-               formspec = tabbuilder.gettab()
-       end
-
-       engine.update_formspec(formspec)
-end
-
---------------------------------------------------------------------------------
-function menu.render_world_list()
-       local retval = ""
-
-       local current_worldlist = filterlist.get_list(worldlist)
-
-       for i,v in ipairs(current_worldlist) do
-               if retval ~= "" then
-                       retval = retval ..","
-               end
-
-               retval = retval .. engine.formspec_escape(v.name) ..
-                                       " \\[" .. engine.formspec_escape(v.gameid) .. "\\]"
-       end
-
-       return retval
-end
-
---------------------------------------------------------------------------------
-function menu.render_texture_pack_list(list)
-       local retval = ""
-
-       for i, v in ipairs(list) do
-               if retval ~= "" then
-                       retval = retval ..","
-               end
-
-               retval = retval .. engine.formspec_escape(v)
-       end
-
-       return retval
-end
-
---------------------------------------------------------------------------------
-function menu.asyncOnlineFavourites()
-       menu.favorites = {}
-       engine.handle_async(
-               function(param)
-                       return engine.get_favorites("online")
-               end,
-               nil,
-               function(result)
-                       menu.favorites = result
-                       engine.event_handler("Refresh")
-               end
-               )
-end
-
---------------------------------------------------------------------------------
-function menu.init()
-       --init menu data
-       gamemgr.update_gamelist()
-
-       menu.last_game  = tonumber(engine.setting_get("main_menu_last_game_idx"))
-
-       if type(menu.last_game) ~= "number" then
-               menu.last_game = 1
-       end
-
-       if engine.setting_getbool("public_serverlist") then
-               menu.asyncOnlineFavourites()
-       else
-               menu.favorites = engine.get_favorites("local")
-       end
-
-       menu.defaulttexturedir = engine.get_texturepath_share() .. DIR_DELIM .. "base" ..
-                                       DIR_DELIM .. "pack" .. DIR_DELIM
-end
-
---------------------------------------------------------------------------------
-function menu.lastgame()
-       if menu.last_game > 0 and menu.last_game <= #gamemgr.games then
-               return gamemgr.games[menu.last_game]
-       end
-
-       if #gamemgr.games >= 1 then
-               menu.last_game = 1
-               return gamemgr.games[menu.last_game]
-       end
-
-       --error case!!
-       return nil
-end
-
---------------------------------------------------------------------------------
-function menu.update_last_game()
-
-       local current_world = filterlist.get_raw_element(worldlist,
-                                                       engine.setting_get("mainmenu_last_selected_world")
-                                                       )
-
-       if current_world == nil then
-               return
-       end
-
-       local gamespec, i = gamemgr.find_by_gameid(current_world.gameid)
-       if i ~= nil then
-               menu.last_game = i
-               engine.setting_set("main_menu_last_game_idx",menu.last_game)
-       end
-end
-
---------------------------------------------------------------------------------
-function menu.handle_key_up_down(fields,textlist,settingname)
-
-       if fields["key_up"] then
-               local oldidx = engine.get_textlist_index(textlist)
-
-               if oldidx ~= nil and oldidx > 1 then
-                       local newidx = oldidx -1
-                       engine.setting_set(settingname,
-                               filterlist.get_raw_index(worldlist,newidx))
-               end
-       end
-
-       if fields["key_down"] then
-               local oldidx = engine.get_textlist_index(textlist)
-
-               if oldidx ~= nil and oldidx < filterlist.size(worldlist) then
-                       local newidx = oldidx + 1
-                       engine.setting_set(settingname,
-                               filterlist.get_raw_index(worldlist,newidx))
-               end
-       end
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.dialog_create_world()
-       local mapgens = {"v6", "v7", "indev", "singlenode", "math"}
-
-       local current_seed = engine.setting_get("fixed_map_seed") or ""
-       local current_mg   = engine.setting_get("mg_name")
-
-       local mglist = ""
-       local selindex = 1
-       local i = 1
-       for k,v in pairs(mapgens) do
-               if current_mg == v then
-                       selindex = i
-               end
-               i = i + 1
-               mglist = mglist .. v .. ","
-       end
-       mglist = mglist:sub(1, -2)
-
-       local retval =
-               "label[2,0;" .. fgettext("World name") .. "]"..
-               "field[4.5,0.4;6,0.5;te_world_name;;]" ..
-
-               "label[2,1;" .. fgettext("Seed") .. "]"..
-               "field[4.5,1.4;6,0.5;te_seed;;".. current_seed .. "]" ..
-
-               "label[2,2;" .. fgettext("Mapgen") .. "]"..
-               "dropdown[4.2,2;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]" ..
-
-               "label[2,3;" .. fgettext("Game") .. "]"..
-               "textlist[4.2,3;5.8,2.3;games;" .. gamemgr.gamelist() ..
-               ";" .. menu.last_game .. ";true]" ..
-
-               "button[5,5.5;2.6,0.5;world_create_confirm;" .. fgettext("Create") .. "]" ..
-               "button[7.5,5.5;2.8,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"
-
-       return retval
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.dialog_delete_world()
-       return  "label[2,2;" ..
-                       fgettext("Delete World \"$1\"?", filterlist.get_raw_list(worldlist)[menu.world_to_del].name) .. "]"..
-                       "button[3.5,4.2;2.6,0.5;world_delete_confirm;" .. fgettext("Yes").. "]" ..
-                       "button[6,4.2;2.8,0.5;world_delete_cancel;" .. fgettext("No") .. "]"
-end
-
---------------------------------------------------------------------------------
-
-function tabbuilder.gettab()
-       local tsize = tabbuilder.tabsizes[tabbuilder.current_tab] or {width=12, height=5.2}
-       local retval = "size[" .. tsize.width .. "," .. tsize.height .. ",true]"
-
-       if tabbuilder.show_buttons then
-               retval = retval .. tabbuilder.tab_header()
-       end
-
-       local buildfunc = tabbuilder.tabfuncs[tabbuilder.current_tab]
-       if buildfunc ~= nil then
-               retval = retval .. buildfunc()
-       end
-
-       retval = retval .. modmgr.gettab(tabbuilder.current_tab)
-       retval = retval .. gamemgr.gettab(tabbuilder.current_tab)
-       retval = retval .. modstore.gettab(tabbuilder.current_tab)
-
-       return retval
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.handle_create_world_buttons(fields)
-
-       if fields["world_create_confirm"] or
-               fields["key_enter"] then
-
-               local worldname = fields["te_world_name"]
-               local gameindex = engine.get_textlist_index("games")
-
-               if gameindex ~= nil and
-                       worldname ~= "" then
-
-                       local message = nil
-
-                       if not filterlist.uid_exists_raw(worldlist,worldname) then
-                               engine.setting_set("mg_name",fields["dd_mapgen"])
-                               message = engine.create_world(worldname,gameindex)
-                       else
-                               message = fgettext("A world named \"$1\" already exists", worldname)
-                       end
-
-                       engine.setting_set("fixed_map_seed", fields["te_seed"])
-
-                       if message ~= nil then
-                               gamedata.errormessage = message
-                       else
-                               menu.last_game = gameindex
-                               engine.setting_set("main_menu_last_game_idx",gameindex)
-
-                               filterlist.refresh(worldlist)
-                               engine.setting_set("mainmenu_last_selected_world",
-                                                                       filterlist.raw_index_by_uid(worldlist,worldname))
-                       end
-               else
-                       gamedata.errormessage =
-                               fgettext("No worldname given or no game selected")
-               end
-       end
-
-       if fields["games"] then
-               tabbuilder.skipformupdate = true
-               return
-       end
-
-       --close dialog
-       tabbuilder.is_dialog = false
-       tabbuilder.show_buttons = true
-       tabbuilder.current_tab = engine.setting_get("main_menu_tab")
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.handle_delete_world_buttons(fields)
-
-       if fields["world_delete_confirm"] then
-               if menu.world_to_del > 0 and
-                       menu.world_to_del <= #filterlist.get_raw_list(worldlist) then
-                       engine.delete_world(menu.world_to_del)
-                       menu.world_to_del = 0
-                       filterlist.refresh(worldlist)
-               end
-       end
-
-       tabbuilder.is_dialog = false
-       tabbuilder.show_buttons = true
-       tabbuilder.current_tab = engine.setting_get("main_menu_tab")
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.handle_multiplayer_buttons(fields)
-
-       if fields["te_name"] ~= nil then
-               gamedata.playername = fields["te_name"]
-               engine.setting_set("name", fields["te_name"])
-       end
-
-       if fields["favourites"] ~= nil then
-               local event = engine.explode_textlist_event(fields["favourites"])
-               if event.type == "DCL" then
-                       if event.index <= #menu.favorites then
-                               gamedata.address = menu.favorites[event.index].address
-                               gamedata.port = menu.favorites[event.index].port
-                               gamedata.playername             = fields["te_name"]
-                               if fields["te_pwd"] ~= nil then
-                                       gamedata.password               = fields["te_pwd"]
-                               end
-                               gamedata.selected_world = 0
-
-                               if menu.favorites ~= nil then
-                                       gamedata.servername = menu.favorites[event.index].name
-                                       gamedata.serverdescription = menu.favorites[event.index].description
-                               end
-
-                               if gamedata.address ~= nil and
-                                       gamedata.port ~= nil then
-                                       engine.setting_set("address",gamedata.address)
-                                       engine.setting_set("remote_port",gamedata.port)
-                                       engine.start()
-                               end
-                       end
-               end
-
-               if event.type == "CHG" then
-                       if event.index <= #menu.favorites then
-                               local address = menu.favorites[event.index].address
-                               local port = menu.favorites[event.index].port
-
-                               if address ~= nil and
-                                       port ~= nil then
-                                       engine.setting_set("address",address)
-                                       engine.setting_set("remote_port",port)
-                               end
-
-                               menu.fav_selected = event.index
-                       end
-               end
-               return
-       end
-
-       if fields["key_up"] ~= nil or
-               fields["key_down"] ~= nil then
-
-               local fav_idx = engine.get_textlist_index("favourites")
-
-               if fav_idx ~= nil then
-                       if fields["key_up"] ~= nil and fav_idx > 1 then
-                               fav_idx = fav_idx -1
-                       else if fields["key_down"] and fav_idx < #menu.favorites then
-                               fav_idx = fav_idx +1
-                       end end
-               end
-
-               local address = menu.favorites[fav_idx].address
-               local port = menu.favorites[fav_idx].port
-
-               if address ~= nil and
-                       port ~= nil then
-                       engine.setting_set("address",address)
-                       engine.setting_set("remote_port",port)
-               end
-
-               menu.fav_selected = fav_idx
-               return
-       end
-
-       if fields["cb_public_serverlist"] ~= nil then
-               engine.setting_set("public_serverlist", fields["cb_public_serverlist"])
-
-               if engine.setting_getbool("public_serverlist") then
-                       menu.asyncOnlineFavourites()
-               else
-                       menu.favorites = engine.get_favorites("local")
-               end
-               menu.fav_selected = nil
-               return
-       end
-
-       if fields["btn_delete_favorite"] ~= nil then
-               local current_favourite = engine.get_textlist_index("favourites")
-               if current_favourite == nil then return end
-               engine.delete_favorite(current_favourite)
-               menu.favorites = engine.get_favorites()
-               menu.fav_selected = nil
-
-               engine.setting_set("address","")
-               engine.setting_set("remote_port","30000")
-
-               return
-       end
-
-       if fields["btn_mp_connect"] ~= nil or
-               fields["key_enter"] ~= nil then
-
-               gamedata.playername             = fields["te_name"]
-               gamedata.password               = fields["te_pwd"]
-               gamedata.address                = fields["te_address"]
-               gamedata.port                   = fields["te_port"]
-
-               local fav_idx = engine.get_textlist_index("favourites")
-
-               if fav_idx ~= nil and fav_idx <= #menu.favorites and
-                       menu.favorites[fav_idx].address == fields["te_address"] and
-                       menu.favorites[fav_idx].port    == fields["te_port"] then
-
-                       gamedata.servername                     = menu.favorites[fav_idx].name
-                       gamedata.serverdescription      = menu.favorites[fav_idx].description
-               else
-                       gamedata.servername                     = ""
-                       gamedata.serverdescription      = ""
-               end
-
-               gamedata.selected_world = 0
-
-               engine.setting_set("address",fields["te_address"])
-               engine.setting_set("remote_port",fields["te_port"])
-
-               engine.start()
-               return
-       end
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.handle_server_buttons(fields)
-
-       local world_doubleclick = false
-
-       if fields["srv_worlds"] ~= nil then
-               local event = engine.explode_textlist_event(fields["srv_worlds"])
-
-               if event.type == "DCL" then
-                       world_doubleclick = true
-               end
-               if event.type == "CHG" then
-                       engine.setting_set("mainmenu_last_selected_world",
-                               filterlist.get_raw_index(worldlist,engine.get_textlist_index("srv_worlds")))
-               end
-       end
-
-       menu.handle_key_up_down(fields,"srv_worlds","mainmenu_last_selected_world")
-
-       if fields["cb_creative_mode"] then
-               engine.setting_set("creative_mode", fields["cb_creative_mode"])
-       end
-
-       if fields["cb_enable_damage"] then
-               engine.setting_set("enable_damage", fields["cb_enable_damage"])
-       end
-
-       if fields["cb_server_announce"] then
-               engine.setting_set("server_announce", fields["cb_server_announce"])
-       end
-
-       if fields["start_server"] ~= nil or
-               world_doubleclick or
-               fields["key_enter"] then
-               local selected = engine.get_textlist_index("srv_worlds")
-               if selected ~= nil then
-                       gamedata.playername             = fields["te_playername"]
-                       gamedata.password               = fields["te_passwd"]
-                       gamedata.port                   = fields["te_serverport"]
-                       gamedata.address                = ""
-                       gamedata.selected_world = filterlist.get_raw_index(worldlist,selected)
-
-                       engine.setting_set("port",gamedata.port)
-                       if fields["te_serveraddr"] ~= nil then
-                               engine.setting_set("bind_address",fields["te_serveraddr"])
-                       end
-
-                       menu.update_last_game(gamedata.selected_world)
-                       engine.start()
-               end
-       end
-
-       if fields["world_create"] ~= nil then
-               tabbuilder.current_tab = "dialog_create_world"
-               tabbuilder.is_dialog = true
-               tabbuilder.show_buttons = false
-       end
-
-       if fields["world_delete"] ~= nil then
-               local selected = engine.get_textlist_index("srv_worlds")
-               if selected ~= nil and
-                       selected <= filterlist.size(worldlist) then
-                       local world = filterlist.get_list(worldlist)[selected]
-                       if world ~= nil and
-                               world.name ~= nil and
-                               world.name ~= "" then
-                               menu.world_to_del = filterlist.get_raw_index(worldlist,selected)
-                               tabbuilder.current_tab = "dialog_delete_world"
-                               tabbuilder.is_dialog = true
-                               tabbuilder.show_buttons = false
-                       else
-                               menu.world_to_del = 0
-                       end
-               end
-       end
-
-       if fields["world_configure"] ~= nil then
-               selected = engine.get_textlist_index("srv_worlds")
-               if selected ~= nil then
-                       modmgr.world_config_selected_world = filterlist.get_raw_index(worldlist,selected)
-                       if modmgr.init_worldconfig() then
-                               tabbuilder.current_tab = "dialog_configure_world"
-                               tabbuilder.is_dialog = true
-                               tabbuilder.show_buttons = false
-                       end
-               end
-       end
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.handle_settings_buttons(fields)
-       if fields["cb_fancy_trees"] then
-               engine.setting_set("new_style_leaves", fields["cb_fancy_trees"])
-       end
-       if fields["cb_smooth_lighting"] then
-               engine.setting_set("smooth_lighting", fields["cb_smooth_lighting"])
-       end
-       if fields["cb_3d_clouds"] then
-               engine.setting_set("enable_3d_clouds", fields["cb_3d_clouds"])
-       end
-       if fields["cb_opaque_water"] then
-               engine.setting_set("opaque_water", fields["cb_opaque_water"])
-       end
-
-       if fields["cb_mipmapping"] then
-               engine.setting_set("mip_map", fields["cb_mipmapping"])
-       end
-       if fields["cb_anisotrophic"] then
-               engine.setting_set("anisotropic_filter", fields["cb_anisotrophic"])
-       end
-       if fields["cb_bilinear"] then
-               engine.setting_set("bilinear_filter", fields["cb_bilinear"])
-       end
-       if fields["cb_trilinear"] then
-               engine.setting_set("trilinear_filter", fields["cb_trilinear"])
-       end
-
-       if fields["cb_shaders"] then
-               if (engine.setting_get("video_driver") == "direct3d8" or engine.setting_get("video_driver") == "direct3d9") then
-                       engine.setting_set("enable_shaders", "false")
-                       gamedata.errormessage = fgettext("To enable shaders the OpenGL driver needs to be used.")
-               else
-                       engine.setting_set("enable_shaders", fields["cb_shaders"])
-               end
-       end
-       if fields["cb_pre_ivis"] then
-               engine.setting_set("preload_item_visuals", fields["cb_pre_ivis"])
-       end
-       if fields["cb_particles"] then
-               engine.setting_set("enable_particles", fields["cb_particles"])
-       end
-       if fields["cb_bumpmapping"] then
-               engine.setting_set("enable_bumpmapping", fields["cb_bumpmapping"])
-       end
-       if fields["cb_parallax"] then
-               engine.setting_set("enable_parallax_occlusion", fields["cb_parallax"])
-       end
-       if fields["cb_generate_normalmaps"] then
-               engine.setting_set("generate_normalmaps", fields["cb_generate_normalmaps"])
-       end
-       if fields["cb_waving_water"] then
-               engine.setting_set("enable_waving_water", fields["cb_waving_water"])
-       end
-       if fields["cb_waving_leaves"] then
-               engine.setting_set("enable_waving_leaves", fields["cb_waving_leaves"])
-       end
-       if fields["cb_waving_plants"] then
-               engine.setting_set("enable_waving_plants", fields["cb_waving_plants"])
-       end
-       if fields["btn_change_keys"] ~= nil then
-               engine.show_keys_menu()
-       end
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.handle_singleplayer_buttons(fields)
-
-       local world_doubleclick = false
-
-       if fields["sp_worlds"] ~= nil then
-               local event = engine.explode_textlist_event(fields["sp_worlds"])
-
-               if event.type == "DCL" then
-                       world_doubleclick = true
-               end
-
-               if event.type == "CHG" then
-                       engine.setting_set("mainmenu_last_selected_world",
-                               filterlist.get_raw_index(worldlist,engine.get_textlist_index("sp_worlds")))
-               end
-       end
-
-       menu.handle_key_up_down(fields,"sp_worlds","mainmenu_last_selected_world")
-
-       if fields["cb_creative_mode"] then
-               engine.setting_set("creative_mode", fields["cb_creative_mode"])
-       end
-
-       if fields["cb_enable_damage"] then
-               engine.setting_set("enable_damage", fields["cb_enable_damage"])
-       end
-
-       if fields["play"] ~= nil or
-               world_doubleclick or
-               fields["key_enter"] then
-               local selected = engine.get_textlist_index("sp_worlds")
-               if selected ~= nil then
-                       gamedata.selected_world = filterlist.get_raw_index(worldlist,selected)
-                       gamedata.singleplayer   = true
-
-                       menu.update_last_game(gamedata.selected_world)
-
-                       engine.start()
-               end
-       end
-
-       if fields["world_create"] ~= nil then
-               tabbuilder.current_tab = "dialog_create_world"
-               tabbuilder.is_dialog = true
-               tabbuilder.show_buttons = false
-       end
-
-       if fields["world_delete"] ~= nil then
-               local selected = engine.get_textlist_index("sp_worlds")
-               if selected ~= nil and
-                       selected <= filterlist.size(worldlist) then
-                       local world = filterlist.get_list(worldlist)[selected]
-                       if world ~= nil and
-                               world.name ~= nil and
-                               world.name ~= "" then
-                               menu.world_to_del = filterlist.get_raw_index(worldlist,selected)
-                               tabbuilder.current_tab = "dialog_delete_world"
-                               tabbuilder.is_dialog = true
-                               tabbuilder.show_buttons = false
-                       else
-                               menu.world_to_del = 0
-                       end
-               end
-       end
-
-       if fields["world_configure"] ~= nil then
-               selected = engine.get_textlist_index("sp_worlds")
-               if selected ~= nil then
-                       modmgr.world_config_selected_world = filterlist.get_raw_index(worldlist,selected)
-                       if modmgr.init_worldconfig() then
-                               tabbuilder.current_tab = "dialog_configure_world"
-                               tabbuilder.is_dialog = true
-                               tabbuilder.show_buttons = false
-                       end
-               end
-       end
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.handle_texture_pack_buttons(fields)
-       if fields["TPs"] ~= nil then
-               local event = engine.explode_textlist_event(fields["TPs"])
-               if event.type == "CHG" or event.type == "DCL" then
-                       local index = engine.get_textlist_index("TPs")
-                       engine.setting_set("mainmenu_last_selected_TP",
-                               index)
-                       local list = filter_texture_pack_list(engine.get_dirlist(engine.get_texturepath(), true))
-                       local current_index = engine.get_textlist_index("TPs")
-                       if current_index ~= nil and #list >= current_index then
-                               local new_path = engine.get_texturepath()..DIR_DELIM..list[current_index]
-                               if list[current_index] == "None" then new_path = "" end
-
-                               engine.setting_set("texture_path", new_path)
-                       end
-               end
-       end
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.tab_header()
-
-       if tabbuilder.last_tab_index == nil then
-               tabbuilder.last_tab_index = 1
-       end
-
-       local toadd = ""
-
-       for i=1,#tabbuilder.current_buttons,1 do
-
-               if toadd ~= "" then
-                       toadd = toadd .. ","
-               end
-
-               toadd = toadd .. tabbuilder.current_buttons[i].caption
-       end
-       return "tabheader[-0.3,-0.99;main_tab;" .. toadd ..";" .. tabbuilder.last_tab_index .. ";true;false]"
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.handle_tab_buttons(fields)
-
-       if fields["main_tab"] then
-               local index = tonumber(fields["main_tab"])
-               tabbuilder.last_tab_index = index
-               tabbuilder.current_tab = tabbuilder.current_buttons[index].name
-
-               engine.setting_set("main_menu_tab",tabbuilder.current_tab)
-       end
-
-       --handle tab changes
-       if tabbuilder.current_tab ~= tabbuilder.old_tab then
-               if tabbuilder.current_tab ~= "singleplayer" and not tabbuilder.is_dialog then
-                       menu.update_gametype(true)
-               end
-       end
-
-       if tabbuilder.current_tab == "singleplayer" then
-               menu.update_gametype()
-       end
-
-       tabbuilder.old_tab = tabbuilder.current_tab
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.tab_multiplayer()
-
-       local retval =
-               "vertlabel[0,-0.25;".. fgettext("CLIENT") .. "]" ..
-               "label[1,-0.25;".. fgettext("Favorites:") .. "]"..
-               "label[1,4.25;".. fgettext("Address/Port") .. "]"..
-               "label[9,2.75;".. fgettext("Name/Password") .. "]" ..
-               "field[1.25,5.25;5.5,0.5;te_address;;" ..engine.setting_get("address") .."]" ..
-               "field[6.75,5.25;2.25,0.5;te_port;;" ..engine.setting_get("remote_port") .."]" ..
-               "checkbox[1,3.6;cb_public_serverlist;".. fgettext("Public Serverlist") .. ";" ..
-               dump(engine.setting_getbool("public_serverlist")) .. "]"
-
-       if not engine.setting_getbool("public_serverlist") then
-               retval = retval ..
-               "button[6.45,3.95;2.25,0.5;btn_delete_favorite;".. fgettext("Delete") .. "]"
-       end
-
-       retval = retval ..
-               "button[9,4.95;2.5,0.5;btn_mp_connect;".. fgettext("Connect") .. "]" ..
-               "field[9.3,3.75;2.5,0.5;te_name;;" ..engine.setting_get("name") .."]" ..
-               "pwdfield[9.3,4.5;2.5,0.5;te_pwd;]" ..
-               "textarea[9.3,0.25;2.5,2.75;;"
-       if menu.fav_selected ~= nil and
-               menu.favorites[menu.fav_selected].description ~= nil then
-               retval = retval ..
-                       engine.formspec_escape(menu.favorites[menu.fav_selected].description,true)
-       end
-
-       retval = retval ..
-               ";]" ..
-               "textlist[1,0.35;7.5,3.35;favourites;"
-
-       local render_details = engine.setting_getbool("public_serverlist")
-
-       if #menu.favorites > 0 then
-               retval = retval .. menu.render_favorite(menu.favorites[1],render_details)
-
-               for i=2,#menu.favorites,1 do
-                       retval = retval .. "," .. menu.render_favorite(menu.favorites[i],render_details)
-               end
-       end
-
-       if menu.fav_selected ~= nil then
-               retval = retval .. ";" .. menu.fav_selected .. "]"
-       else
-               retval = retval .. ";0]"
-       end
-
-       return retval
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.tab_server()
-
-       local index = filterlist.get_current_index(worldlist,
-                               tonumber(engine.setting_get("mainmenu_last_selected_world"))
-                               )
-
-       local retval =
-               "button[4,4.15;2.6,0.5;world_delete;".. fgettext("Delete") .. "]" ..
-               "button[6.5,4.15;2.8,0.5;world_create;".. fgettext("New") .. "]" ..
-               "button[9.2,4.15;2.55,0.5;world_configure;".. fgettext("Configure") .. "]" ..
-               "button[8.5,4.9;3.25,0.5;start_server;".. fgettext("Start Game") .. "]" ..
-               "label[4,-0.25;".. fgettext("Select World:") .. "]"..
-               "vertlabel[0,-0.25;".. fgettext("START SERVER") .. "]" ..
-               "checkbox[0.5,0.25;cb_creative_mode;".. fgettext("Creative Mode") .. ";" ..
-               dump(engine.setting_getbool("creative_mode")) .. "]"..
-               "checkbox[0.5,0.7;cb_enable_damage;".. fgettext("Enable Damage") .. ";" ..
-               dump(engine.setting_getbool("enable_damage")) .. "]"..
-               "checkbox[0.5,1.15;cb_server_announce;".. fgettext("Public") .. ";" ..
-               dump(engine.setting_getbool("server_announce")) .. "]"..
-               "field[0.8,3.2;3.5,0.5;te_playername;".. fgettext("Name") .. ";" ..
-               engine.setting_get("name") .. "]" ..
-               "pwdfield[0.8,4.2;3.5,0.5;te_passwd;".. fgettext("Password") .. "]"
-               
-       local bind_addr = engine.setting_get("bind_address")
-       if bind_addr ~= nil and bind_addr ~= "" then
-               retval = retval ..
-                       "field[0.8,5.2;2.25,0.5;te_serveraddr;".. fgettext("Bind Address") .. ";" ..
-                       engine.setting_get("bind_address") .."]" ..
-                       "field[3.05,5.2;1.25,0.5;te_serverport;".. fgettext("Port") .. ";" ..
-                       engine.setting_get("port") .."]"
-       else
-               retval = retval ..
-                       "field[0.8,5.2;3.5,0.5;te_serverport;".. fgettext("Server Port") .. ";" ..
-                       engine.setting_get("port") .."]"
-       end
-       
-       retval = retval ..
-               "textlist[4,0.25;7.5,3.7;srv_worlds;" ..
-               menu.render_world_list() ..
-               ";" .. index .. "]"
-
-       return retval
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.tab_settings()
-       tab_string =
-                       "vertlabel[0,0;" .. fgettext("SETTINGS") .. "]" ..
-                       "checkbox[1,0;cb_fancy_trees;".. fgettext("Fancy Trees") .. ";"
-                                       .. dump(engine.setting_getbool("new_style_leaves")) .. "]"..
-                       "checkbox[1,0.5;cb_smooth_lighting;".. fgettext("Smooth Lighting")
-                                       .. ";".. dump(engine.setting_getbool("smooth_lighting")) .. "]"..
-                       "checkbox[1,1;cb_3d_clouds;".. fgettext("3D Clouds") .. ";"
-                                       .. dump(engine.setting_getbool("enable_3d_clouds")) .. "]"..
-                       "checkbox[1,1.5;cb_opaque_water;".. fgettext("Opaque Water") .. ";"
-                                       .. dump(engine.setting_getbool("opaque_water")) .. "]"..
-                       "checkbox[1,2.0;cb_pre_ivis;".. fgettext("Preload item visuals") .. ";"
-                                       .. dump(engine.setting_getbool("preload_item_visuals")) .. "]"..
-                       "checkbox[1,2.5;cb_particles;".. fgettext("Enable Particles") .. ";"
-                                       .. dump(engine.setting_getbool("enable_particles"))     .. "]"..
-                       "checkbox[4.5,0;cb_mipmapping;".. fgettext("Mip-Mapping") .. ";"
-                                       .. dump(engine.setting_getbool("mip_map")) .. "]"..
-                       "checkbox[4.5,0.5;cb_anisotrophic;".. fgettext("Anisotropic Filtering") .. ";"
-                                       .. dump(engine.setting_getbool("anisotropic_filter")) .. "]"..
-                       "checkbox[4.5,1.0;cb_bilinear;".. fgettext("Bi-Linear Filtering") .. ";"
-                                       .. dump(engine.setting_getbool("bilinear_filter")) .. "]"..
-                       "checkbox[4.5,1.5;cb_trilinear;".. fgettext("Tri-Linear Filtering") .. ";"
-                                       .. dump(engine.setting_getbool("trilinear_filter")) .. "]"..
-
-                       "checkbox[8,0;cb_shaders;".. fgettext("Shaders") .. ";"
-                                       .. dump(engine.setting_getbool("enable_shaders")) .. "]"..
-                       "button[1,4.5;2.25,0.5;btn_change_keys;".. fgettext("Change keys") .. "]"
-
-       if engine.setting_getbool("enable_shaders") then
-               tab_string = tab_string ..
-                       "checkbox[8,0.5;cb_bumpmapping;".. fgettext("Bumpmapping") .. ";"
-                                       .. dump(engine.setting_getbool("enable_bumpmapping")) .. "]"..
-                       "checkbox[8,1.0;cb_parallax;".. fgettext("Parallax Occlusion") .. ";"
-                                       .. dump(engine.setting_getbool("enable_parallax_occlusion")) .. "]"..
-                       "checkbox[8,1.5;cb_generate_normalmaps;".. fgettext("Generate Normalmaps") .. ";"
-                                       .. dump(engine.setting_getbool("generate_normalmaps")) .. "]"..
-                       "checkbox[8,2.0;cb_waving_water;".. fgettext("Waving Water") .. ";"
-                                       .. dump(engine.setting_getbool("enable_waving_water")) .. "]"..
-                       "checkbox[8,2.5;cb_waving_leaves;".. fgettext("Waving Leaves") .. ";"
-                                       .. dump(engine.setting_getbool("enable_waving_leaves")) .. "]"..
-                       "checkbox[8,3.0;cb_waving_plants;".. fgettext("Waving Plants") .. ";"
-                                       .. dump(engine.setting_getbool("enable_waving_plants")) .. "]"
-       else
-               tab_string = tab_string ..
-                       "textlist[8.33,0.7;4,1;;#888888" .. fgettext("Bumpmapping") .. ";0;true]" ..
-                       "textlist[8.33,1.2;4,1;;#888888" .. fgettext("Parallax Occlusion") .. ";0;true]" ..
-                       "textlist[8.33,1.7;4,1;;#888888" .. fgettext("Generate Normalmaps") .. ";0;true]" ..
-                       "textlist[8.33,2.2;4,1;;#888888" .. fgettext("Waving Water") .. ";0;true]" ..
-                       "textlist[8.33,2.7;4,1;;#888888" .. fgettext("Waving Leaves") .. ";0;true]" ..
-                       "textlist[8.33,3.2;4,1;;#888888" .. fgettext("Waving Plants") .. ";0;true]"
-       end
-       return tab_string
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.tab_singleplayer()
-
-       local index = filterlist.get_current_index(worldlist,
-                               tonumber(engine.setting_get("mainmenu_last_selected_world"))
-                               )
-
-       return  "button[4,4.15;2.6,0.5;world_delete;".. fgettext("Delete") .. "]" ..
-                       "button[6.5,4.15;2.8,0.5;world_create;".. fgettext("New") .. "]" ..
-                       "button[9.2,4.15;2.55,0.5;world_configure;".. fgettext("Configure") .. "]" ..
-                       "button[8.5,4.95;3.25,0.5;play;".. fgettext("Play") .. "]" ..
-                       "label[4,-0.25;".. fgettext("Select World:") .. "]"..
-                       "vertlabel[0,-0.25;".. fgettext("SINGLE PLAYER") .. "]" ..
-                       "checkbox[0.5,0.25;cb_creative_mode;".. fgettext("Creative Mode") .. ";" ..
-                       dump(engine.setting_getbool("creative_mode")) .. "]"..
-                       "checkbox[0.5,0.7;cb_enable_damage;".. fgettext("Enable Damage") .. ";" ..
-                       dump(engine.setting_getbool("enable_damage")) .. "]"..
-                       "textlist[4,0.25;7.5,3.7;sp_worlds;" ..
-                       menu.render_world_list() ..
-                       ";" .. index .. "]" ..
-                       menubar.formspec
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.tab_texture_packs()
-       local retval = "label[4,-0.25;".. fgettext("Select texture pack:") .. "]"..
-                       "vertlabel[0,-0.25;".. fgettext("TEXTURE PACKS") .. "]" ..
-                       "textlist[4,0.25;7.5,5.0;TPs;"
-
-       local current_texture_path = engine.setting_get("texture_path")
-       local list = filter_texture_pack_list(engine.get_dirlist(engine.get_texturepath(), true))
-       local index = tonumber(engine.setting_get("mainmenu_last_selected_TP"))
-
-       if index == nil then index = 1 end
-
-       if current_texture_path == "" then
-               retval = retval ..
-                       menu.render_texture_pack_list(list) ..
-                       ";" .. index .. "]"
-               return retval
-       end
-
-       local infofile = current_texture_path ..DIR_DELIM.."info.txt"
-       local infotext = ""
-       local f = io.open(infofile, "r")
-       if f==nil then
-               infotext = fgettext("No information available")
-       else
-               infotext = f:read("*all")
-               f:close()
-       end
-
-       local screenfile = current_texture_path..DIR_DELIM.."screenshot.png"
-       local no_screenshot = nil
-       if not file_exists(screenfile) then
-               screenfile = nil
-               no_screenshot = menu.defaulttexturedir .. "no_screenshot.png"
-       end
-
-       return  retval ..
-                       menu.render_texture_pack_list(list) ..
-                       ";" .. index .. "]" ..
-                       "image[0.65,0.25;4.0,3.7;"..engine.formspec_escape(screenfile or no_screenshot).."]"..
-                       "textarea[1.0,3.25;3.7,1.5;;"..engine.formspec_escape(infotext or "")..";]"
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.tab_credits()
-       local logofile = menu.defaulttexturedir .. "logo.png"
-       return  "vertlabel[0,-0.5;CREDITS]" ..
-                       "label[0.5,3;Minetest " .. engine.get_version() .. "]" ..
-                       "label[0.5,3.3;http://minetest.net]" ..
-                       "image[0.5,1;" .. engine.formspec_escape(logofile) .. "]" ..
-                       "textlist[3.5,-0.25;8.5,5.8;list_credits;" ..
-                       "#FFFF00" .. fgettext("Core Developers") .."," ..
-                       "Perttu Ahola (celeron55) <celeron55@gmail.com>,"..
-                       "Ryan Kwolek (kwolekr) <kwolekr@minetest.net>,"..
-                       "PilzAdam <pilzadam@minetest.net>," ..
-                       "Ilya Zhuravlev (xyz) <xyz@minetest.net>,"..
-                       "Lisa Milne (darkrose) <lisa@ltmnet.com>,"..
-                       "Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>,"..
-                       "proller <proler@gmail.com>,"..
-                       "sfan5 <sfan5@live.de>,"..
-                       "kahrl <kahrl@gmx.net>,"..
-                       "sapier,"..
-                       "ShadowNinja <shadowninja@minetest.net>,"..
-                       "Nathanael Courant (Nore/Novatux) <nore@mesecons.net>,"..
-                       "BlockMen,"..
-                       ","..
-                       "#FFFF00" .. fgettext("Active Contributors") .. "," ..
-                       "Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>,"..
-                       "Jurgen Doser (doserj) <jurgen.doser@gmail.com>,"..
-                       "Jeija <jeija@mesecons.net>,"..
-                       "MirceaKitsune <mirceakitsune@gmail.com>,"..
-                       "dannydark <the_skeleton_of_a_child@yahoo.co.uk>,"..
-                       "0gb.us <0gb.us@0gb.us>,"..
-                       "," ..
-                       "#FFFF00" .. fgettext("Previous Contributors") .. "," ..
-                       "Guiseppe Bilotta (Oblomov) <guiseppe.bilotta@gmail.com>,"..
-                       "Jonathan Neuschafer <j.neuschaefer@gmx.net>,"..
-                       "Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net>,"..
-                       "Constantin Wenger (SpeedProg) <constantin.wenger@googlemail.com>,"..
-                       "matttpt <matttpt@gmail.com>,"..
-                       "JacobF <queatz@gmail.com>,"..
-                       ";0;true]"
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.init()
-       tabbuilder.tabfuncs = {
-               singleplayer  = tabbuilder.tab_singleplayer,
-               multiplayer   = tabbuilder.tab_multiplayer,
-               server        = tabbuilder.tab_server,
-               settings      = tabbuilder.tab_settings,
-               texture_packs = tabbuilder.tab_texture_packs,
-               credits       = tabbuilder.tab_credits,
-               dialog_create_world = tabbuilder.dialog_create_world,
-               dialog_delete_world = tabbuilder.dialog_delete_world
-       }
-
-       tabbuilder.tabsizes = {
-               dialog_create_world = {width=12, height=7},
-               dialog_delete_world = {width=12, height=5.2}
-       }
-
-       tabbuilder.current_tab = engine.setting_get("main_menu_tab")
-
-       if tabbuilder.current_tab == nil or
-               tabbuilder.current_tab == "" then
-               tabbuilder.current_tab = "singleplayer"
-               engine.setting_set("main_menu_tab",tabbuilder.current_tab)
-       end
-
-       --initialize tab buttons
-       tabbuilder.last_tab = nil
-       tabbuilder.show_buttons = true
-
-       tabbuilder.current_buttons = {}
-       table.insert(tabbuilder.current_buttons,{name="singleplayer", caption=fgettext("Singleplayer")})
-       table.insert(tabbuilder.current_buttons,{name="multiplayer", caption=fgettext("Client")})
-       table.insert(tabbuilder.current_buttons,{name="server", caption=fgettext("Server")})
-       table.insert(tabbuilder.current_buttons,{name="settings", caption=fgettext("Settings")})
-       table.insert(tabbuilder.current_buttons,{name="texture_packs", caption=fgettext("Texture Packs")})
-
-       if engine.setting_getbool("main_menu_game_mgr") then
-               table.insert(tabbuilder.current_buttons,{name="game_mgr", caption=fgettext("Games")})
-       end
-
-       if engine.setting_getbool("main_menu_mod_mgr") then
-               table.insert(tabbuilder.current_buttons,{name="mod_mgr", caption=fgettext("Mods")})
-       end
-       table.insert(tabbuilder.current_buttons,{name="credits", caption=fgettext("Credits")})
-
-
-       for i=1,#tabbuilder.current_buttons,1 do
-               if tabbuilder.current_buttons[i].name == tabbuilder.current_tab then
-                       tabbuilder.last_tab_index = i
-               end
-       end
-
-       if tabbuilder.current_tab ~= "singleplayer" then
-               menu.update_gametype(true)
-       else
-               menu.update_gametype()
-       end
-end
-
---------------------------------------------------------------------------------
-function tabbuilder.checkretval(retval)
-
-       if retval ~= nil then
-               if retval.current_tab ~= nil then
-                       tabbuilder.current_tab = retval.current_tab
-               end
-
-               if retval.is_dialog ~= nil then
-                       tabbuilder.is_dialog = retval.is_dialog
-               end
-
-               if retval.show_buttons ~= nil then
-                       tabbuilder.show_buttons = retval.show_buttons
-               end
-
-               if retval.skipformupdate ~= nil then
-                       tabbuilder.skipformupdate = retval.skipformupdate
-               end
-
-               if retval.ignore_menu_quit == true then
-                       tabbuilder.ignore_menu_quit = true
-               else
-                       tabbuilder.ignore_menu_quit = false
-               end
-       end
-end
-
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
--- initialize callbacks
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
-engine.button_handler = function(fields)
-       --print("Buttonhandler: tab: " .. tabbuilder.current_tab .. " fields: " .. dump(fields))
-
-       if fields["btn_error_confirm"] then
-               gamedata.errormessage = nil
-       end
-
-       local retval = modmgr.handle_buttons(tabbuilder.current_tab,fields)
-       tabbuilder.checkretval(retval)
-
-       retval = gamemgr.handle_buttons(tabbuilder.current_tab,fields)
-       tabbuilder.checkretval(retval)
-
-       retval = modstore.handle_buttons(tabbuilder.current_tab,fields)
-       tabbuilder.checkretval(retval)
-
-       if tabbuilder.current_tab == "dialog_create_world" then
-               tabbuilder.handle_create_world_buttons(fields)
-       end
-
-       if tabbuilder.current_tab == "dialog_delete_world" then
-               tabbuilder.handle_delete_world_buttons(fields)
-       end
-
-       if tabbuilder.current_tab == "singleplayer" then
-               tabbuilder.handle_singleplayer_buttons(fields)
-       end
-
-       if tabbuilder.current_tab == "texture_packs" then
-               tabbuilder.handle_texture_pack_buttons(fields)
-       end
-
-       if tabbuilder.current_tab == "multiplayer" then
-               tabbuilder.handle_multiplayer_buttons(fields)
-       end
-
-       if tabbuilder.current_tab == "settings" then
-               tabbuilder.handle_settings_buttons(fields)
-       end
-
-       if tabbuilder.current_tab == "server" then
-               tabbuilder.handle_server_buttons(fields)
-       end
-
-       --tab buttons
-       tabbuilder.handle_tab_buttons(fields)
-
-       --menubar buttons
-       menubar.handle_buttons(fields)
-
-       if not tabbuilder.skipformupdate then
-               --update menu
-               update_menu()
-       else
-               tabbuilder.skipformupdate = false
-       end
-end
-
---------------------------------------------------------------------------------
-engine.event_handler = function(event)
-       if event == "MenuQuit" then
-               if tabbuilder.is_dialog then
-                       if tabbuilder.ignore_menu_quit then
-                               return
-                       end
-
-                       tabbuilder.is_dialog = false
-                       tabbuilder.show_buttons = true
-                       tabbuilder.current_tab = engine.setting_get("main_menu_tab")
-                       menu.update_gametype()
-                       update_menu()
-               else
-                       engine.close()
-               end
-       end
-
-       if event == "Refresh" then
-               update_menu()
-       end
-end
-
---------------------------------------------------------------------------------
-function menu.update_gametype(reset)
-       local game = menu.lastgame()
-
-       if reset or game == nil then
-               mm_texture.reset()
-               engine.set_topleft_text("")
-               filterlist.set_filtercriteria(worldlist,nil)
-       else
-               mm_texture.update(tabbuilder.current_tab,game)
-               engine.set_topleft_text(game.name)
-               filterlist.set_filtercriteria(worldlist,game.id)
-       end
-end
-
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
--- menu startup
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
-init_globals()
-mm_texture.init()
-menu.init()
-tabbuilder.init()
-menubar.refresh()
-modstore.init()
-
-engine.sound_play("main_menu", true)
-
-update_menu()
diff --git a/builtin/mainmenu/filterlist.lua b/builtin/mainmenu/filterlist.lua
new file mode 100644 (file)
index 0000000..379a5ce
--- /dev/null
@@ -0,0 +1,301 @@
+--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.
+
+--------------------------------------------------------------------------------
+-- Generic implementation of a filter/sortable list                           --
+-- Usage:                                                                     --
+-- Filterlist needs to be initialized on creation. To achieve this you need to --
+-- pass following functions:                                                  --
+-- raw_fct() (mandatory):                                                     --
+--     function returning a table containing the elements to be filtered      --
+-- compare_fct(element1,element2) (mandatory):                                --
+--     function returning true/false if element1 is same element as element2  --
+-- uid_match_fct(element1,uid) (optional)                                     --
+--     function telling if uid is attached to element1                        --
+-- filter_fct(element,filtercriteria) (optional)                              --
+--     function returning true/false if filtercriteria met to element         --
+-- fetch_param (optional)                                                     --
+--     parameter passed to raw_fct to aquire correct raw data                 --
+--                                                                            --
+--------------------------------------------------------------------------------
+filterlist = {}
+
+--------------------------------------------------------------------------------
+function filterlist.refresh(this)
+       this.m_raw_list = this.m_raw_list_fct(this.m_fetch_param)
+       filterlist.process(this)
+end
+
+--------------------------------------------------------------------------------
+function filterlist.create(raw_fct,compare_fct,uid_match_fct,filter_fct,fetch_param)
+
+       assert((raw_fct ~= nil) and (type(raw_fct) == "function"))
+       assert((compare_fct ~= nil) and (type(compare_fct) == "function"))
+       
+       local this = {}
+       
+       this.m_raw_list_fct  = raw_fct
+       this.m_compare_fct   = compare_fct
+       this.m_filter_fct    = filter_fct
+       this.m_uid_match_fct = uid_match_fct
+       
+       this.m_filtercriteria = nil
+       this.m_fetch_param = fetch_param
+       
+       this.m_sortmode = "none"
+       this.m_sort_list = {}
+
+       this.m_processed_list = nil
+       this.m_raw_list = this.m_raw_list_fct(this.m_fetch_param)
+
+       filterlist.process(this)
+       
+       return this
+end
+
+--------------------------------------------------------------------------------
+function filterlist.add_sort_mechanism(this,name,fct)
+       this.m_sort_list[name] = fct
+end
+
+--------------------------------------------------------------------------------
+function filterlist.set_filtercriteria(this,criteria)
+       if criteria == this.m_filtercriteria and
+               type(criteria) ~= "table" then
+               return
+       end
+       this.m_filtercriteria = criteria
+       filterlist.process(this)
+end
+
+--------------------------------------------------------------------------------
+function filterlist.get_filtercriteria(this)
+       return this.m_filtercriteria
+end
+
+--------------------------------------------------------------------------------
+--supported sort mode "alphabetic|none"
+function filterlist.set_sortmode(this,mode)
+       if (mode == this.m_sortmode) then
+               return
+       end
+       this.m_sortmode = mode
+       filterlist.process(this)
+end
+
+--------------------------------------------------------------------------------
+function filterlist.get_list(this)
+       return this.m_processed_list
+end
+
+--------------------------------------------------------------------------------
+function filterlist.get_raw_list(this)
+       return this.m_raw_list
+end
+
+--------------------------------------------------------------------------------
+function filterlist.get_raw_element(this,idx)
+       if type(idx) ~= "number" then
+               idx = tonumber(idx)
+       end
+       
+       if idx ~= nil and idx > 0 and idx < #this.m_raw_list then
+               return this.m_raw_list[idx]
+       end
+       
+       return nil
+end
+
+--------------------------------------------------------------------------------
+function filterlist.get_raw_index(this,listindex)
+       assert(this.m_processed_list ~= nil)
+       
+       if listindex ~= nil and listindex > 0 and
+               listindex <= #this.m_processed_list then
+               local entry = this.m_processed_list[listindex]
+               
+               for i,v in ipairs(this.m_raw_list) do
+               
+                       if this.m_compare_fct(v,entry) then
+                               return i
+                       end
+               end
+       end
+       
+       return 0
+end
+
+--------------------------------------------------------------------------------
+function filterlist.get_current_index(this,listindex)
+       assert(this.m_processed_list ~= nil)
+       
+       if listindex ~= nil and listindex > 0 and
+               listindex <= #this.m_raw_list then
+               local entry = this.m_raw_list[listindex]
+               
+               for i,v in ipairs(this.m_processed_list) do
+               
+                       if this.m_compare_fct(v,entry) then
+                               return i
+                       end
+               end
+       end
+       
+       return 0
+end
+
+--------------------------------------------------------------------------------
+function filterlist.process(this)
+       assert(this.m_raw_list ~= nil)
+
+       if this.m_sortmode == "none" and
+               this.m_filtercriteria == nil then
+               this.m_processed_list = this.m_raw_list
+               return
+       end
+       
+       this.m_processed_list = {}
+       
+       for k,v in pairs(this.m_raw_list) do
+               if this.m_filtercriteria == nil or
+                       this.m_filter_fct(v,this.m_filtercriteria) then
+                       table.insert(this.m_processed_list,v)
+               end
+       end
+       
+       if this.m_sortmode == "none" then
+               return
+       end
+       
+       if this.m_sort_list[this.m_sortmode] ~= nil and
+               type(this.m_sort_list[this.m_sortmode]) == "function" then
+               
+               this.m_sort_list[this.m_sortmode](this)
+       end
+end
+
+--------------------------------------------------------------------------------
+function filterlist.size(this)
+       if this.m_processed_list == nil then
+               return 0
+       end
+       
+       return #this.m_processed_list
+end
+
+--------------------------------------------------------------------------------
+function filterlist.uid_exists_raw(this,uid)
+       for i,v in ipairs(this.m_raw_list) do
+               if this.m_uid_match_fct(v,uid) then
+                       return true
+               end
+       end
+       return false
+end
+
+--------------------------------------------------------------------------------
+function filterlist.raw_index_by_uid(this, uid)
+       local elementcount = 0
+       local elementidx = 0
+       for i,v in ipairs(this.m_raw_list) do
+               if this.m_uid_match_fct(v,uid) then
+                       elementcount = elementcount +1
+                       elementidx = i
+               end
+       end
+       
+       
+       -- If there are more elements than one with same name uid can't decide which
+       -- one is meant. This shouldn't be possible but just for sure.
+       if elementcount > 1 then
+               elementidx=0
+       end
+
+       return elementidx
+end
+
+--------------------------------------------------------------------------------
+-- COMMON helper functions                                                    --
+--------------------------------------------------------------------------------
+
+--------------------------------------------------------------------------------
+function compare_worlds(world1,world2)
+
+       if world1.path ~= world2.path then
+               return false
+       end
+       
+       if world1.name ~= world2.name then
+               return false
+       end
+       
+       if world1.gameid ~= world2.gameid then
+               return false
+       end
+
+       return true
+end
+
+--------------------------------------------------------------------------------
+function sort_worlds_alphabetic(this)
+
+       table.sort(this.m_processed_list, function(a, b)
+               --fixes issue #857 (crash due to sorting nil in worldlist)
+               if a == nil or b == nil then
+                       if a == nil and b ~= nil then return false end
+                       if b == nil and a ~= nil then return true end
+                       return false
+               end
+               if a.name:lower() == b.name:lower() then
+                       return a.name < b.name
+               end
+               return a.name:lower() < b.name:lower()
+       end)
+end
+
+--------------------------------------------------------------------------------
+function sort_mod_list(this)
+
+       table.sort(this.m_processed_list, function(a, b)
+               -- Show game mods at bottom
+               if a.typ ~= b.typ then
+                       return b.typ == "game_mod"
+               end
+               -- If in same or no modpack, sort by name
+               if a.modpack == b.modpack then
+                       if a.name:lower() == b.name:lower() then
+                               return a.name < b.name
+                       end
+                       return a.name:lower() < b.name:lower()
+               -- Else compare name to modpack name
+               else
+                       -- Always show modpack pseudo-mod on top of modpack mod list
+                       if a.name == b.modpack then
+                               return true
+                       elseif b.name == a.modpack then
+                               return false
+                       end
+                       
+                       local name_a = a.modpack or a.name
+                       local name_b = b.modpack or b.name
+                       if name_a:lower() == name_b:lower() then
+                               return  name_a < name_b
+                       end
+                       return name_a:lower() < name_b:lower()
+               end
+       end)
+end
diff --git a/builtin/mainmenu/gamemgr.lua b/builtin/mainmenu/gamemgr.lua
new file mode 100644 (file)
index 0000000..c99c2de
--- /dev/null
@@ -0,0 +1,322 @@
+--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;" .. fgettext("Game Name") .. "]"..
+               "field[4.5,2.4;6,0.5;te_game_name;;]" ..
+               "button[5,4.2;2.6,0.5;new_game_confirm;" .. fgettext("Create") .. "]" ..
+               "button[7.5,4.2;2.8,0.5;new_game_cancel;" .. fgettext("Cancel") .. "]"
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.handle_games_buttons(fields)
+       if fields["gamelist"] ~= nil then
+               local event = engine.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")
+               
+               local mod = modmgr.get_global_mod(modindex)
+               if mod ~= nil then
+                       
+                       local sourcepath = mod.path
+                       
+                       if not gamemgr.add_mod(current_game,sourcepath) then
+                               gamedata.errormessage =
+                                       fgettext("Gamemgr: Unable to copy mod \"$1\" to game \"$2\"", mod.name, current_game.id)
+                       end
+               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)
+               
+               return engine.copy_dir(sourcepath,gamespec.gamemods_path .. DIR_DELIM .. modname);
+       end
+       
+       return false
+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
+
+                       if game_mods[modindex].path:sub(0,gamespec.gamemods_path:len()) 
+                                       == gamespec.gamemods_path then
+                               engine.delete_dir(game_mods[modindex].path)
+                       end
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.find_by_gameid(gameid)
+       for i=1,#gamemgr.games,1 do             
+               if gamemgr.games[i].id == gameid then
+                       return gamemgr.games[i], i
+               end
+       end
+       return nil, nil
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.get_game_mods(gamespec, retval)
+       if gamespec ~= nil and
+               gamespec.gamemods_path ~= nil and
+               gamespec.gamemods_path ~= "" then
+               get_mods(gamespec.gamemods_path, retval)
+       end
+end
+
+--------------------------------------------------------------------------------
+function gamemgr.get_game_modlist(gamespec)
+       local retval = ""
+       local game_mods = {}
+       gamemgr.get_game_mods(gamespec, game_mods)
+       for i=1,#game_mods,1 do
+               if retval ~= "" then
+                       retval = retval..","
+               end
+               retval = retval .. game_mods[i].name
+       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;" .. fgettext("GAMES") .. "]" ..
+               "label[1,-0.25;" .. fgettext("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;" ..
+                               engine.formspec_escape(current_game.menuicon_path) .. "]"
+               end
+               
+               retval = retval ..
+                       "field[8,-0.25;6,2;;" .. current_game.name .. ";]"..
+                       "label[6,1.4;" .. fgettext("Mods:") .."]" ..
+                       "button[9.7,1.5;2,0.2;btn_game_mgr_edit_game;" .. fgettext("edit game") .. "]" ..
+                       "textlist[6,2;5.5,3.3;game_mgr_modlist;"
+                       .. gamemgr.get_game_modlist(current_game) ..";0]" ..
+                       "button[1,4.75;3.2,0.5;btn_game_mgr_new_game;" .. fgettext("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;" .. fgettext("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;" ..
+                               engine.formspec_escape(current_game.menuicon_path) .. "]"
+               end
+               
+               retval = retval .. 
+                       "textlist[0.5,0.5;4.5,4.3;mods_current;"
+                       .. gamemgr.get_game_modlist(current_game) ..";0]"
+                       
+                       
+               retval = retval .. 
+                       "textlist[7,0.5;4.5,4.3;mods_available;"
+                       .. modmgr.render_modlist() .. ";0]"
+
+               retval = retval ..
+                       "button[0.55,4.95;4.7,0.5;btn_remove_mod_from_game;" .. fgettext("Remove selected mod") .."]"
+                       
+               retval = retval ..
+                       "button[7.05,4.95;4.7,0.5;btn_add_mod_to_game;" .. fgettext("<<-- 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
diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua
new file mode 100644 (file)
index 0000000..d78a668
--- /dev/null
@@ -0,0 +1,1337 @@
+
+local menupath = engine.get_mainmenu_path()..DIR_DELIM
+local commonpath = engine.get_builtin_path()..DIR_DELIM.."common"..DIR_DELIM
+
+dofile(menupath.."filterlist.lua")
+dofile(menupath.."modmgr.lua")
+dofile(menupath.."modstore.lua")
+dofile(menupath.."gamemgr.lua")
+dofile(menupath.."textures.lua")
+dofile(menupath.."menubar.lua")
+dofile(commonpath.."async_event.lua")
+
+mt_color_grey  = "#AAAAAA"
+mt_color_blue  = "#0000DD"
+mt_color_green = "#00DD00"
+mt_color_dark_green = "#003300"
+
+--for all other colors ask sfan5 to complete his worK!
+
+menu = {}
+local tabbuilder = {}
+local worldlist = nil
+
+--------------------------------------------------------------------------------
+local function filter_texture_pack_list(list)
+       retval = {"None"}
+       for _,i in ipairs(list) do
+               if i~="base" then
+                       table.insert(retval, i)
+               end
+       end
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function menu.render_favorite(spec,render_details)
+       local text = ""
+
+       if spec.name ~= nil then
+               text = text .. engine.formspec_escape(spec.name:trim())
+
+--             if spec.description ~= nil and
+--                     engine.formspec_escape(spec.description):trim() ~= "" then
+--                     text = text .. " (" .. engine.formspec_escape(spec.description) .. ")"
+--             end
+       else
+               if spec.address ~= nil then
+                       text = text .. spec.address:trim()
+
+                       if spec.port ~= nil then
+                               text = text .. ":" .. spec.port
+                       end
+               end
+       end
+
+       if not render_details then
+               return text
+       end
+
+       local details = ""
+       if spec.password == true then
+               details = details .. "*"
+       else
+               details = details .. "_"
+       end
+
+       if spec.creative then
+               details = details .. "C"
+       else
+               details = details .. "_"
+       end
+
+       if spec.damage then
+               details = details .. "D"
+       else
+               details = details .. "_"
+       end
+
+       if spec.pvp then
+               details = details .. "P"
+       else
+               details = details .. "_"
+       end
+       details = details .. " "
+
+       local playercount = ""
+
+       if spec.clients ~= nil and
+               spec.clients_max ~= nil then
+               playercount = string.format("%03d",spec.clients) .. "/" ..
+                                               string.format("%03d",spec.clients_max) .. " "
+       end
+
+       return playercount .. engine.formspec_escape(details) ..  text
+end
+
+--------------------------------------------------------------------------------
+os.tempfolder = function()
+       local filetocheck = os.tmpname()
+       os.remove(filetocheck)
+
+       local randname = "MTTempModFolder_" .. math.random(0,10000)
+       if DIR_DELIM == "\\" then
+               local tempfolder = os.getenv("TEMP")
+               return tempfolder .. filetocheck
+       else
+               local backstring = filetocheck:reverse()
+               return filetocheck:sub(0,filetocheck:len()-backstring:find(DIR_DELIM)+1) ..randname
+       end
+
+end
+
+--------------------------------------------------------------------------------
+function text2textlist(xpos,ypos,width,height,tl_name,textlen,text,transparency)
+       local textlines = engine.splittext(text,textlen)
+       
+       local retval = "textlist[" .. xpos .. "," .. ypos .. ";"
+                                                               .. width .. "," .. height .. ";"
+                                                               .. tl_name .. ";"
+       
+       for i=1, #textlines, 1 do
+               textlines[i] = textlines[i]:gsub("\r","")
+               retval = retval .. engine.formspec_escape(textlines[i]) .. ","
+       end
+       
+       retval = retval .. ";0;"
+       
+       if transparency then
+               retval = retval .. "true"
+       end
+       
+       retval = retval .. "]"
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function init_globals()
+       --init gamedata
+       gamedata.worldindex = 0
+
+       worldlist = filterlist.create(
+                                       engine.get_worlds,
+                                       compare_worlds,
+                                       function(element,uid)
+                                               if element.name == uid then
+                                                       return true
+                                               end
+                                               return false
+                                       end, --unique id compare fct
+                                       function(element,gameid)
+                                               if element.gameid == gameid then
+                                                       return true
+                                               end
+                                               return false
+                                       end --filter fct
+                                       )
+
+       filterlist.add_sort_mechanism(worldlist,"alphabetic",sort_worlds_alphabetic)
+       filterlist.set_sortmode(worldlist,"alphabetic")
+end
+
+--------------------------------------------------------------------------------
+function update_menu()
+
+       local formspec
+
+       -- handle errors
+       if gamedata.errormessage ~= nil then
+               formspec = "size[12,5.2,true]" ..
+                       "textarea[1,2;10,2;;ERROR: " ..
+                       engine.formspec_escape(gamedata.errormessage) ..
+                       ";]"..
+                       "button[4.5,4.2;3,0.5;btn_error_confirm;" .. fgettext("Ok") .. "]"
+       else
+               formspec = tabbuilder.gettab()
+       end
+
+       engine.update_formspec(formspec)
+end
+
+--------------------------------------------------------------------------------
+function menu.render_world_list()
+       local retval = ""
+
+       local current_worldlist = filterlist.get_list(worldlist)
+
+       for i,v in ipairs(current_worldlist) do
+               if retval ~= "" then
+                       retval = retval ..","
+               end
+
+               retval = retval .. engine.formspec_escape(v.name) ..
+                                       " \\[" .. engine.formspec_escape(v.gameid) .. "\\]"
+       end
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function menu.render_texture_pack_list(list)
+       local retval = ""
+
+       for i, v in ipairs(list) do
+               if retval ~= "" then
+                       retval = retval ..","
+               end
+
+               retval = retval .. engine.formspec_escape(v)
+       end
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function menu.asyncOnlineFavourites()
+       menu.favorites = {}
+       engine.handle_async(
+               function(param)
+                       return engine.get_favorites("online")
+               end,
+               nil,
+               function(result)
+                       menu.favorites = result
+                       engine.event_handler("Refresh")
+               end
+               )
+end
+
+--------------------------------------------------------------------------------
+function menu.init()
+       --init menu data
+       gamemgr.update_gamelist()
+
+       menu.last_game  = tonumber(engine.setting_get("main_menu_last_game_idx"))
+
+       if type(menu.last_game) ~= "number" then
+               menu.last_game = 1
+       end
+
+       if engine.setting_getbool("public_serverlist") then
+               menu.asyncOnlineFavourites()
+       else
+               menu.favorites = engine.get_favorites("local")
+       end
+
+       menu.defaulttexturedir = engine.get_texturepath_share() .. DIR_DELIM .. "base" ..
+                                       DIR_DELIM .. "pack" .. DIR_DELIM
+end
+
+--------------------------------------------------------------------------------
+function menu.lastgame()
+       if menu.last_game > 0 and menu.last_game <= #gamemgr.games then
+               return gamemgr.games[menu.last_game]
+       end
+
+       if #gamemgr.games >= 1 then
+               menu.last_game = 1
+               return gamemgr.games[menu.last_game]
+       end
+
+       --error case!!
+       return nil
+end
+
+--------------------------------------------------------------------------------
+function menu.update_last_game()
+
+       local current_world = filterlist.get_raw_element(worldlist,
+                                                       engine.setting_get("mainmenu_last_selected_world")
+                                                       )
+
+       if current_world == nil then
+               return
+       end
+
+       local gamespec, i = gamemgr.find_by_gameid(current_world.gameid)
+       if i ~= nil then
+               menu.last_game = i
+               engine.setting_set("main_menu_last_game_idx",menu.last_game)
+       end
+end
+
+--------------------------------------------------------------------------------
+function menu.handle_key_up_down(fields,textlist,settingname)
+
+       if fields["key_up"] then
+               local oldidx = engine.get_textlist_index(textlist)
+
+               if oldidx ~= nil and oldidx > 1 then
+                       local newidx = oldidx -1
+                       engine.setting_set(settingname,
+                               filterlist.get_raw_index(worldlist,newidx))
+               end
+       end
+
+       if fields["key_down"] then
+               local oldidx = engine.get_textlist_index(textlist)
+
+               if oldidx ~= nil and oldidx < filterlist.size(worldlist) then
+                       local newidx = oldidx + 1
+                       engine.setting_set(settingname,
+                               filterlist.get_raw_index(worldlist,newidx))
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.dialog_create_world()
+       local mapgens = {"v6", "v7", "indev", "singlenode", "math"}
+
+       local current_seed = engine.setting_get("fixed_map_seed") or ""
+       local current_mg   = engine.setting_get("mg_name")
+
+       local mglist = ""
+       local selindex = 1
+       local i = 1
+       for k,v in pairs(mapgens) do
+               if current_mg == v then
+                       selindex = i
+               end
+               i = i + 1
+               mglist = mglist .. v .. ","
+       end
+       mglist = mglist:sub(1, -2)
+
+       local retval =
+               "label[2,0;" .. fgettext("World name") .. "]"..
+               "field[4.5,0.4;6,0.5;te_world_name;;]" ..
+
+               "label[2,1;" .. fgettext("Seed") .. "]"..
+               "field[4.5,1.4;6,0.5;te_seed;;".. current_seed .. "]" ..
+
+               "label[2,2;" .. fgettext("Mapgen") .. "]"..
+               "dropdown[4.2,2;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]" ..
+
+               "label[2,3;" .. fgettext("Game") .. "]"..
+               "textlist[4.2,3;5.8,2.3;games;" .. gamemgr.gamelist() ..
+               ";" .. menu.last_game .. ";true]" ..
+
+               "button[5,5.5;2.6,0.5;world_create_confirm;" .. fgettext("Create") .. "]" ..
+               "button[7.5,5.5;2.8,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]"
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.dialog_delete_world()
+       return  "label[2,2;" ..
+                       fgettext("Delete World \"$1\"?", filterlist.get_raw_list(worldlist)[menu.world_to_del].name) .. "]"..
+                       "button[3.5,4.2;2.6,0.5;world_delete_confirm;" .. fgettext("Yes").. "]" ..
+                       "button[6,4.2;2.8,0.5;world_delete_cancel;" .. fgettext("No") .. "]"
+end
+
+--------------------------------------------------------------------------------
+
+function tabbuilder.gettab()
+       local tsize = tabbuilder.tabsizes[tabbuilder.current_tab] or {width=12, height=5.2}
+       local retval = "size[" .. tsize.width .. "," .. tsize.height .. ",true]"
+
+       if tabbuilder.show_buttons then
+               retval = retval .. tabbuilder.tab_header()
+       end
+
+       local buildfunc = tabbuilder.tabfuncs[tabbuilder.current_tab]
+       if buildfunc ~= nil then
+               retval = retval .. buildfunc()
+       end
+
+       retval = retval .. modmgr.gettab(tabbuilder.current_tab)
+       retval = retval .. gamemgr.gettab(tabbuilder.current_tab)
+       retval = retval .. modstore.gettab(tabbuilder.current_tab)
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_create_world_buttons(fields)
+
+       if fields["world_create_confirm"] or
+               fields["key_enter"] then
+
+               local worldname = fields["te_world_name"]
+               local gameindex = engine.get_textlist_index("games")
+
+               if gameindex ~= nil and
+                       worldname ~= "" then
+
+                       local message = nil
+
+                       if not filterlist.uid_exists_raw(worldlist,worldname) then
+                               engine.setting_set("mg_name",fields["dd_mapgen"])
+                               message = engine.create_world(worldname,gameindex)
+                       else
+                               message = fgettext("A world named \"$1\" already exists", worldname)
+                       end
+
+                       engine.setting_set("fixed_map_seed", fields["te_seed"])
+
+                       if message ~= nil then
+                               gamedata.errormessage = message
+                       else
+                               menu.last_game = gameindex
+                               engine.setting_set("main_menu_last_game_idx",gameindex)
+
+                               filterlist.refresh(worldlist)
+                               engine.setting_set("mainmenu_last_selected_world",
+                                                                       filterlist.raw_index_by_uid(worldlist,worldname))
+                       end
+               else
+                       gamedata.errormessage =
+                               fgettext("No worldname given or no game selected")
+               end
+       end
+
+       if fields["games"] then
+               tabbuilder.skipformupdate = true
+               return
+       end
+
+       --close dialog
+       tabbuilder.is_dialog = false
+       tabbuilder.show_buttons = true
+       tabbuilder.current_tab = engine.setting_get("main_menu_tab")
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_delete_world_buttons(fields)
+
+       if fields["world_delete_confirm"] then
+               if menu.world_to_del > 0 and
+                       menu.world_to_del <= #filterlist.get_raw_list(worldlist) then
+                       engine.delete_world(menu.world_to_del)
+                       menu.world_to_del = 0
+                       filterlist.refresh(worldlist)
+               end
+       end
+
+       tabbuilder.is_dialog = false
+       tabbuilder.show_buttons = true
+       tabbuilder.current_tab = engine.setting_get("main_menu_tab")
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_multiplayer_buttons(fields)
+
+       if fields["te_name"] ~= nil then
+               gamedata.playername = fields["te_name"]
+               engine.setting_set("name", fields["te_name"])
+       end
+
+       if fields["favourites"] ~= nil then
+               local event = engine.explode_textlist_event(fields["favourites"])
+               if event.type == "DCL" then
+                       if event.index <= #menu.favorites then
+                               gamedata.address = menu.favorites[event.index].address
+                               gamedata.port = menu.favorites[event.index].port
+                               gamedata.playername             = fields["te_name"]
+                               if fields["te_pwd"] ~= nil then
+                                       gamedata.password               = fields["te_pwd"]
+                               end
+                               gamedata.selected_world = 0
+
+                               if menu.favorites ~= nil then
+                                       gamedata.servername = menu.favorites[event.index].name
+                                       gamedata.serverdescription = menu.favorites[event.index].description
+                               end
+
+                               if gamedata.address ~= nil and
+                                       gamedata.port ~= nil then
+                                       engine.setting_set("address",gamedata.address)
+                                       engine.setting_set("remote_port",gamedata.port)
+                                       engine.start()
+                               end
+                       end
+               end
+
+               if event.type == "CHG" then
+                       if event.index <= #menu.favorites then
+                               local address = menu.favorites[event.index].address
+                               local port = menu.favorites[event.index].port
+
+                               if address ~= nil and
+                                       port ~= nil then
+                                       engine.setting_set("address",address)
+                                       engine.setting_set("remote_port",port)
+                               end
+
+                               menu.fav_selected = event.index
+                       end
+               end
+               return
+       end
+
+       if fields["key_up"] ~= nil or
+               fields["key_down"] ~= nil then
+
+               local fav_idx = engine.get_textlist_index("favourites")
+
+               if fav_idx ~= nil then
+                       if fields["key_up"] ~= nil and fav_idx > 1 then
+                               fav_idx = fav_idx -1
+                       else if fields["key_down"] and fav_idx < #menu.favorites then
+                               fav_idx = fav_idx +1
+                       end end
+               end
+
+               local address = menu.favorites[fav_idx].address
+               local port = menu.favorites[fav_idx].port
+
+               if address ~= nil and
+                       port ~= nil then
+                       engine.setting_set("address",address)
+                       engine.setting_set("remote_port",port)
+               end
+
+               menu.fav_selected = fav_idx
+               return
+       end
+
+       if fields["cb_public_serverlist"] ~= nil then
+               engine.setting_set("public_serverlist", fields["cb_public_serverlist"])
+
+               if engine.setting_getbool("public_serverlist") then
+                       menu.asyncOnlineFavourites()
+               else
+                       menu.favorites = engine.get_favorites("local")
+               end
+               menu.fav_selected = nil
+               return
+       end
+
+       if fields["btn_delete_favorite"] ~= nil then
+               local current_favourite = engine.get_textlist_index("favourites")
+               if current_favourite == nil then return end
+               engine.delete_favorite(current_favourite)
+               menu.favorites = engine.get_favorites()
+               menu.fav_selected = nil
+
+               engine.setting_set("address","")
+               engine.setting_set("remote_port","30000")
+
+               return
+       end
+
+       if fields["btn_mp_connect"] ~= nil or
+               fields["key_enter"] ~= nil then
+
+               gamedata.playername             = fields["te_name"]
+               gamedata.password               = fields["te_pwd"]
+               gamedata.address                = fields["te_address"]
+               gamedata.port                   = fields["te_port"]
+
+               local fav_idx = engine.get_textlist_index("favourites")
+
+               if fav_idx ~= nil and fav_idx <= #menu.favorites and
+                       menu.favorites[fav_idx].address == fields["te_address"] and
+                       menu.favorites[fav_idx].port    == fields["te_port"] then
+
+                       gamedata.servername                     = menu.favorites[fav_idx].name
+                       gamedata.serverdescription      = menu.favorites[fav_idx].description
+               else
+                       gamedata.servername                     = ""
+                       gamedata.serverdescription      = ""
+               end
+
+               gamedata.selected_world = 0
+
+               engine.setting_set("address",fields["te_address"])
+               engine.setting_set("remote_port",fields["te_port"])
+
+               engine.start()
+               return
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_server_buttons(fields)
+
+       local world_doubleclick = false
+
+       if fields["srv_worlds"] ~= nil then
+               local event = engine.explode_textlist_event(fields["srv_worlds"])
+
+               if event.type == "DCL" then
+                       world_doubleclick = true
+               end
+               if event.type == "CHG" then
+                       engine.setting_set("mainmenu_last_selected_world",
+                               filterlist.get_raw_index(worldlist,engine.get_textlist_index("srv_worlds")))
+               end
+       end
+
+       menu.handle_key_up_down(fields,"srv_worlds","mainmenu_last_selected_world")
+
+       if fields["cb_creative_mode"] then
+               engine.setting_set("creative_mode", fields["cb_creative_mode"])
+       end
+
+       if fields["cb_enable_damage"] then
+               engine.setting_set("enable_damage", fields["cb_enable_damage"])
+       end
+
+       if fields["cb_server_announce"] then
+               engine.setting_set("server_announce", fields["cb_server_announce"])
+       end
+
+       if fields["start_server"] ~= nil or
+               world_doubleclick or
+               fields["key_enter"] then
+               local selected = engine.get_textlist_index("srv_worlds")
+               if selected ~= nil then
+                       gamedata.playername             = fields["te_playername"]
+                       gamedata.password               = fields["te_passwd"]
+                       gamedata.port                   = fields["te_serverport"]
+                       gamedata.address                = ""
+                       gamedata.selected_world = filterlist.get_raw_index(worldlist,selected)
+
+                       engine.setting_set("port",gamedata.port)
+                       if fields["te_serveraddr"] ~= nil then
+                               engine.setting_set("bind_address",fields["te_serveraddr"])
+                       end
+
+                       menu.update_last_game(gamedata.selected_world)
+                       engine.start()
+               end
+       end
+
+       if fields["world_create"] ~= nil then
+               tabbuilder.current_tab = "dialog_create_world"
+               tabbuilder.is_dialog = true
+               tabbuilder.show_buttons = false
+       end
+
+       if fields["world_delete"] ~= nil then
+               local selected = engine.get_textlist_index("srv_worlds")
+               if selected ~= nil and
+                       selected <= filterlist.size(worldlist) then
+                       local world = filterlist.get_list(worldlist)[selected]
+                       if world ~= nil and
+                               world.name ~= nil and
+                               world.name ~= "" then
+                               menu.world_to_del = filterlist.get_raw_index(worldlist,selected)
+                               tabbuilder.current_tab = "dialog_delete_world"
+                               tabbuilder.is_dialog = true
+                               tabbuilder.show_buttons = false
+                       else
+                               menu.world_to_del = 0
+                       end
+               end
+       end
+
+       if fields["world_configure"] ~= nil then
+               selected = engine.get_textlist_index("srv_worlds")
+               if selected ~= nil then
+                       modmgr.world_config_selected_world = filterlist.get_raw_index(worldlist,selected)
+                       if modmgr.init_worldconfig() then
+                               tabbuilder.current_tab = "dialog_configure_world"
+                               tabbuilder.is_dialog = true
+                               tabbuilder.show_buttons = false
+                       end
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_settings_buttons(fields)
+       if fields["cb_fancy_trees"] then
+               engine.setting_set("new_style_leaves", fields["cb_fancy_trees"])
+       end
+       if fields["cb_smooth_lighting"] then
+               engine.setting_set("smooth_lighting", fields["cb_smooth_lighting"])
+       end
+       if fields["cb_3d_clouds"] then
+               engine.setting_set("enable_3d_clouds", fields["cb_3d_clouds"])
+       end
+       if fields["cb_opaque_water"] then
+               engine.setting_set("opaque_water", fields["cb_opaque_water"])
+       end
+
+       if fields["cb_mipmapping"] then
+               engine.setting_set("mip_map", fields["cb_mipmapping"])
+       end
+       if fields["cb_anisotrophic"] then
+               engine.setting_set("anisotropic_filter", fields["cb_anisotrophic"])
+       end
+       if fields["cb_bilinear"] then
+               engine.setting_set("bilinear_filter", fields["cb_bilinear"])
+       end
+       if fields["cb_trilinear"] then
+               engine.setting_set("trilinear_filter", fields["cb_trilinear"])
+       end
+
+       if fields["cb_shaders"] then
+               if (engine.setting_get("video_driver") == "direct3d8" or engine.setting_get("video_driver") == "direct3d9") then
+                       engine.setting_set("enable_shaders", "false")
+                       gamedata.errormessage = fgettext("To enable shaders the OpenGL driver needs to be used.")
+               else
+                       engine.setting_set("enable_shaders", fields["cb_shaders"])
+               end
+       end
+       if fields["cb_pre_ivis"] then
+               engine.setting_set("preload_item_visuals", fields["cb_pre_ivis"])
+       end
+       if fields["cb_particles"] then
+               engine.setting_set("enable_particles", fields["cb_particles"])
+       end
+       if fields["cb_bumpmapping"] then
+               engine.setting_set("enable_bumpmapping", fields["cb_bumpmapping"])
+       end
+       if fields["cb_parallax"] then
+               engine.setting_set("enable_parallax_occlusion", fields["cb_parallax"])
+       end
+       if fields["cb_generate_normalmaps"] then
+               engine.setting_set("generate_normalmaps", fields["cb_generate_normalmaps"])
+       end
+       if fields["cb_waving_water"] then
+               engine.setting_set("enable_waving_water", fields["cb_waving_water"])
+       end
+       if fields["cb_waving_leaves"] then
+               engine.setting_set("enable_waving_leaves", fields["cb_waving_leaves"])
+       end
+       if fields["cb_waving_plants"] then
+               engine.setting_set("enable_waving_plants", fields["cb_waving_plants"])
+       end
+       if fields["btn_change_keys"] ~= nil then
+               engine.show_keys_menu()
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_singleplayer_buttons(fields)
+
+       local world_doubleclick = false
+
+       if fields["sp_worlds"] ~= nil then
+               local event = engine.explode_textlist_event(fields["sp_worlds"])
+
+               if event.type == "DCL" then
+                       world_doubleclick = true
+               end
+
+               if event.type == "CHG" then
+                       engine.setting_set("mainmenu_last_selected_world",
+                               filterlist.get_raw_index(worldlist,engine.get_textlist_index("sp_worlds")))
+               end
+       end
+
+       menu.handle_key_up_down(fields,"sp_worlds","mainmenu_last_selected_world")
+
+       if fields["cb_creative_mode"] then
+               engine.setting_set("creative_mode", fields["cb_creative_mode"])
+       end
+
+       if fields["cb_enable_damage"] then
+               engine.setting_set("enable_damage", fields["cb_enable_damage"])
+       end
+
+       if fields["play"] ~= nil or
+               world_doubleclick or
+               fields["key_enter"] then
+               local selected = engine.get_textlist_index("sp_worlds")
+               if selected ~= nil then
+                       gamedata.selected_world = filterlist.get_raw_index(worldlist,selected)
+                       gamedata.singleplayer   = true
+
+                       menu.update_last_game(gamedata.selected_world)
+
+                       engine.start()
+               end
+       end
+
+       if fields["world_create"] ~= nil then
+               tabbuilder.current_tab = "dialog_create_world"
+               tabbuilder.is_dialog = true
+               tabbuilder.show_buttons = false
+       end
+
+       if fields["world_delete"] ~= nil then
+               local selected = engine.get_textlist_index("sp_worlds")
+               if selected ~= nil and
+                       selected <= filterlist.size(worldlist) then
+                       local world = filterlist.get_list(worldlist)[selected]
+                       if world ~= nil and
+                               world.name ~= nil and
+                               world.name ~= "" then
+                               menu.world_to_del = filterlist.get_raw_index(worldlist,selected)
+                               tabbuilder.current_tab = "dialog_delete_world"
+                               tabbuilder.is_dialog = true
+                               tabbuilder.show_buttons = false
+                       else
+                               menu.world_to_del = 0
+                       end
+               end
+       end
+
+       if fields["world_configure"] ~= nil then
+               selected = engine.get_textlist_index("sp_worlds")
+               if selected ~= nil then
+                       modmgr.world_config_selected_world = filterlist.get_raw_index(worldlist,selected)
+                       if modmgr.init_worldconfig() then
+                               tabbuilder.current_tab = "dialog_configure_world"
+                               tabbuilder.is_dialog = true
+                               tabbuilder.show_buttons = false
+                       end
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_texture_pack_buttons(fields)
+       if fields["TPs"] ~= nil then
+               local event = engine.explode_textlist_event(fields["TPs"])
+               if event.type == "CHG" or event.type == "DCL" then
+                       local index = engine.get_textlist_index("TPs")
+                       engine.setting_set("mainmenu_last_selected_TP",
+                               index)
+                       local list = filter_texture_pack_list(engine.get_dirlist(engine.get_texturepath(), true))
+                       local current_index = engine.get_textlist_index("TPs")
+                       if current_index ~= nil and #list >= current_index then
+                               local new_path = engine.get_texturepath()..DIR_DELIM..list[current_index]
+                               if list[current_index] == "None" then new_path = "" end
+
+                               engine.setting_set("texture_path", new_path)
+                       end
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_header()
+
+       if tabbuilder.last_tab_index == nil then
+               tabbuilder.last_tab_index = 1
+       end
+
+       local toadd = ""
+
+       for i=1,#tabbuilder.current_buttons,1 do
+
+               if toadd ~= "" then
+                       toadd = toadd .. ","
+               end
+
+               toadd = toadd .. tabbuilder.current_buttons[i].caption
+       end
+       return "tabheader[-0.3,-0.99;main_tab;" .. toadd ..";" .. tabbuilder.last_tab_index .. ";true;false]"
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.handle_tab_buttons(fields)
+
+       if fields["main_tab"] then
+               local index = tonumber(fields["main_tab"])
+               tabbuilder.last_tab_index = index
+               tabbuilder.current_tab = tabbuilder.current_buttons[index].name
+
+               engine.setting_set("main_menu_tab",tabbuilder.current_tab)
+       end
+
+       --handle tab changes
+       if tabbuilder.current_tab ~= tabbuilder.old_tab then
+               if tabbuilder.current_tab ~= "singleplayer" and not tabbuilder.is_dialog then
+                       menu.update_gametype(true)
+               end
+       end
+
+       if tabbuilder.current_tab == "singleplayer" then
+               menu.update_gametype()
+       end
+
+       tabbuilder.old_tab = tabbuilder.current_tab
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_multiplayer()
+
+       local retval =
+               "vertlabel[0,-0.25;".. fgettext("CLIENT") .. "]" ..
+               "label[1,-0.25;".. fgettext("Favorites:") .. "]"..
+               "label[1,4.25;".. fgettext("Address/Port") .. "]"..
+               "label[9,2.75;".. fgettext("Name/Password") .. "]" ..
+               "field[1.25,5.25;5.5,0.5;te_address;;" ..engine.setting_get("address") .."]" ..
+               "field[6.75,5.25;2.25,0.5;te_port;;" ..engine.setting_get("remote_port") .."]" ..
+               "checkbox[1,3.6;cb_public_serverlist;".. fgettext("Public Serverlist") .. ";" ..
+               dump(engine.setting_getbool("public_serverlist")) .. "]"
+
+       if not engine.setting_getbool("public_serverlist") then
+               retval = retval ..
+               "button[6.45,3.95;2.25,0.5;btn_delete_favorite;".. fgettext("Delete") .. "]"
+       end
+
+       retval = retval ..
+               "button[9,4.95;2.5,0.5;btn_mp_connect;".. fgettext("Connect") .. "]" ..
+               "field[9.3,3.75;2.5,0.5;te_name;;" ..engine.setting_get("name") .."]" ..
+               "pwdfield[9.3,4.5;2.5,0.5;te_pwd;]" ..
+               "textarea[9.3,0.25;2.5,2.75;;"
+       if menu.fav_selected ~= nil and
+               menu.favorites[menu.fav_selected].description ~= nil then
+               retval = retval ..
+                       engine.formspec_escape(menu.favorites[menu.fav_selected].description,true)
+       end
+
+       retval = retval ..
+               ";]" ..
+               "textlist[1,0.35;7.5,3.35;favourites;"
+
+       local render_details = engine.setting_getbool("public_serverlist")
+
+       if #menu.favorites > 0 then
+               retval = retval .. menu.render_favorite(menu.favorites[1],render_details)
+
+               for i=2,#menu.favorites,1 do
+                       retval = retval .. "," .. menu.render_favorite(menu.favorites[i],render_details)
+               end
+       end
+
+       if menu.fav_selected ~= nil then
+               retval = retval .. ";" .. menu.fav_selected .. "]"
+       else
+               retval = retval .. ";0]"
+       end
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_server()
+
+       local index = filterlist.get_current_index(worldlist,
+                               tonumber(engine.setting_get("mainmenu_last_selected_world"))
+                               )
+
+       local retval =
+               "button[4,4.15;2.6,0.5;world_delete;".. fgettext("Delete") .. "]" ..
+               "button[6.5,4.15;2.8,0.5;world_create;".. fgettext("New") .. "]" ..
+               "button[9.2,4.15;2.55,0.5;world_configure;".. fgettext("Configure") .. "]" ..
+               "button[8.5,4.9;3.25,0.5;start_server;".. fgettext("Start Game") .. "]" ..
+               "label[4,-0.25;".. fgettext("Select World:") .. "]"..
+               "vertlabel[0,-0.25;".. fgettext("START SERVER") .. "]" ..
+               "checkbox[0.5,0.25;cb_creative_mode;".. fgettext("Creative Mode") .. ";" ..
+               dump(engine.setting_getbool("creative_mode")) .. "]"..
+               "checkbox[0.5,0.7;cb_enable_damage;".. fgettext("Enable Damage") .. ";" ..
+               dump(engine.setting_getbool("enable_damage")) .. "]"..
+               "checkbox[0.5,1.15;cb_server_announce;".. fgettext("Public") .. ";" ..
+               dump(engine.setting_getbool("server_announce")) .. "]"..
+               "field[0.8,3.2;3.5,0.5;te_playername;".. fgettext("Name") .. ";" ..
+               engine.setting_get("name") .. "]" ..
+               "pwdfield[0.8,4.2;3.5,0.5;te_passwd;".. fgettext("Password") .. "]"
+               
+       local bind_addr = engine.setting_get("bind_address")
+       if bind_addr ~= nil and bind_addr ~= "" then
+               retval = retval ..
+                       "field[0.8,5.2;2.25,0.5;te_serveraddr;".. fgettext("Bind Address") .. ";" ..
+                       engine.setting_get("bind_address") .."]" ..
+                       "field[3.05,5.2;1.25,0.5;te_serverport;".. fgettext("Port") .. ";" ..
+                       engine.setting_get("port") .."]"
+       else
+               retval = retval ..
+                       "field[0.8,5.2;3.5,0.5;te_serverport;".. fgettext("Server Port") .. ";" ..
+                       engine.setting_get("port") .."]"
+       end
+       
+       retval = retval ..
+               "textlist[4,0.25;7.5,3.7;srv_worlds;" ..
+               menu.render_world_list() ..
+               ";" .. index .. "]"
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_settings()
+       tab_string =
+                       "vertlabel[0,0;" .. fgettext("SETTINGS") .. "]" ..
+                       "checkbox[1,0;cb_fancy_trees;".. fgettext("Fancy Trees") .. ";"
+                                       .. dump(engine.setting_getbool("new_style_leaves")) .. "]"..
+                       "checkbox[1,0.5;cb_smooth_lighting;".. fgettext("Smooth Lighting")
+                                       .. ";".. dump(engine.setting_getbool("smooth_lighting")) .. "]"..
+                       "checkbox[1,1;cb_3d_clouds;".. fgettext("3D Clouds") .. ";"
+                                       .. dump(engine.setting_getbool("enable_3d_clouds")) .. "]"..
+                       "checkbox[1,1.5;cb_opaque_water;".. fgettext("Opaque Water") .. ";"
+                                       .. dump(engine.setting_getbool("opaque_water")) .. "]"..
+                       "checkbox[1,2.0;cb_pre_ivis;".. fgettext("Preload item visuals") .. ";"
+                                       .. dump(engine.setting_getbool("preload_item_visuals")) .. "]"..
+                       "checkbox[1,2.5;cb_particles;".. fgettext("Enable Particles") .. ";"
+                                       .. dump(engine.setting_getbool("enable_particles"))     .. "]"..
+                       "checkbox[4.5,0;cb_mipmapping;".. fgettext("Mip-Mapping") .. ";"
+                                       .. dump(engine.setting_getbool("mip_map")) .. "]"..
+                       "checkbox[4.5,0.5;cb_anisotrophic;".. fgettext("Anisotropic Filtering") .. ";"
+                                       .. dump(engine.setting_getbool("anisotropic_filter")) .. "]"..
+                       "checkbox[4.5,1.0;cb_bilinear;".. fgettext("Bi-Linear Filtering") .. ";"
+                                       .. dump(engine.setting_getbool("bilinear_filter")) .. "]"..
+                       "checkbox[4.5,1.5;cb_trilinear;".. fgettext("Tri-Linear Filtering") .. ";"
+                                       .. dump(engine.setting_getbool("trilinear_filter")) .. "]"..
+
+                       "checkbox[8,0;cb_shaders;".. fgettext("Shaders") .. ";"
+                                       .. dump(engine.setting_getbool("enable_shaders")) .. "]"..
+                       "button[1,4.5;2.25,0.5;btn_change_keys;".. fgettext("Change keys") .. "]"
+
+       if engine.setting_getbool("enable_shaders") then
+               tab_string = tab_string ..
+                       "checkbox[8,0.5;cb_bumpmapping;".. fgettext("Bumpmapping") .. ";"
+                                       .. dump(engine.setting_getbool("enable_bumpmapping")) .. "]"..
+                       "checkbox[8,1.0;cb_parallax;".. fgettext("Parallax Occlusion") .. ";"
+                                       .. dump(engine.setting_getbool("enable_parallax_occlusion")) .. "]"..
+                       "checkbox[8,1.5;cb_generate_normalmaps;".. fgettext("Generate Normalmaps") .. ";"
+                                       .. dump(engine.setting_getbool("generate_normalmaps")) .. "]"..
+                       "checkbox[8,2.0;cb_waving_water;".. fgettext("Waving Water") .. ";"
+                                       .. dump(engine.setting_getbool("enable_waving_water")) .. "]"..
+                       "checkbox[8,2.5;cb_waving_leaves;".. fgettext("Waving Leaves") .. ";"
+                                       .. dump(engine.setting_getbool("enable_waving_leaves")) .. "]"..
+                       "checkbox[8,3.0;cb_waving_plants;".. fgettext("Waving Plants") .. ";"
+                                       .. dump(engine.setting_getbool("enable_waving_plants")) .. "]"
+       else
+               tab_string = tab_string ..
+                       "textlist[8.33,0.7;4,1;;#888888" .. fgettext("Bumpmapping") .. ";0;true]" ..
+                       "textlist[8.33,1.2;4,1;;#888888" .. fgettext("Parallax Occlusion") .. ";0;true]" ..
+                       "textlist[8.33,1.7;4,1;;#888888" .. fgettext("Generate Normalmaps") .. ";0;true]" ..
+                       "textlist[8.33,2.2;4,1;;#888888" .. fgettext("Waving Water") .. ";0;true]" ..
+                       "textlist[8.33,2.7;4,1;;#888888" .. fgettext("Waving Leaves") .. ";0;true]" ..
+                       "textlist[8.33,3.2;4,1;;#888888" .. fgettext("Waving Plants") .. ";0;true]"
+       end
+       return tab_string
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_singleplayer()
+
+       local index = filterlist.get_current_index(worldlist,
+                               tonumber(engine.setting_get("mainmenu_last_selected_world"))
+                               )
+
+       return  "button[4,4.15;2.6,0.5;world_delete;".. fgettext("Delete") .. "]" ..
+                       "button[6.5,4.15;2.8,0.5;world_create;".. fgettext("New") .. "]" ..
+                       "button[9.2,4.15;2.55,0.5;world_configure;".. fgettext("Configure") .. "]" ..
+                       "button[8.5,4.95;3.25,0.5;play;".. fgettext("Play") .. "]" ..
+                       "label[4,-0.25;".. fgettext("Select World:") .. "]"..
+                       "vertlabel[0,-0.25;".. fgettext("SINGLE PLAYER") .. "]" ..
+                       "checkbox[0.5,0.25;cb_creative_mode;".. fgettext("Creative Mode") .. ";" ..
+                       dump(engine.setting_getbool("creative_mode")) .. "]"..
+                       "checkbox[0.5,0.7;cb_enable_damage;".. fgettext("Enable Damage") .. ";" ..
+                       dump(engine.setting_getbool("enable_damage")) .. "]"..
+                       "textlist[4,0.25;7.5,3.7;sp_worlds;" ..
+                       menu.render_world_list() ..
+                       ";" .. index .. "]" ..
+                       menubar.formspec
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_texture_packs()
+       local retval = "label[4,-0.25;".. fgettext("Select texture pack:") .. "]"..
+                       "vertlabel[0,-0.25;".. fgettext("TEXTURE PACKS") .. "]" ..
+                       "textlist[4,0.25;7.5,5.0;TPs;"
+
+       local current_texture_path = engine.setting_get("texture_path")
+       local list = filter_texture_pack_list(engine.get_dirlist(engine.get_texturepath(), true))
+       local index = tonumber(engine.setting_get("mainmenu_last_selected_TP"))
+
+       if index == nil then index = 1 end
+
+       if current_texture_path == "" then
+               retval = retval ..
+                       menu.render_texture_pack_list(list) ..
+                       ";" .. index .. "]"
+               return retval
+       end
+
+       local infofile = current_texture_path ..DIR_DELIM.."info.txt"
+       local infotext = ""
+       local f = io.open(infofile, "r")
+       if f==nil then
+               infotext = fgettext("No information available")
+       else
+               infotext = f:read("*all")
+               f:close()
+       end
+
+       local screenfile = current_texture_path..DIR_DELIM.."screenshot.png"
+       local no_screenshot = nil
+       if not file_exists(screenfile) then
+               screenfile = nil
+               no_screenshot = menu.defaulttexturedir .. "no_screenshot.png"
+       end
+
+       return  retval ..
+                       menu.render_texture_pack_list(list) ..
+                       ";" .. index .. "]" ..
+                       "image[0.65,0.25;4.0,3.7;"..engine.formspec_escape(screenfile or no_screenshot).."]"..
+                       "textarea[1.0,3.25;3.7,1.5;;"..engine.formspec_escape(infotext or "")..";]"
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.tab_credits()
+       local logofile = menu.defaulttexturedir .. "logo.png"
+       return  "vertlabel[0,-0.5;CREDITS]" ..
+                       "label[0.5,3;Minetest " .. engine.get_version() .. "]" ..
+                       "label[0.5,3.3;http://minetest.net]" ..
+                       "image[0.5,1;" .. engine.formspec_escape(logofile) .. "]" ..
+                       "textlist[3.5,-0.25;8.5,5.8;list_credits;" ..
+                       "#FFFF00" .. fgettext("Core Developers") .."," ..
+                       "Perttu Ahola (celeron55) <celeron55@gmail.com>,"..
+                       "Ryan Kwolek (kwolekr) <kwolekr@minetest.net>,"..
+                       "PilzAdam <pilzadam@minetest.net>," ..
+                       "Ilya Zhuravlev (xyz) <xyz@minetest.net>,"..
+                       "Lisa Milne (darkrose) <lisa@ltmnet.com>,"..
+                       "Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>,"..
+                       "proller <proler@gmail.com>,"..
+                       "sfan5 <sfan5@live.de>,"..
+                       "kahrl <kahrl@gmx.net>,"..
+                       "sapier,"..
+                       "ShadowNinja <shadowninja@minetest.net>,"..
+                       "Nathanael Courant (Nore/Novatux) <nore@mesecons.net>,"..
+                       "BlockMen,"..
+                       ","..
+                       "#FFFF00" .. fgettext("Active Contributors") .. "," ..
+                       "Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>,"..
+                       "Jurgen Doser (doserj) <jurgen.doser@gmail.com>,"..
+                       "Jeija <jeija@mesecons.net>,"..
+                       "MirceaKitsune <mirceakitsune@gmail.com>,"..
+                       "dannydark <the_skeleton_of_a_child@yahoo.co.uk>,"..
+                       "0gb.us <0gb.us@0gb.us>,"..
+                       "," ..
+                       "#FFFF00" .. fgettext("Previous Contributors") .. "," ..
+                       "Guiseppe Bilotta (Oblomov) <guiseppe.bilotta@gmail.com>,"..
+                       "Jonathan Neuschafer <j.neuschaefer@gmx.net>,"..
+                       "Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net>,"..
+                       "Constantin Wenger (SpeedProg) <constantin.wenger@googlemail.com>,"..
+                       "matttpt <matttpt@gmail.com>,"..
+                       "JacobF <queatz@gmail.com>,"..
+                       ";0;true]"
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.init()
+       tabbuilder.tabfuncs = {
+               singleplayer  = tabbuilder.tab_singleplayer,
+               multiplayer   = tabbuilder.tab_multiplayer,
+               server        = tabbuilder.tab_server,
+               settings      = tabbuilder.tab_settings,
+               texture_packs = tabbuilder.tab_texture_packs,
+               credits       = tabbuilder.tab_credits,
+               dialog_create_world = tabbuilder.dialog_create_world,
+               dialog_delete_world = tabbuilder.dialog_delete_world
+       }
+
+       tabbuilder.tabsizes = {
+               dialog_create_world = {width=12, height=7},
+               dialog_delete_world = {width=12, height=5.2}
+       }
+
+       tabbuilder.current_tab = engine.setting_get("main_menu_tab")
+
+       if tabbuilder.current_tab == nil or
+               tabbuilder.current_tab == "" then
+               tabbuilder.current_tab = "singleplayer"
+               engine.setting_set("main_menu_tab",tabbuilder.current_tab)
+       end
+
+       --initialize tab buttons
+       tabbuilder.last_tab = nil
+       tabbuilder.show_buttons = true
+
+       tabbuilder.current_buttons = {}
+       table.insert(tabbuilder.current_buttons,{name="singleplayer", caption=fgettext("Singleplayer")})
+       table.insert(tabbuilder.current_buttons,{name="multiplayer", caption=fgettext("Client")})
+       table.insert(tabbuilder.current_buttons,{name="server", caption=fgettext("Server")})
+       table.insert(tabbuilder.current_buttons,{name="settings", caption=fgettext("Settings")})
+       table.insert(tabbuilder.current_buttons,{name="texture_packs", caption=fgettext("Texture Packs")})
+
+       if engine.setting_getbool("main_menu_game_mgr") then
+               table.insert(tabbuilder.current_buttons,{name="game_mgr", caption=fgettext("Games")})
+       end
+
+       if engine.setting_getbool("main_menu_mod_mgr") then
+               table.insert(tabbuilder.current_buttons,{name="mod_mgr", caption=fgettext("Mods")})
+       end
+       table.insert(tabbuilder.current_buttons,{name="credits", caption=fgettext("Credits")})
+
+
+       for i=1,#tabbuilder.current_buttons,1 do
+               if tabbuilder.current_buttons[i].name == tabbuilder.current_tab then
+                       tabbuilder.last_tab_index = i
+               end
+       end
+
+       if tabbuilder.current_tab ~= "singleplayer" then
+               menu.update_gametype(true)
+       else
+               menu.update_gametype()
+       end
+end
+
+--------------------------------------------------------------------------------
+function tabbuilder.checkretval(retval)
+
+       if retval ~= nil then
+               if retval.current_tab ~= nil then
+                       tabbuilder.current_tab = retval.current_tab
+               end
+
+               if retval.is_dialog ~= nil then
+                       tabbuilder.is_dialog = retval.is_dialog
+               end
+
+               if retval.show_buttons ~= nil then
+                       tabbuilder.show_buttons = retval.show_buttons
+               end
+
+               if retval.skipformupdate ~= nil then
+                       tabbuilder.skipformupdate = retval.skipformupdate
+               end
+
+               if retval.ignore_menu_quit == true then
+                       tabbuilder.ignore_menu_quit = true
+               else
+                       tabbuilder.ignore_menu_quit = false
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+-- initialize callbacks
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+engine.button_handler = function(fields)
+       --print("Buttonhandler: tab: " .. tabbuilder.current_tab .. " fields: " .. dump(fields))
+
+       if fields["btn_error_confirm"] then
+               gamedata.errormessage = nil
+       end
+
+       local retval = modmgr.handle_buttons(tabbuilder.current_tab,fields)
+       tabbuilder.checkretval(retval)
+
+       retval = gamemgr.handle_buttons(tabbuilder.current_tab,fields)
+       tabbuilder.checkretval(retval)
+
+       retval = modstore.handle_buttons(tabbuilder.current_tab,fields)
+       tabbuilder.checkretval(retval)
+
+       if tabbuilder.current_tab == "dialog_create_world" then
+               tabbuilder.handle_create_world_buttons(fields)
+       end
+
+       if tabbuilder.current_tab == "dialog_delete_world" then
+               tabbuilder.handle_delete_world_buttons(fields)
+       end
+
+       if tabbuilder.current_tab == "singleplayer" then
+               tabbuilder.handle_singleplayer_buttons(fields)
+       end
+
+       if tabbuilder.current_tab == "texture_packs" then
+               tabbuilder.handle_texture_pack_buttons(fields)
+       end
+
+       if tabbuilder.current_tab == "multiplayer" then
+               tabbuilder.handle_multiplayer_buttons(fields)
+       end
+
+       if tabbuilder.current_tab == "settings" then
+               tabbuilder.handle_settings_buttons(fields)
+       end
+
+       if tabbuilder.current_tab == "server" then
+               tabbuilder.handle_server_buttons(fields)
+       end
+
+       --tab buttons
+       tabbuilder.handle_tab_buttons(fields)
+
+       --menubar buttons
+       menubar.handle_buttons(fields)
+
+       if not tabbuilder.skipformupdate then
+               --update menu
+               update_menu()
+       else
+               tabbuilder.skipformupdate = false
+       end
+end
+
+--------------------------------------------------------------------------------
+engine.event_handler = function(event)
+       if event == "MenuQuit" then
+               if tabbuilder.is_dialog then
+                       if tabbuilder.ignore_menu_quit then
+                               return
+                       end
+
+                       tabbuilder.is_dialog = false
+                       tabbuilder.show_buttons = true
+                       tabbuilder.current_tab = engine.setting_get("main_menu_tab")
+                       menu.update_gametype()
+                       update_menu()
+               else
+                       engine.close()
+               end
+       end
+
+       if event == "Refresh" then
+               update_menu()
+       end
+end
+
+--------------------------------------------------------------------------------
+function menu.update_gametype(reset)
+       local game = menu.lastgame()
+
+       if reset or game == nil then
+               mm_texture.reset()
+               engine.set_topleft_text("")
+               filterlist.set_filtercriteria(worldlist,nil)
+       else
+               mm_texture.update(tabbuilder.current_tab,game)
+               engine.set_topleft_text(game.name)
+               filterlist.set_filtercriteria(worldlist,game.id)
+       end
+end
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+-- menu startup
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+init_globals()
+mm_texture.init()
+menu.init()
+tabbuilder.init()
+menubar.refresh()
+modstore.init()
+
+engine.sound_play("main_menu", true)
+
+update_menu()
diff --git a/builtin/mainmenu/menubar.lua b/builtin/mainmenu/menubar.lua
new file mode 100644 (file)
index 0000000..2e4d5f8
--- /dev/null
@@ -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.
+
+menubar = {}
+
+--------------------------------------------------------------------------------
+function menubar.handle_buttons(fields)
+       for i=1,#menubar.buttons,1 do
+               if fields[menubar.buttons[i].btn_name] ~= nil then
+                       menu.last_game = menubar.buttons[i].index
+                       engine.setting_set("main_menu_last_game_idx",menu.last_game)
+                       menu.update_gametype()
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+function menubar.refresh()
+
+       menubar.formspec = "box[-0.3,5.625;12.4,1.2;#000000]" ..
+                                          "box[-0.3,5.6;12.4,0.05;#FFFFFF]"
+       menubar.buttons = {}
+
+       local button_base = -0.08
+       
+       local maxbuttons = #gamemgr.games
+       
+       if maxbuttons > 11 then
+               maxbuttons = 11
+       end
+       
+       for i=1,maxbuttons,1 do
+
+               local btn_name = "menubar_btn_" .. gamemgr.games[i].id
+               local buttonpos = button_base + (i-1) * 1.1
+               if gamemgr.games[i].menuicon_path ~= nil and
+                       gamemgr.games[i].menuicon_path ~= "" then
+
+                       menubar.formspec = menubar.formspec ..
+                               "image_button[" .. buttonpos ..  ",5.72;1.165,1.175;"  ..
+                               engine.formspec_escape(gamemgr.games[i].menuicon_path) .. ";" ..
+                               btn_name .. ";;true;false]"
+               else
+               
+                       local part1 = gamemgr.games[i].id:sub(1,5)
+                       local part2 = gamemgr.games[i].id:sub(6,10)
+                       local part3 = gamemgr.games[i].id:sub(11)
+                       
+                       local text = part1 .. "\n" .. part2
+                       if part3 ~= nil and
+                               part3 ~= "" then
+                               text = text .. "\n" .. part3
+                       end
+                       menubar.formspec = menubar.formspec ..
+                               "image_button[" .. buttonpos ..  ",5.72;1.165,1.175;;" ..btn_name ..
+                               ";" .. text .. ";true;true]"
+               end
+               
+               local toadd = {
+                       btn_name = btn_name,
+                       index = i,
+               }
+               
+               table.insert(menubar.buttons,toadd)
+       end
+end
diff --git a/builtin/mainmenu/modmgr.lua b/builtin/mainmenu/modmgr.lua
new file mode 100644 (file)
index 0000000..eeb65ad
--- /dev/null
@@ -0,0 +1,1130 @@
+--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,modpack)
+
+       local mods = engine.get_dirlist(path,true)
+       for i=1,#mods,1 do
+               local toadd = {}
+               local modpackfile = nil
+
+               toadd.name              = mods[i]
+               toadd.path              = path .. DIR_DELIM .. mods[i] .. DIR_DELIM
+               if modpack ~= nil and
+                       modpack ~= "" then
+                       toadd.modpack   = modpack
+               else
+                       local filename = path .. DIR_DELIM .. mods[i] .. DIR_DELIM .. "modpack.txt"
+                       local error = nil
+                       modpackfile,error = io.open(filename,"r")
+               end
+
+               if modpackfile ~= nil then
+                       modpackfile:close()
+                       toadd.is_modpack = true
+                       table.insert(retval,toadd)
+                       get_mods(path .. DIR_DELIM .. mods[i],retval,mods[i])
+               else
+                       table.insert(retval,toadd)
+               end
+       end
+end
+
+--modmanager implementation
+modmgr = {}
+
+--------------------------------------------------------------------------------
+function modmgr.extract(modfile)
+       if modfile.type == "zip" then
+               local tempfolder = os.tempfolder()
+
+               if tempfolder ~= nil and
+                       tempfolder ~= "" then
+                       engine.create_dir(tempfolder)
+                       if engine.extract_zip(modfile.name,tempfolder) then
+                               return tempfolder
+                       end
+               end
+       end
+       return nil
+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
+                               local retval = item:sub(1,pos3-1)
+                               if retval ~= nil and
+                                       retval ~= "" then
+                                       return retval
+                               end
+                       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.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.global_mods == nil then
+               modmgr.refresh_globals()
+       end
+
+       if modmgr.selected_mod == nil then
+               modmgr.selected_mod = 1
+       end
+
+       local retval =
+               "vertlabel[0,-0.25;".. fgettext("MODS") .. "]" ..
+               "label[0.8,-0.25;".. fgettext("Installed Mods:") .. "]" ..
+               "textlist[0.75,0.25;4.5,4;modlist;" ..
+               modmgr.render_modlist(modmgr.global_mods) ..
+               ";" .. modmgr.selected_mod .. "]"
+
+       retval = retval ..
+               "label[0.8,4.2;" .. fgettext("Add mod:") .. "]" ..
+--             TODO Disabled due to upcoming release 0.4.8 and irrlicht messing up localization
+--             "button[0.75,4.85;1.8,0.5;btn_mod_mgr_install_local;".. fgettext("Local install") .. "]" ..
+               "button[2.45,4.85;3.05,0.5;btn_mod_mgr_download;".. fgettext("Online mod repository") .. "]"
+
+       local selected_mod = nil
+
+       if filterlist.size(modmgr.global_mods) >= modmgr.selected_mod then
+               selected_mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
+       end
+
+       if selected_mod ~= nil then
+               local modscreenshot = nil
+
+               --check for screenshot beeing available
+               local screenshotfilename = selected_mod.path .. DIR_DELIM .. "screenshot.png"
+               local error = nil
+               screenshotfile,error = io.open(screenshotfilename,"r")
+               if error == nil then
+                       screenshotfile:close()
+                       modscreenshot = screenshotfilename
+               end
+
+               if modscreenshot == nil then
+                               modscreenshot = modstore.basetexturedir .. "no_screenshot.png"
+               end
+
+               retval = retval
+                               .. "image[5.5,0;3,2;" .. engine.formspec_escape(modscreenshot) .. "]"
+                               .. "label[8.25,0.6;" .. selected_mod.name .. "]"
+
+               local descriptionlines = nil
+               error = nil
+               local descriptionfilename = selected_mod.path .. "description.txt"
+               descriptionfile,error = io.open(descriptionfilename,"r")
+               if error == nil then
+                       descriptiontext = descriptionfile:read("*all")
+
+                       descriptionlines = engine.splittext(descriptiontext,42)
+                       descriptionfile:close()
+               else
+                       descriptionlines = {}
+                       table.insert(descriptionlines,fgettext("No mod description available"))
+               end
+
+               retval = retval ..
+                       "label[5.5,1.7;".. fgettext("Mod information:") .. "]" ..
+                       "textlist[5.5,2.2;6.2,2.4;description;"
+
+               for i=1,#descriptionlines,1 do
+                       retval = retval .. engine.formspec_escape(descriptionlines[i]) .. ","
+               end
+
+
+               if selected_mod.is_modpack then
+                       retval = retval .. ";0]" ..
+                               "button[10,4.85;2,0.5;btn_mod_mgr_rename_modpack;" ..
+                               fgettext("Rename") .. "]"
+                       retval = retval .. "button[5.5,4.85;4.5,0.5;btn_mod_mgr_delete_mod;"
+                               .. fgettext("Uninstall selected modpack") .. "]"
+               else
+                       --show dependencies
+
+                       retval = retval .. ",Depends:,"
+
+                       toadd = modmgr.get_dependencies(selected_mod.path)
+
+                       retval = retval .. toadd .. ";0]"
+
+                       retval = retval .. "button[5.5,4.85;4.5,0.5;btn_mod_mgr_delete_mod;"
+                               .. fgettext("Uninstall selected mod") .. "]"
+               end
+       end
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modmgr.dialog_rename_modpack()
+
+       local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
+
+       local retval =
+               "label[1.75,1;".. fgettext("Rename Modpack:") .. "]"..
+               "field[4.5,1.4;6,0.5;te_modpack_name;;" ..
+               mod.name ..
+               "]" ..
+               "button[5,4.2;2.6,0.5;dlg_rename_modpack_confirm;"..
+                               fgettext("Accept") .. "]" ..
+               "button[7.5,4.2;2.8,0.5;dlg_rename_modpack_cancel;"..
+                               fgettext("Cancel") .. "]"
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modmgr.precheck()
+
+       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
+
+       if modmgr.hide_modpackcontents == nil then
+               modmgr.hide_modpackcontents = true
+       end
+end
+
+--------------------------------------------------------------------------------
+function modmgr.render_modlist(render_list)
+       local retval = ""
+
+       if render_list == nil then
+               if modmgr.global_mods == nil then
+                       modmgr.refresh_globals()
+               end
+               render_list = modmgr.global_mods
+       end
+
+       local list = filterlist.get_list(render_list)
+       local last_modpack = nil
+
+       for i,v in ipairs(list) do
+               if retval ~= "" then
+                       retval = retval ..","
+               end
+
+               local color = ""
+
+               if v.is_modpack then
+                       local rawlist = filterlist.get_raw_list(render_list)
+
+                       local all_enabled = true
+                       for j=1,#rawlist,1 do
+                               if rawlist[j].modpack == list[i].name and
+                                       rawlist[j].enabled ~= true then
+                                               all_enabled = false
+                                               break
+                               end
+                       end
+
+                       if all_enabled == false then
+                               color = mt_color_grey
+                       else
+                               color = mt_color_dark_green
+                       end
+               end
+
+               if v.typ == "game_mod" then
+                       color = mt_color_blue
+               else
+                       if v.enabled then
+                               color = mt_color_green
+                       end
+               end
+
+               retval = retval .. color
+               if v.modpack  ~= nil then
+                       retval = retval .. "    "
+               end
+               retval = retval .. v.name
+       end
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modmgr.dialog_configure_world()
+       modmgr.precheck()
+
+       local worldspec = engine.get_worlds()[modmgr.world_config_selected_world]
+       local mod = filterlist.get_list(modmgr.modlist)[modmgr.world_config_selected_mod]
+
+       local retval =
+               "size[11,6.5,true]" ..
+               "label[0.5,-0.25;" .. fgettext("World:") .. "]" ..
+               "label[1.75,-0.25;" .. worldspec.name .. "]"
+
+       if modmgr.hide_gamemods then
+               retval = retval .. "checkbox[0,5.75;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";true]"
+       else
+               retval = retval .. "checkbox[0,5.75;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";false]"
+       end
+
+       if modmgr.hide_modpackcontents then
+               retval = retval .. "checkbox[2,5.75;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";true]"
+       else
+               retval = retval .. "checkbox[2,5.75;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";false]"
+       end
+
+       if mod == nil then
+               mod = {name=""}
+       end
+       retval = retval ..
+               "label[0,0.45;" .. fgettext("Mod:") .. "]" ..
+               "label[0.75,0.45;" .. mod.name .. "]" ..
+               "label[0,1;" .. fgettext("Depends:") .. "]" ..
+               "textlist[0,1.5;5,4.25;world_config_depends;" ..
+               modmgr.get_dependencies(mod.path) .. ";0]" ..
+               "button[9.25,6.35;2,0.5;btn_config_world_save;" .. fgettext("Save") .. "]" ..
+               "button[7.4,6.35;2,0.5;btn_config_world_cancel;" .. fgettext("Cancel") .. "]"
+
+       if mod ~= nil and mod.name ~= "" and mod.typ ~= "game_mod" then
+               if mod.is_modpack then
+                       local rawlist = filterlist.get_raw_list(modmgr.modlist)
+
+                       local all_enabled = true
+                       for j=1,#rawlist,1 do
+                               if rawlist[j].modpack == mod.name and
+                                       rawlist[j].enabled ~= true then
+                                               all_enabled = false
+                                               break
+                               end
+                       end
+
+                       if all_enabled == false then
+                               retval = retval .. "button[5.5,-0.125;2,0.5;btn_mp_enable;" .. fgettext("Enable MP") .. "]"
+                       else
+                               retval = retval .. "button[5.5,-0.125;2,0.5;btn_mp_disable;" .. fgettext("Disable MP") .. "]"
+                       end
+               else
+                       if mod.enabled then
+                               retval = retval .. "checkbox[5.5,-0.375;cb_mod_enable;" .. fgettext("enabled") .. ";true]"
+                       else
+                               retval = retval .. "checkbox[5.5,-0.375;cb_mod_enable;" .. fgettext("enabled") .. ";false]"
+                       end
+               end
+       end
+
+       retval = retval ..
+               "button[8.5,-0.125;2.5,0.5;btn_all_mods;" .. fgettext("Enable all") .. "]" ..
+               "textlist[5.5,0.5;5.5,5.75;world_config_modlist;"
+
+       retval = retval .. modmgr.render_modlist(modmgr.modlist)
+
+       retval = retval .. ";" .. modmgr.world_config_selected_mod .."]"
+
+       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_dependencies(modfolder)
+       local toadd = ""
+       if modfolder ~= nil then
+               local filename = modfolder ..
+                                       DIR_DELIM .. "depends.txt"
+
+               local dependencyfile = io.open(filename,"r")
+
+               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()
+               end
+       end
+
+       return toadd
+end
+
+
+--------------------------------------------------------------------------------
+function modmgr.get_worldconfig(worldpath)
+       local filename = worldpath ..
+                               DIR_DELIM .. "world.mt"
+
+       local worldfile = Settings(filename)
+
+       local worldconfig = {}
+       worldconfig.global_mods = {}
+       worldconfig.game_mods = {}
+
+       for key,value in pairs(worldfile:to_table()) do
+               if key == "gameid" then
+                       worldconfig.id = value
+               else
+                       worldconfig.global_mods[key] = engine.is_yes(value)
+               end
+       end
+
+       --read gamemods
+       local gamespec = gamemgr.find_by_gameid(worldconfig.id)
+       gamemgr.get_game_mods(gamespec, 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 = engine.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",fgettext("Select Mod File:"))
+       end
+
+       if fields["btn_mod_mgr_download"] ~= nil then
+               modstore.update_modlist()
+               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 = modmgr.identify_filetype(modfilename)
+       local modpath = modmgr.extract(modfile)
+
+       if modpath == nil then
+               gamedata.errormessage = fgettext("Install Mod: file: \"$1\"", modfile.name) ..
+                       fgettext("\nInstall Mod: unsupported filetype \"$1\" or broken archive", 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
+                       if not engine.copy_dir(basefolder.path,targetpath) then
+                               gamedata.errormessage = fgettext("Failed to install $1 to $2", basename, targetpath)
+                       end
+               else
+                       gamedata.errormessage = fgettext("Install Mod: unable to find suitable foldername for modpack $1", 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 = fgettext("Install Mod: unable to find real modname for: $1", modfilename)
+               end
+       end
+
+       engine.delete_dir(modpath)
+
+       modmgr.refresh_globals()
+
+end
+
+--------------------------------------------------------------------------------
+function modmgr.handle_rename_modpack_buttons(fields)
+
+       if fields["dlg_rename_modpack_confirm"] ~= nil then
+               local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
+               local oldpath = engine.get_modpath() .. DIR_DELIM .. mod.name
+               local targetpath = engine.get_modpath() .. DIR_DELIM .. fields["te_modpack_name"]
+               engine.copy_dir(oldpath,targetpath,false)
+               modmgr.refresh_globals()
+               modmgr.selected_mod = filterlist.get_current_index(modmgr.global_mods,
+                       filterlist.raw_index_by_uid(modmgr.global_mods, fields["te_modpack_name"]))
+       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 = engine.explode_textlist_event(fields["world_config_modlist"])
+               modmgr.world_config_selected_mod = event.index
+
+               if event.type == "DCL" then
+                       modmgr.world_config_enable_mod(nil)
+               end
+       end
+
+       if fields["key_enter"] ~= nil then
+               modmgr.world_config_enable_mod(nil)
+       end
+
+       if fields["cb_mod_enable"] ~= nil then
+               local toset = engine.is_yes(fields["cb_mod_enable"])
+               modmgr.world_config_enable_mod(toset)
+       end
+
+       if fields["btn_mp_enable"] ~= nil or
+               fields["btn_mp_disable"] then
+               local toset = (fields["btn_mp_enable"] ~= nil)
+               modmgr.world_config_enable_mod(toset)
+       end
+
+       if fields["cb_hide_gamemods"] ~= nil then
+               local current = filterlist.get_filtercriteria(modmgr.modlist)
+
+               if current == nil then
+                       current = {}
+               end
+
+               if engine.is_yes(fields["cb_hide_gamemods"]) then
+                       current.hide_game = true
+                       modmgr.hide_gamemods = true
+               else
+                       current.hide_game = false
+                       modmgr.hide_gamemods = false
+               end
+
+               filterlist.set_filtercriteria(modmgr.modlist,current)
+       end
+
+               if fields["cb_hide_mpcontent"] ~= nil then
+               local current = filterlist.get_filtercriteria(modmgr.modlist)
+
+               if current == nil then
+                       current = {}
+               end
+
+               if engine.is_yes(fields["cb_hide_mpcontent"]) then
+                       current.hide_modpackcontents = true
+                       modmgr.hide_modpackcontents = true
+               else
+                       current.hide_modpackcontents = false
+                       modmgr.hide_modpackcontents = false
+               end
+
+               filterlist.set_filtercriteria(modmgr.modlist,current)
+       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 = Settings(filename)
+               local mods = worldfile:to_table()
+
+               local rawlist = filterlist.get_raw_list(modmgr.modlist)
+
+               local i,mod
+               for i,mod in ipairs(rawlist) do
+                       if not mod.is_modpack and
+                                       mod.typ ~= "game_mod" then
+                               if mod.enabled then
+                                       worldfile:set("load_mod_"..mod.name, "true")
+                               else
+                                       worldfile:set("load_mod_"..mod.name, "false")
+                               end
+                               mods["load_mod_"..mod.name] = nil
+                       end
+               end
+
+               -- Remove mods that are not present anymore
+               for key,value in pairs(mods) do
+                       if key:sub(1,9) == "load_mod_" then
+                               worldfile:remove(key)
+                       end
+               end
+
+               if not worldfile:write() then
+                       engine.log("error", "Failed to write world config file")
+               end
+
+               modmgr.modlist = nil
+               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_all_mods"] then
+               local list = filterlist.get_raw_list(modmgr.modlist)
+
+               for i=1,#list,1 do
+                       if list[i].typ ~= "game_mod" and
+                               not list[i].is_modpack then
+                               list[i].enabled = true
+                       end
+               end
+       end
+
+
+
+       return nil
+end
+--------------------------------------------------------------------------------
+function modmgr.world_config_enable_mod(toset)
+       local mod = filterlist.get_list(modmgr.modlist)
+               [engine.get_textlist_index("world_config_modlist")]
+
+       if mod.typ == "game_mod" then
+               -- game mods can't be enabled or disabled
+       elseif not mod.is_modpack then
+               if toset == nil then
+                       mod.enabled = not mod.enabled
+               else
+                       mod.enabled = toset
+               end
+       else
+               local list = filterlist.get_raw_list(modmgr.modlist)
+               for i=1,#list,1 do
+                       if list[i].modpack == mod.name then
+                               if toset == nil then
+                                       toset = not list[i].enabled
+                               end
+                               list[i].enabled = toset
+                       end
+               end
+       end
+end
+--------------------------------------------------------------------------------
+function modmgr.handle_delete_mod_buttons(fields)
+       local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
+
+       if fields["dlg_delete_mod_confirm"] ~= nil then
+
+               if mod.path ~= nil and
+                       mod.path ~= "" and
+                       mod.path ~= engine.get_modpath() then
+                       if not engine.delete_dir(mod.path) then
+                               gamedata.errormessage = fgettext("Modmgr: failed to delete \"$1\"", mod.path)
+                       end
+                       modmgr.refresh_globals()
+               else
+                       gamedata.errormessage = fgettext("Modmgr: invalid modpath \"$1\"", mod.path)
+               end
+       end
+
+       return {
+               is_dialog = false,
+               show_buttons = true,
+               current_tab = engine.setting_get("main_menu_tab")
+               }
+end
+
+--------------------------------------------------------------------------------
+function modmgr.dialog_delete_mod()
+
+       local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
+
+       local retval =
+               "field[1.75,1;10,3;;" .. fgettext("Are you sure you want to delete \"$1\"?", mod.name) ..  ";]"..
+               "button[4,4.2;1,0.5;dlg_delete_mod_confirm;" .. fgettext("Yes") .. "]" ..
+               "button[6.5,4.2;3,0.5;dlg_delete_mod_cancel;" .. fgettext("No of course not!") .. "]"
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modmgr.preparemodlist(data)
+       local retval = {}
+
+       local global_mods = {}
+       local game_mods = {}
+
+       --read global mods
+       local modpath = engine.get_modpath()
+
+       if modpath ~= nil and
+               modpath ~= "" then
+               get_mods(modpath,global_mods)
+       end
+
+       for i=1,#global_mods,1 do
+               global_mods[i].typ = "global_mod"
+               table.insert(retval,global_mods[i])
+       end
+
+       --read game mods
+       local gamespec = gamemgr.find_by_gameid(data.gameid)
+       gamemgr.get_game_mods(gamespec, game_mods)
+
+       for i=1,#game_mods,1 do
+               game_mods[i].typ = "game_mod"
+               table.insert(retval,game_mods[i])
+       end
+
+       if data.worldpath == nil then
+               return retval
+       end
+
+       --read world mod configuration
+       local filename = data.worldpath ..
+                               DIR_DELIM .. "world.mt"
+
+       local worldfile = Settings(filename)
+
+       for key,value in pairs(worldfile:to_table()) do
+               if key:sub(1, 9) == "load_mod_" then
+                       key = key:sub(10)
+                       local element = nil
+                       for i=1,#retval,1 do
+                               if retval[i].name == key then
+                                       element = retval[i]
+                                       break
+                               end
+                       end
+                       if element ~= nil then
+                               element.enabled = engine.is_yes(value)
+                       else
+                               engine.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
+                       end
+               end
+       end
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+function modmgr.init_worldconfig()
+       modmgr.precheck()
+       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
+
+               modmgr.modlist = filterlist.create(
+                                               modmgr.preparemodlist, --refresh
+                                               modmgr.comparemod, --compare
+                                               function(element,uid) --uid match
+                                                       if element.name == uid then
+                                                               return true
+                                                       end
+                                               end,
+                                               function(element,criteria)
+                                                       if criteria.hide_game and
+                                                               element.typ == "game_mod" then
+                                                                       return false
+                                                       end
+
+                                                       if criteria.hide_modpackcontents and
+                                                               element.modpack ~= nil then
+                                                                       return false
+                                                               end
+                                                       return true
+                                               end, --filter
+                                               { worldpath= worldspec.path,
+                                                 gameid = worldspec.gameid }
+                                       )
+
+               filterlist.set_filtercriteria(modmgr.modlist, {
+                                                                       hide_game=modmgr.hide_gamemods,
+                                                                       hide_modpackcontents= modmgr.hide_modpackcontents
+                                                                       })
+               filterlist.add_sort_mechanism(modmgr.modlist, "alphabetic", sort_mod_list)
+               filterlist.set_sortmode(modmgr.modlist, "alphabetic")
+
+               return true
+       end
+
+       return false
+end
+
+--------------------------------------------------------------------------------
+function modmgr.comparemod(elem1,elem2)
+       if elem1 == nil or elem2 == nil then
+               return false
+       end
+       if elem1.name ~= elem2.name then
+               return false
+       end
+       if elem1.is_modpack ~= elem2.is_modpack then
+               return false
+       end
+       if elem1.typ ~= elem2.typ then
+               return false
+       end
+       if elem1.modpack ~= elem2.modpack then
+               return false
+       end
+
+       if elem1.path ~= elem2.path then
+               return false
+       end
+
+       return true
+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
+
+--------------------------------------------------------------------------------
+function modmgr.mod_exists(basename)
+
+       if modmgr.global_mods == nil then
+               modmgr.refresh_globals()
+       end
+
+       if filterlist.raw_index_by_uid(modmgr.global_mods,basename) > 0 then
+               return true
+       end
+
+       return false
+end
+
+--------------------------------------------------------------------------------
+function modmgr.get_global_mod(idx)
+
+       if modmgr.global_mods == nil then
+               return nil
+       end
+
+       if idx == nil or idx < 1 or idx > filterlist.size(modmgr.global_mods) then
+               return nil
+       end
+
+       return filterlist.get_list(modmgr.global_mods)[idx]
+end
+
+--------------------------------------------------------------------------------
+function modmgr.refresh_globals()
+       modmgr.global_mods = filterlist.create(
+                                       modmgr.preparemodlist, --refresh
+                                       modmgr.comparemod, --compare
+                                       function(element,uid) --uid match
+                                               if element.name == uid then
+                                                       return true
+                                               end
+                                       end,
+                                       nil, --filter
+                                       {}
+                                       )
+       filterlist.add_sort_mechanism(modmgr.global_mods, "alphabetic", sort_mod_list)
+       filterlist.set_sortmode(modmgr.global_mods, "alphabetic")
+end
+
+--------------------------------------------------------------------------------
+function modmgr.identify_filetype(name)
+
+       if name:sub(-3):lower() == "zip" then
+               return {
+                               name = name,
+                               type = "zip"
+                               }
+       end
+
+       if name:sub(-6):lower() == "tar.gz" or
+               name:sub(-3):lower() == "tgz"then
+               return {
+                               name = name,
+                               type = "tgz"
+                               }
+       end
+
+       if name:sub(-6):lower() == "tar.bz2" then
+               return {
+                               name = name,
+                               type = "tbz"
+                               }
+       end
+
+       if name:sub(-2):lower() == "7z" then
+               return {
+                               name = name,
+                               type = "7z"
+                               }
+       end
+
+       return {
+               name = name,
+               type = "ukn"
+       }
+end
diff --git a/builtin/mainmenu/modstore.lua b/builtin/mainmenu/modstore.lua
new file mode 100644 (file)
index 0000000..ef7fd01
--- /dev/null
@@ -0,0 +1,615 @@
+--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 [parent=#modstore] init
+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_texturepath() .. DIR_DELIM .. "base" ..
+                                               DIR_DELIM .. "pack" .. DIR_DELIM
+
+       modstore.lastmodtitle = ""
+       modstore.last_search = ""
+       
+       modstore.searchlist = filterlist.create(
+               function()
+                       if modstore.modlist_unsorted ~= nil and
+                               modstore.modlist_unsorted.data ~= nil then
+                               return modstore.modlist_unsorted.data
+                       end
+                       return {}
+               end,
+               function(element,modid)
+                       if element.id == modid then
+                               return true
+                       end
+                       return false
+               end, --compare fct
+               nil, --uid match fct
+               function(element,substring)
+                       if substring == nil or
+                               substring == "" then
+                               return false
+                       end
+                       substring = substring:upper()
+                       
+                       if element.title ~= nil and
+                               element.title:upper():find(substring) ~= nil then
+                               return true
+                       end
+                       
+                       if element.details ~= nil and
+                               element.details.author ~= nil and
+                               element.details.author:upper():find(substring) ~= nil then
+                               return true
+                       end
+                       
+                       if element.details ~= nil and
+                               element.details.description ~= nil and
+                               element.details.description:upper():find(substring) ~= nil then
+                               return true
+                       end
+                       return false
+               end --filter fct
+               )
+
+       modstore.current_list = nil
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#modstore] nametoindex
+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 [parent=#modstore] getsuccessfuldialog
+function modstore.getsuccessfuldialog()
+       local retval = ""
+       retval = retval .. "size[6,2,true]"
+       if modstore.lastmodentry ~= nil then
+               retval = retval .. "label[0,0.25;" .. fgettext("Successfully installed:") .. "]"
+               retval = retval .. "label[3,0.25;" .. modstore.lastmodentry.moddetails.title .. "]"
+       
+               
+               retval = retval .. "label[0,0.75;" .. fgettext("Shortname:") .. "]"
+               retval = retval .. "label[3,0.75;" .. engine.formspec_escape(modstore.lastmodentry.moddetails.basename) .. "]"
+
+       end
+       retval = retval .. "button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;" .. fgettext("ok") .. "]"
+                               
+                               
+       return retval
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#modstore] gettab
+function modstore.gettab(tabname)
+       local retval = ""
+
+       local is_modstore_tab = false
+
+       if tabname == "dialog_modstore_unsorted" then
+               modstore.modsperpage = 5
+               retval = modstore.getmodlist(modstore.modlist_unsorted)
+               is_modstore_tab = true
+       end
+
+       if tabname == "dialog_modstore_search" then
+               retval = modstore.getsearchpage()
+               is_modstore_tab = true
+       end
+
+       if is_modstore_tab then
+               return modstore.tabheader(tabname) .. retval
+       end
+
+       if tabname == "modstore_mod_installed" then
+               return modstore.getsuccessfuldialog()
+       end
+
+       if tabname == "modstore_downloading" then
+               return "size[6,2]label[0.25,0.75;" .. fgettext("Downloading") ..
+                               " " .. modstore.lastmodtitle .. " " ..
+                               fgettext("please wait...") .. "]"
+       end
+
+       return ""
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#modstore] tabheader
+function modstore.tabheader(tabname)
+       local retval  = "size[12,10.25,true]"
+       retval = retval .. "tabheader[-0.3,-0.99;modstore_tab;" ..
+                               "Unsorted,Search;" ..
+                               modstore.nametoindex(tabname) .. ";true;false]" ..
+                               "button[4,9.9;4,0.5;btn_modstore_close;" ..
+                               fgettext("Close modstore") .. "]"
+
+       return retval
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#modstore] handle_buttons
+function modstore.handle_buttons(current_tab,fields)
+
+       if fields["modstore_tab"] then
+               local index = tonumber(fields["modstore_tab"])
+
+               if index > 0 and
+                       index <= #modstore.tabnames then
+                       if modstore.tabnames[index] == "dialog_modstore_search" then
+                               filterlist.set_filtercriteria(modstore.searchlist,modstore.last_search)
+                               filterlist.refresh(modstore.searchlist)
+                               modstore.modsperpage = 4
+                               modstore.currentlist = {
+                                       page = 0,
+                                       pagecount =
+                                               math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage),
+                                       data = filterlist.get_list(modstore.searchlist),
+                               }
+                       end
+                       
+                       return {
+                                       current_tab = modstore.tabnames[index],
+                                       is_dialog = true,
+                                       show_buttons = false
+                       }
+               end
+               
+       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-1 then
+                       modstore.current_list.page = modstore.current_list.page +1
+               end
+       end
+
+       if fields["btn_hidden_close_download"] ~= nil then
+               if fields["btn_hidden_close_download"].successfull then
+                       modstore.lastmodentry = fields["btn_hidden_close_download"]
+                       return {
+                                       current_tab = "modstore_mod_installed",
+                                       is_dialog = true,
+                                       show_buttons = false
+                       }
+               else
+                       modstore.lastmodtitle = ""
+                       return {
+                                               current_tab = modstore.tabnames[1],
+                                               is_dialog = true,
+                                               show_buttons = false
+                               }
+               end
+       end
+
+       if fields["btn_confirm_mod_successfull"] then
+               modstore.lastmodentry = nil
+               modstore.lastmodtitle = ""
+               return {
+                                       current_tab = modstore.tabnames[1],
+                                       is_dialog = true,
+                                       show_buttons = false
+                       }
+       end
+
+       if fields["btn_modstore_search"] or
+               (fields["key_enter"] and fields["te_modstore_search"] ~= nil) then
+               modstore.last_search = fields["te_modstore_search"]
+               filterlist.set_filtercriteria(modstore.searchlist,fields["te_modstore_search"])
+               filterlist.refresh(modstore.searchlist)
+               modstore.currentlist = {
+                       page = 0,
+                       pagecount =  math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage),
+                       data = filterlist.get_list(modstore.searchlist),
+               }
+       end
+       
+       
+       if fields["btn_modstore_close"] then
+               return {
+                       is_dialog = false,
+                       show_buttons = true,
+                       current_tab = engine.setting_get("main_menu_tab")
+               }
+       end
+       
+       for key,value in pairs(fields) do
+               local foundat = key:find("btn_install_mod_")
+               if ( foundat == 1) then
+                       local modid = tonumber(key:sub(17))
+                       for i=1,#modstore.modlist_unsorted.data,1 do
+                               if modstore.modlist_unsorted.data[i].id == modid then
+                                       local moddetails = modstore.modlist_unsorted.data[i].details
+
+                                       if modstore.lastmodtitle ~= "" then
+                                               modstore.lastmodtitle = modstore.lastmodtitle .. ", "
+                                       end
+       
+                                       modstore.lastmodtitle = modstore.lastmodtitle .. moddetails.title
+       
+                                       engine.handle_async(
+                                               function(param)
+                                               
+                                                       local fullurl = engine.setting_get("modstore_download_url") ..
+                                                                                       param.moddetails.download_url
+                                                                                       
+                                                       if param.version ~= nil then
+                                                               local found = false
+                                                               for i=1,#param.moddetails.versions, 1 do
+                                                                       if param.moddetails.versions[i].date:sub(1,10) == param.version then
+                                                                               fullurl = engine.setting_get("modstore_download_url") ..
+                                                                                                               param.moddetails.versions[i].download_url
+                                                                               found = true
+                                                                       end
+                                                               end
+                                                               
+                                                               if not found then
+                                                                       return {
+                                                                               moddetails = param.moddetails,
+                                                                               successfull = false
+                                                                       }
+                                                               end
+                                                       end
+       
+                                                       if engine.download_file(fullurl,param.filename) then
+                                                               return {
+                                                                       texturename = param.texturename,
+                                                                       moddetails = param.moddetails,
+                                                                       filename = param.filename,
+                                                                       successfull = true
+                                                               }
+                                                       else
+                                                               return {
+                                                                       moddetails = param.moddetails,
+                                                                       successfull = false
+                                                               }
+                                                       end
+                                               end,
+                                               {
+                                                       moddetails = moddetails,
+                                                       version = fields["dd_version" .. modid],
+                                                       filename = os.tempfolder() .. "_MODNAME_" .. moddetails.basename .. ".zip",
+                                                       texturename = modstore.modlist_unsorted.data[i].texturename
+                                               },
+                                               function(result)
+                                                       if result.successfull then
+                                                               modmgr.installmod(result.filename,result.moddetails.basename)
+                                                               os.remove(result.filename)
+                                                       else
+                                                               gamedata.errormessage = "Failed to download " .. result.moddetails.title
+                                                       end
+       
+                                                       if gamedata.errormessage == nil then
+                                                               engine.button_handler({btn_hidden_close_download=result})
+                                                       else
+                                                               engine.button_handler({btn_hidden_close_download={successfull=false}})
+                                                       end
+                                               end
+                                       )
+       
+                                       return {
+                                               current_tab = "modstore_downloading",
+                                               is_dialog = true,
+                                               show_buttons = false,
+                                               ignore_menu_quit = true
+                                       }
+                               end
+                       end
+                       break
+               end
+       end
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#modstore] update_modlist
+function modstore.update_modlist()
+       modstore.modlist_unsorted = {}
+       modstore.modlist_unsorted.data = {}
+       modstore.modlist_unsorted.pagecount = 1
+       modstore.modlist_unsorted.page = 0
+
+       engine.handle_async(
+               function(param)
+                       return engine.get_modstore_list()
+               end,
+               nil,
+               function(result)
+                       if result ~= nil then
+                               modstore.modlist_unsorted = {}
+                               modstore.modlist_unsorted.data = result
+
+                               if modstore.modlist_unsorted.data ~= nil then
+                                       modstore.modlist_unsorted.pagecount =
+                                               math.ceil((#modstore.modlist_unsorted.data / modstore.modsperpage))
+                               else
+                                       modstore.modlist_unsorted.data = {}
+                                       modstore.modlist_unsorted.pagecount = 1
+                               end
+                               modstore.modlist_unsorted.page = 0
+                               modstore.fetchdetails()
+                               engine.event_handler("Refresh")
+                       end
+               end
+       )
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#modstore] fetchdetails
+function modstore.fetchdetails()
+
+       for i=1,#modstore.modlist_unsorted.data,1 do
+               engine.handle_async(
+               function(param)
+                       param.details = engine.get_modstore_details(tostring(param.modid))
+                       return param
+               end,
+               {
+                       modid=modstore.modlist_unsorted.data[i].id,
+                       listindex=i
+               },
+               function(result)
+                       if result ~= nil and
+                               modstore.modlist_unsorted ~= nil
+                               and modstore.modlist_unsorted.data ~= nil and
+                               modstore.modlist_unsorted.data[result.listindex] ~= nil and
+                               modstore.modlist_unsorted.data[result.listindex].id ~= nil then
+
+                               modstore.modlist_unsorted.data[result.listindex].details = result.details
+                               engine.event_handler("Refresh")
+                       end
+               end
+               )
+       end
+end
+
+--------------------------------------------------------------------------------
+-- @function [parent=#modstore] getscreenshot
+function modstore.getscreenshot(ypos,listentry)
+
+       if      listentry.details ~= nil and
+               (listentry.details.screenshot_url == nil or
+               listentry.details.screenshot_url == "") then
+               
+               if listentry.texturename == nil then
+                       listentry.texturename = modstore.basetexturedir .. "no_screenshot.png"
+               end
+               
+               return "image[0,".. ypos .. ";3,2;" ..
+                       engine.formspec_escape(listentry.texturename) .. "]"
+       end
+       
+       if listentry.details ~= nil and
+               listentry.texturename == nil then
+               --make sure we don't download multiple times
+               listentry.texturename = "in progress"
+       
+               --prepare url and filename
+               local fullurl = engine.setting_get("modstore_download_url") ..
+                                       listentry.details.screenshot_url
+               local filename = os.tempfolder() .. "_MID_" .. listentry.id
+               
+               --trigger download
+               engine.handle_async(
+                       --first param is downloadfct
+                       function(param)
+                               param.successfull = engine.download_file(param.fullurl,param.filename)
+                               return param
+                       end,
+                       --second parameter is data passed to async job
+                       {
+                               fullurl = fullurl,
+                               filename = filename,
+                               modid = listentry.id
+                       },
+                       --integrate result to raw list
+                       function(result)
+                               if result.successfull then
+                                       local found = false
+                                       for i=1,#modstore.modlist_unsorted.data,1 do
+                                               if modstore.modlist_unsorted.data[i].id == result.modid then
+                                                       found = true
+                                                       modstore.modlist_unsorted.data[i].texturename = result.filename
+                                                       break
+                                               end
+                                       end
+                                       if found then
+                                               engine.event_handler("Refresh")
+                                       else
+                                               engine.log("error","got screenshot but didn't find matching mod: " .. result.modid)
+                                       end
+                               end
+                       end
+               )
+       end
+
+       if listentry.texturename ~= nil and
+               listentry.texturename ~= "in progress" then
+               return "image[0,".. ypos .. ";3,2;" ..
+                       engine.formspec_escape(listentry.texturename) .. "]"
+       end
+       
+       return ""
+end
+
+--------------------------------------------------------------------------------
+--@function [parent=#modstore] getshortmodinfo
+function modstore.getshortmodinfo(ypos,listentry,details)
+       local retval = ""
+       
+       retval = retval .. "box[0," .. ypos .. ";11.4,1.75;#FFFFFF]"
+
+       --screenshot
+       retval = retval .. modstore.getscreenshot(ypos,listentry)
+
+       --title + author
+       retval = retval .."label[2.75," .. ypos .. ";" ..
+               engine.formspec_escape(details.title) .. " (" .. details.author .. ")]"
+
+       --description
+       local descriptiony = ypos + 0.5
+       retval = retval .. "textarea[3," .. descriptiony .. ";6.5,1.55;;" ..
+               engine.formspec_escape(details.description) .. ";]"
+               
+       --rating
+       local ratingy = ypos
+       retval = retval .."label[7," .. ratingy .. ";" ..
+                                       fgettext("Rating") .. ":]"
+       retval = retval .. "label[8.7," .. ratingy .. ";" .. details.rating .."]"
+       
+       --versions (IMPORTANT has to be defined AFTER rating)
+       if details.versions ~= nil and
+               #details.versions > 1 then
+               local versiony = ypos + 0.05
+               retval = retval .. "dropdown[9.1," .. versiony .. ";2.48,0.25;dd_version" .. details.id .. ";"
+               local versions = ""
+               for i=1,#details.versions , 1 do
+                       if versions ~= "" then
+                               versions = versions .. ","
+                       end
+                       
+                       versions = versions .. details.versions[i].date:sub(1,10)
+               end
+               retval = retval .. versions .. ";1]"
+       end
+
+       if details.basename then
+               --install button
+               local buttony = ypos + 1.2
+               retval = retval .."button[9.1," .. buttony .. ";2.5,0.5;btn_install_mod_" .. details.id .. ";"
+
+               if modmgr.mod_exists(details.basename) then
+                       retval = retval .. fgettext("re-Install") .."]"
+               else
+                       retval = retval .. fgettext("Install") .."]"
+               end
+       end
+       
+       return retval
+end
+
+--------------------------------------------------------------------------------
+--@function [parent=#modstore] getmodlist
+function modstore.getmodlist(list,yoffset)
+
+       modstore.current_list = list
+       
+       if #list.data == 0 then
+               return ""
+       end
+       
+       if yoffset == nil then
+               yoffset = 0
+       end
+
+       local scrollbar = ""
+       scrollbar = scrollbar .. "label[0.1,9.5;"
+                               .. fgettext("Page $1 of $2", list.page+1, list.pagecount) .. "]"
+       scrollbar = scrollbar .. "box[11.6," .. (yoffset + 0.35) .. ";0.28,"
+                               .. (8.6 - yoffset) .. ";#000000]"
+       local scrollbarpos = (yoffset + 0.75) +
+                               ((7.7 -yoffset)/(list.pagecount-1)) * list.page
+       scrollbar = scrollbar .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;#32CD32]"
+       scrollbar = scrollbar .. "button[11.6," .. (yoffset + (0.3))
+                               .. ";0.5,0.5;btn_modstore_page_up;^]"
+       scrollbar = scrollbar .. "button[11.6," .. 9.0
+                               .. ";0.5,0.5;btn_modstore_page_down;v]"
+
+       local retval = ""
+
+       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 = list.data[i].details
+
+               if details == nil then
+                       details = {}
+                       details.title = list.data[i].title
+                       details.author = ""
+                       details.rating = -1
+                       details.description = ""
+               end
+
+               if details ~= nil then
+                       local screenshot_ypos =
+                               yoffset +(i-1 - (list.page * modstore.modsperpage))*1.9 +0.2
+                               
+                       retval = retval .. modstore.getshortmodinfo(screenshot_ypos,
+                                                                                                               list.data[i],
+                                                                                                               details)
+               end
+       end
+       
+       return retval .. scrollbar
+end
+
+--------------------------------------------------------------------------------
+--@function [parent=#modstore] getsearchpage
+function modstore.getsearchpage()
+       local retval = ""
+       local search = ""
+       
+       if modstore.last_search ~= nil then
+               search = modstore.last_search
+       end
+
+       retval = retval ..
+               "button[9.5,0.2;2.5,0.5;btn_modstore_search;".. fgettext("Search") .. "]" ..
+               "field[0.5,0.5;9,0.5;te_modstore_search;;" .. search .. "]"
+
+       
+       --show 4 mods only
+       modstore.modsperpage = 4
+       retval = retval ..
+               modstore.getmodlist(
+                       modstore.currentlist,
+                       1.75)
+
+       return retval;
+end
+
diff --git a/builtin/mainmenu/textures.lua b/builtin/mainmenu/textures.lua
new file mode 100644 (file)
index 0000000..998fc21
--- /dev/null
@@ -0,0 +1,146 @@
+--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.
+
+
+mm_texture = {}
+
+--------------------------------------------------------------------------------
+function mm_texture.init()
+       mm_texture.defaulttexturedir = engine.get_texturepath() .. DIR_DELIM .. "base" .. 
+                                               DIR_DELIM .. "pack" .. DIR_DELIM
+       mm_texture.basetexturedir = mm_texture.defaulttexturedir
+       
+       mm_texture.texturepack = engine.setting_get("texture_path")
+       
+       mm_texture.gameid = nil
+end
+
+--------------------------------------------------------------------------------
+function mm_texture.update(tab,gamedetails)
+       if tab ~= "singleplayer" then
+               mm_texture.reset()
+               return
+       end
+
+       if gamedetails == nil then
+               return
+       end
+       
+       mm_texture.update_game(gamedetails)
+end
+
+--------------------------------------------------------------------------------
+function mm_texture.reset()
+       mm_texture.gameid = nil
+       local have_bg      = false
+       local have_overlay = mm_texture.set_generic("overlay")
+       
+       if not have_overlay then
+               have_bg = mm_texture.set_generic("background")
+       end
+       
+       mm_texture.clear("header")
+       mm_texture.clear("footer")
+       engine.set_clouds(false)
+       
+       mm_texture.set_generic("footer")
+       mm_texture.set_generic("header")
+       
+       if not have_bg and
+               engine.setting_getbool("enable_clouds") then
+                       engine.set_clouds(true)
+       end
+end
+
+--------------------------------------------------------------------------------
+function mm_texture.update_game(gamedetails)
+       if mm_texture.gameid == gamedetails.id then
+               return
+       end
+       
+       local have_bg      = false 
+       local have_overlay = mm_texture.set_game("overlay",gamedetails)
+       
+       if not have_overlay then
+               have_bg = mm_texture.set_game("background",gamedetails)
+       end
+       
+       mm_texture.clear("header")
+       mm_texture.clear("footer")
+       engine.set_clouds(false)
+       
+       if not have_bg and
+               engine.setting_getbool("enable_clouds") then
+                       engine.set_clouds(true)
+       end
+       
+       mm_texture.set_game("footer",gamedetails)
+       mm_texture.set_game("header",gamedetails)
+       
+       mm_texture.gameid = gamedetails.id
+end
+
+--------------------------------------------------------------------------------
+function mm_texture.clear(identifier)
+       engine.set_background(identifier,"")
+end
+
+--------------------------------------------------------------------------------
+function mm_texture.set_generic(identifier)
+       --try texture pack first
+       if mm_texture.texturepack ~= nil then
+               local path = mm_texture.texturepack .. DIR_DELIM .."menu_" .. 
+                                                                               identifier .. ".png"
+               if engine.set_background(identifier,path) then
+                       return true
+               end
+       end
+       
+       if mm_texture.defaulttexturedir ~= nil then
+               local path = mm_texture.defaulttexturedir .. DIR_DELIM .."menu_" .. 
+                                                                               identifier .. ".png"
+               if engine.set_background(identifier,path) then
+                       return true
+               end
+       end
+       
+       return false
+end
+
+--------------------------------------------------------------------------------
+function mm_texture.set_game(identifier,gamedetails)
+       
+       if gamedetails == nil then
+               return false
+       end
+
+       if mm_texture.texturepack ~= nil then
+               local path = mm_texture.texturepack .. DIR_DELIM ..
+                                               gamedetails.id .. "_menu_" .. identifier .. ".png"
+               if engine.set_background(identifier,path) then
+                       return true
+               end
+       end
+       
+       local path = gamedetails.path .. DIR_DELIM .."menu" .. 
+                                                                        DIR_DELIM .. identifier .. ".png"
+       if engine.set_background(identifier,path) then
+               return true
+       end
+       
+       return false
+end
diff --git a/builtin/misc.lua b/builtin/misc.lua
deleted file mode 100644 (file)
index 82cc527..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
--- Minetest: builtin/misc.lua
-
---
--- Misc. API functions
---
-
-minetest.timers_to_add = {}
-minetest.timers = {}
-minetest.register_globalstep(function(dtime)
-       for _, timer in ipairs(minetest.timers_to_add) do
-               table.insert(minetest.timers, timer)
-       end
-       minetest.timers_to_add = {}
-       for index, timer in ipairs(minetest.timers) do
-               timer.time = timer.time - dtime
-               if timer.time <= 0 then
-                       timer.func(unpack(timer.args or {}))
-                       table.remove(minetest.timers,index)
-               end
-       end
-end)
-
-function minetest.after(time, func, ...)
-       assert(tonumber(time) and type(func) == "function",
-                       "Invalid minetest.after invocation")
-       table.insert(minetest.timers_to_add, {time=time, func=func, args={...}})
-end
-
-function minetest.check_player_privs(name, privs)
-       local player_privs = minetest.get_player_privs(name)
-       local missing_privileges = {}
-       for priv, val in pairs(privs) do
-               if val then
-                       if not player_privs[priv] then
-                               table.insert(missing_privileges, priv)
-                       end
-               end
-       end
-       if #missing_privileges > 0 then
-               return false, missing_privileges
-       end
-       return true, ""
-end
-
-local player_list = {}
-
-minetest.register_on_joinplayer(function(player)
-       player_list[player:get_player_name()] = player
-end)
-
-minetest.register_on_leaveplayer(function(player)
-       player_list[player:get_player_name()] = nil
-end)
-
-function minetest.get_connected_players()
-       local temp_table = {}
-       for index, value in pairs(player_list) do
-               if value:is_player_connected() then
-                       table.insert(temp_table, value)
-               end
-       end
-       return temp_table
-end
-
-function minetest.hash_node_position(pos)
-       return (pos.z+32768)*65536*65536 + (pos.y+32768)*65536 + pos.x+32768
-end
-
-function minetest.get_position_from_hash(hash)
-       local pos = {}
-       pos.x = (hash%65536) - 32768
-       hash = math.floor(hash/65536)
-       pos.y = (hash%65536) - 32768
-       hash = math.floor(hash/65536)
-       pos.z = (hash%65536) - 32768
-       return pos
-end
-
-function minetest.get_item_group(name, group)
-       if not minetest.registered_items[name] or not
-                       minetest.registered_items[name].groups[group] then
-               return 0
-       end
-       return minetest.registered_items[name].groups[group]
-end
-
-function minetest.get_node_group(name, group)
-       minetest.log("deprecated", "Deprecated usage of get_node_group, use get_item_group instead")
-       return minetest.get_item_group(name, group)
-end
-
-function minetest.string_to_pos(value)
-       local p = {}
-       p.x, p.y, p.z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
-       if p.x and p.y and p.z then
-               p.x = tonumber(p.x)
-               p.y = tonumber(p.y)
-               p.z = tonumber(p.z)
-               return p
-       end
-       local p = {}
-       p.x, p.y, p.z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$")
-       if p.x and p.y and p.z then
-               p.x = tonumber(p.x)
-               p.y = tonumber(p.y)
-               p.z = tonumber(p.z)
-               return p
-       end
-       return nil
-end
-
-assert(minetest.string_to_pos("10.0, 5, -2").x == 10)
-assert(minetest.string_to_pos("( 10.0, 5, -2)").z == -2)
-assert(minetest.string_to_pos("asd, 5, -2)") == nil)
-
-function minetest.setting_get_pos(name)
-       local value = minetest.setting_get(name)
-       if not value then
-               return nil
-       end
-       return minetest.string_to_pos(value)
-end
-
--- To be overriden by protection mods
-function minetest.is_protected(pos, name)
-       return false
-end
-
-function minetest.record_protection_violation(pos, name)
-       for _, func in pairs(minetest.registered_on_protection_violation) do
-               func(pos, name)
-       end
-end
-
diff --git a/builtin/misc_helpers.lua b/builtin/misc_helpers.lua
deleted file mode 100644 (file)
index 9c73497..0000000
+++ /dev/null
@@ -1,428 +0,0 @@
--- Minetest: builtin/misc_helpers.lua
-
---------------------------------------------------------------------------------
-function basic_dump2(o)
-       if type(o) == "number" then
-               return tostring(o)
-       elseif type(o) == "string" then
-               return string.format("%q", o)
-       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 dump2(o, name, dumped)
-       name = name or "_"
-       dumped = dumped or {}
-       io.write(name, " = ")
-       if type(o) == "number" or type(o) == "string" or type(o) == "boolean"
-                       or type(o) == "function" or type(o) == "nil"
-                       or type(o) == "userdata" then
-               io.write(basic_dump2(o), "\n")
-       elseif type(o) == "table" then
-               if dumped[o] then
-                       io.write(dumped[o], "\n")
-               else
-                       dumped[o] = name
-                       io.write("{}\n") -- new table
-                       for k,v in pairs(o) do
-                               local fieldname = string.format("%s[%s]", name, basic_dump2(k))
-                               dump2(v, fieldname, dumped)
-                       end
-               end
-       else
-               error("cannot dump a " .. type(o))
-               return nil
-       end
-end
-
---------------------------------------------------------------------------------
-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] = "[" .. dump(k, dumped) .. "] = " .. 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 file_exists(filename)
-       local f = io.open(filename, "r")
-       if f==nil then
-               return false
-       else
-               f:close()
-               return true
-       end
-end
-
---------------------------------------------------------------------------------
-function string:trim()
-       return (self:gsub("^%s*(.-)%s*$", "%1"))
-end
-
-assert(string.trim("\n \t\tfoo bar\t ") == "foo bar")
-
---------------------------------------------------------------------------------
-function math.hypot(x, y)
-       local t
-       x = math.abs(x)
-       y = math.abs(y)
-       t = math.min(x, y)
-       x = math.max(x, y)
-       if x == 0 then return 0 end
-       t = t / x
-       return x * math.sqrt(1 + t * t)
-end
-
---------------------------------------------------------------------------------
-function get_last_folder(text,count)
-       local parts = text:split(DIR_DELIM)
-       
-       if count == nil then
-               return parts[#parts]
-       end
-       
-       local retval = ""
-       for i=1,count,1 do
-               retval = retval .. parts[#parts - (count-i)] .. DIR_DELIM
-       end
-       
-       return retval
-end
-
---------------------------------------------------------------------------------
-function cleanup_path(temppath)
-       
-       local parts = temppath:split("-")
-       temppath = ""   
-       for i=1,#parts,1 do
-               if temppath ~= "" then
-                       temppath = temppath .. "_"
-               end
-               temppath = temppath .. parts[i]
-       end
-       
-       parts = temppath:split(".")
-       temppath = ""   
-       for i=1,#parts,1 do
-               if temppath ~= "" then
-                       temppath = temppath .. "_"
-               end
-               temppath = temppath .. parts[i]
-       end
-       
-       parts = temppath:split("'")
-       temppath = ""   
-       for i=1,#parts,1 do
-               if temppath ~= "" then
-                       temppath = temppath .. ""
-               end
-               temppath = temppath .. parts[i]
-       end
-       
-       parts = temppath:split(" ")
-       temppath = ""   
-       for i=1,#parts,1 do
-               if temppath ~= "" then
-                       temppath = temppath
-               end
-               temppath = temppath .. parts[i]
-       end
-       
-       return temppath
-end
-
-local tbl = engine or minetest
-function tbl.formspec_escape(text)
-       if text ~= nil then
-               text = string.gsub(text,"\\","\\\\")
-               text = string.gsub(text,"%]","\\]")
-               text = string.gsub(text,"%[","\\[")
-               text = string.gsub(text,";","\\;")
-               text = string.gsub(text,",","\\,")
-       end
-       return text
-end
-
-
-function tbl.splittext(text,charlimit)
-       local retval = {}
-
-       local current_idx = 1
-       
-       local start,stop = string.find(text," ",current_idx)
-       local nl_start,nl_stop = string.find(text,"\n",current_idx)
-       local gotnewline = false
-       if nl_start ~= nil and (start == nil or nl_start < start) then
-               start = nl_start
-               stop = nl_stop
-               gotnewline = true
-       end
-       local last_line = ""
-       while start ~= nil do
-               if string.len(last_line) + (stop-start) > charlimit then
-                       table.insert(retval,last_line)
-                       last_line = ""
-               end
-               
-               if last_line ~= "" then
-                       last_line = last_line .. " "
-               end
-               
-               last_line = last_line .. string.sub(text,current_idx,stop -1)
-               
-               if gotnewline then
-                       table.insert(retval,last_line)
-                       last_line = ""
-                       gotnewline = false
-               end
-               current_idx = stop+1
-               
-               start,stop = string.find(text," ",current_idx)
-               nl_start,nl_stop = string.find(text,"\n",current_idx)
-       
-               if nl_start ~= nil and (start == nil or nl_start < start) then
-                       start = nl_start
-                       stop = nl_stop
-                       gotnewline = true
-               end
-       end
-       
-       --add last part of text
-       if string.len(last_line) + (string.len(text) - current_idx) > charlimit then
-                       table.insert(retval,last_line)
-                       table.insert(retval,string.sub(text,current_idx))
-       else
-               last_line = last_line .. " " .. string.sub(text,current_idx)
-               table.insert(retval,last_line)
-       end
-       
-       return retval
-end
-
---------------------------------------------------------------------------------
-
-if minetest then
-       local dirs1 = {9, 18, 7, 12}
-       local dirs2 = {20, 23, 22, 21}
-
-       function minetest.rotate_and_place(itemstack, placer, pointed_thing,
-                               infinitestacks, orient_flags)
-               orient_flags = orient_flags or {}
-
-               local unode = minetest.get_node_or_nil(pointed_thing.under)
-               if not unode then
-                       return
-               end
-               local undef = minetest.registered_nodes[unode.name]
-               if undef and undef.on_rightclick then
-                       undef.on_rightclick(pointed_thing.under, unode, placer,
-                                       itemstack, pointed_thing)
-                       return
-               end
-               local pitch = placer:get_look_pitch()
-               local fdir = minetest.dir_to_facedir(placer:get_look_dir())
-               local wield_name = itemstack:get_name()
-
-               local above = pointed_thing.above
-               local under = pointed_thing.under
-               local iswall = (above.y == under.y)
-               local isceiling = not iswall and (above.y < under.y)
-               local anode = minetest.get_node_or_nil(above)
-               if not anode then
-                       return
-               end
-               local pos = pointed_thing.above
-               local node = anode
-
-               if undef and undef.buildable_to then
-                       pos = pointed_thing.under
-                       node = unode
-                       iswall = false
-               end
-
-               if minetest.is_protected(pos, placer:get_player_name()) then
-                       minetest.record_protection_violation(pos,
-                                       placer:get_player_name())
-                       return
-               end
-
-               local ndef = minetest.registered_nodes[node.name]
-               if not ndef or not ndef.buildable_to then
-                       return
-               end
-
-               if orient_flags.force_floor then
-                       iswall = false
-                       isceiling = false
-               elseif orient_flags.force_ceiling then
-                       iswall = false
-                       isceiling = true
-               elseif orient_flags.force_wall then
-                       iswall = true
-                       isceiling = false
-               elseif orient_flags.invert_wall then
-                       iswall = not iswall
-               end
-
-               if iswall then
-                       minetest.set_node(pos, {name = wield_name,
-                                       param2 = dirs1[fdir+1]})
-               elseif isceiling then
-                       if orient_flags.force_facedir then
-                               minetest.set_node(pos, {name = wield_name,
-                                               param2 = 20})
-                       else
-                               minetest.set_node(pos, {name = wield_name,
-                                               param2 = dirs2[fdir+1]})
-                       end
-               else -- place right side up
-                       if orient_flags.force_facedir then
-                               minetest.set_node(pos, {name = wield_name,
-                                               param2 = 0})
-                       else
-                               minetest.set_node(pos, {name = wield_name,
-                                               param2 = fdir})
-                       end
-               end
-
-               if not infinitestacks then
-                       itemstack:take_item()
-                       return itemstack
-               end
-       end
-
-
---------------------------------------------------------------------------------
---Wrapper for rotate_and_place() to check for sneak and assume Creative mode
---implies infinite stacks when performing a 6d rotation.
---------------------------------------------------------------------------------
-
-
-       minetest.rotate_node = function(itemstack, placer, pointed_thing)
-               minetest.rotate_and_place(itemstack, placer, pointed_thing,
-                               minetest.setting_getbool("creative_mode"),
-                               {invert_wall = placer:get_player_control().sneak})
-               return itemstack
-       end
-end
-
---------------------------------------------------------------------------------
-function tbl.explode_table_event(evt)
-       if evt ~= nil then
-               local parts = evt:split(":")
-               if #parts == 3 then
-                       local t = parts[1]:trim()
-                       local r = tonumber(parts[2]:trim())
-                       local c = tonumber(parts[3]:trim())
-                       if type(r) == "number" and type(c) == "number" and t ~= "INV" then
-                               return {type=t, row=r, column=c}
-                       end
-               end
-       end
-       return {type="INV", row=0, column=0}
-end
-
---------------------------------------------------------------------------------
-function tbl.explode_textlist_event(evt)
-       if evt ~= nil then
-               local parts = evt:split(":")
-               if #parts == 2 then
-                       local t = parts[1]:trim()
-                       local r = tonumber(parts[2]:trim())
-                       if type(r) == "number" and t ~= "INV" then
-                               return {type=t, index=r}
-                       end
-               end
-       end
-       return {type="INV", index=0}
-end
-
---------------------------------------------------------------------------------
--- mainmenu only functions
---------------------------------------------------------------------------------
-if engine ~= nil then
-       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 fgettext(text, ...)
-               text = engine.gettext(text)
-               local arg = {n=select('#', ...), ...}
-               if arg.n >= 1 then
-                       -- Insert positional parameters ($1, $2, ...)
-                       result = ''
-                       pos = 1
-                       while pos <= text:len() do
-                               newpos = text:find('[$]', pos)
-                               if newpos == nil then
-                                       result = result .. text:sub(pos)
-                                       pos = text:len() + 1
-                               else
-                                       paramindex = tonumber(text:sub(newpos+1, newpos+1))
-                                       result = result .. text:sub(pos, newpos-1) .. tostring(arg[paramindex])
-                                       pos = newpos + 2
-                               end
-                       end
-                       text = result
-               end
-               return engine.formspec_escape(text)
-       end
-end
---------------------------------------------------------------------------------
--- core only fct
---------------------------------------------------------------------------------
-if minetest ~= nil then
-       --------------------------------------------------------------------------------
-       function minetest.pos_to_string(pos)
-               return "(" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")"
-       end
-end
-
diff --git a/builtin/misc_register.lua b/builtin/misc_register.lua
deleted file mode 100644 (file)
index 99c5115..0000000
+++ /dev/null
@@ -1,410 +0,0 @@
--- Minetest: builtin/misc_register.lua
-
---
--- Make raw registration functions inaccessible to anyone except this file
---
-
-local register_item_raw = minetest.register_item_raw
-minetest.register_item_raw = nil
-
-local register_alias_raw = minetest.register_alias_raw
-minetest.register_item_raw = nil
-
---
--- Item / entity / ABM registration functions
---
-
-minetest.registered_abms = {}
-minetest.registered_entities = {}
-minetest.registered_items = {}
-minetest.registered_nodes = {}
-minetest.registered_craftitems = {}
-minetest.registered_tools = {}
-minetest.registered_aliases = {}
-
--- For tables that are indexed by item name:
--- If table[X] does not exist, default to table[minetest.registered_aliases[X]]
-local alias_metatable = {
-       __index = function(t, name)
-               return rawget(t, minetest.registered_aliases[name])
-       end
-}
-setmetatable(minetest.registered_items, alias_metatable)
-setmetatable(minetest.registered_nodes, alias_metatable)
-setmetatable(minetest.registered_craftitems, alias_metatable)
-setmetatable(minetest.registered_tools, alias_metatable)
-
--- These item names may not be used because they would interfere
--- with legacy itemstrings
-local forbidden_item_names = {
-       MaterialItem = true,
-       MaterialItem2 = true,
-       MaterialItem3 = true,
-       NodeItem = true,
-       node = true,
-       CraftItem = true,
-       craft = true,
-       MBOItem = true,
-       ToolItem = true,
-       tool = true,
-}
-
-local function check_modname_prefix(name)
-       if name:sub(1,1) == ":" then
-               -- Escape the modname prefix enforcement mechanism
-               return name:sub(2)
-       else
-               -- Modname prefix enforcement
-               local expected_prefix = minetest.get_current_modname() .. ":"
-               if name:sub(1, #expected_prefix) ~= expected_prefix then
-                       error("Name " .. name .. " does not follow naming conventions: " ..
-                               "\"modname:\" or \":\" prefix required")
-               end
-               local subname = name:sub(#expected_prefix+1)
-               if subname:find("[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_]") then
-                       error("Name " .. name .. " does not follow naming conventions: " ..
-                               "contains unallowed characters")
-               end
-               return name
-       end
-end
-
-function minetest.register_abm(spec)
-       -- Add to minetest.registered_abms
-       minetest.registered_abms[#minetest.registered_abms+1] = spec
-end
-
-function minetest.register_entity(name, prototype)
-       -- Check name
-       if name == nil then
-               error("Unable to register entity: Name is nil")
-       end
-       name = check_modname_prefix(tostring(name))
-
-       prototype.name = name
-       prototype.__index = prototype  -- so that it can be used as a metatable
-
-       -- Add to minetest.registered_entities
-       minetest.registered_entities[name] = prototype
-end
-
-function minetest.register_item(name, itemdef)
-       -- Check name
-       if name == nil then
-               error("Unable to register item: Name is nil")
-       end
-       name = check_modname_prefix(tostring(name))
-       if forbidden_item_names[name] then
-               error("Unable to register item: Name is forbidden: " .. name)
-       end
-       itemdef.name = name
-
-       -- Apply defaults and add to registered_* table
-       if itemdef.type == "node" then
-               -- Use the nodebox as selection box if it's not set manually
-               if itemdef.drawtype == "nodebox" and not itemdef.selection_box then
-                       itemdef.selection_box = itemdef.node_box
-               elseif itemdef.drawtype == "fencelike" and not itemdef.selection_box then
-                       itemdef.selection_box = {
-                               type = "fixed",
-                               fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8},
-                       }
-               end
-               setmetatable(itemdef, {__index = minetest.nodedef_default})
-               minetest.registered_nodes[itemdef.name] = itemdef
-       elseif itemdef.type == "craft" then
-               setmetatable(itemdef, {__index = minetest.craftitemdef_default})
-               minetest.registered_craftitems[itemdef.name] = itemdef
-       elseif itemdef.type == "tool" then
-               setmetatable(itemdef, {__index = minetest.tooldef_default})
-               minetest.registered_tools[itemdef.name] = itemdef
-       elseif itemdef.type == "none" then
-               setmetatable(itemdef, {__index = minetest.noneitemdef_default})
-       else
-               error("Unable to register item: Type is invalid: " .. dump(itemdef))
-       end
-
-       -- Flowing liquid uses param2
-       if itemdef.type == "node" and itemdef.liquidtype == "flowing" then
-               itemdef.paramtype2 = "flowingliquid"
-       end
-
-       -- BEGIN Legacy stuff
-       if itemdef.cookresult_itemstring ~= nil and itemdef.cookresult_itemstring ~= "" then
-               minetest.register_craft({
-                       type="cooking",
-                       output=itemdef.cookresult_itemstring,
-                       recipe=itemdef.name,
-                       cooktime=itemdef.furnace_cooktime
-               })
-       end
-       if itemdef.furnace_burntime ~= nil and itemdef.furnace_burntime >= 0 then
-               minetest.register_craft({
-                       type="fuel",
-                       recipe=itemdef.name,
-                       burntime=itemdef.furnace_burntime
-               })
-       end
-       -- END Legacy stuff
-
-       -- Disable all further modifications
-       getmetatable(itemdef).__newindex = {}
-
-       --minetest.log("Registering item: " .. itemdef.name)
-       minetest.registered_items[itemdef.name] = itemdef
-       minetest.registered_aliases[itemdef.name] = nil
-       register_item_raw(itemdef)
-end
-
-function minetest.register_node(name, nodedef)
-       nodedef.type = "node"
-       minetest.register_item(name, nodedef)
-end
-
-function minetest.register_craftitem(name, craftitemdef)
-       craftitemdef.type = "craft"
-
-       -- BEGIN Legacy stuff
-       if craftitemdef.inventory_image == nil and craftitemdef.image ~= nil then
-               craftitemdef.inventory_image = craftitemdef.image
-       end
-       -- END Legacy stuff
-
-       minetest.register_item(name, craftitemdef)
-end
-
-function minetest.register_tool(name, tooldef)
-       tooldef.type = "tool"
-       tooldef.stack_max = 1
-
-       -- BEGIN Legacy stuff
-       if tooldef.inventory_image == nil and tooldef.image ~= nil then
-               tooldef.inventory_image = tooldef.image
-       end
-       if tooldef.tool_capabilities == nil and
-          (tooldef.full_punch_interval ~= nil or
-           tooldef.basetime ~= nil or
-           tooldef.dt_weight ~= nil or
-           tooldef.dt_crackiness ~= nil or
-           tooldef.dt_crumbliness ~= nil or
-           tooldef.dt_cuttability ~= nil or
-           tooldef.basedurability ~= nil or
-           tooldef.dd_weight ~= nil or
-           tooldef.dd_crackiness ~= nil or
-           tooldef.dd_crumbliness ~= nil or
-           tooldef.dd_cuttability ~= nil) then
-               tooldef.tool_capabilities = {
-                       full_punch_interval = tooldef.full_punch_interval,
-                       basetime = tooldef.basetime,
-                       dt_weight = tooldef.dt_weight,
-                       dt_crackiness = tooldef.dt_crackiness,
-                       dt_crumbliness = tooldef.dt_crumbliness,
-                       dt_cuttability = tooldef.dt_cuttability,
-                       basedurability = tooldef.basedurability,
-                       dd_weight = tooldef.dd_weight,
-                       dd_crackiness = tooldef.dd_crackiness,
-                       dd_crumbliness = tooldef.dd_crumbliness,
-                       dd_cuttability = tooldef.dd_cuttability,
-               }
-       end
-       -- END Legacy stuff
-
-       minetest.register_item(name, tooldef)
-end
-
-function minetest.register_alias(name, convert_to)
-       if forbidden_item_names[name] then
-               error("Unable to register alias: Name is forbidden: " .. name)
-       end
-       if minetest.registered_items[name] ~= nil then
-               minetest.log("WARNING: Not registering alias, item with same name" ..
-                       " is already defined: " .. name .. " -> " .. convert_to)
-       else
-               --minetest.log("Registering alias: " .. name .. " -> " .. convert_to)
-               minetest.registered_aliases[name] = convert_to
-               register_alias_raw(name, convert_to)
-       end
-end
-
-local register_biome_raw = minetest.register_biome
-minetest.registered_biomes = {}
-function minetest.register_biome(biome)
-       minetest.registered_biomes[biome.name] = biome
-       register_biome_raw(biome)
-end
-
-function minetest.on_craft(itemstack, player, old_craft_list, craft_inv)
-       for _, func in ipairs(minetest.registered_on_crafts) do
-               itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack
-       end
-       return itemstack
-end
-
-function minetest.craft_predict(itemstack, player, old_craft_list, craft_inv)
-       for _, func in ipairs(minetest.registered_craft_predicts) do
-               itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack
-       end
-       return itemstack
-end
-
--- Alias the forbidden item names to "" so they can't be
--- created via itemstrings (e.g. /give)
-local name
-for name in pairs(forbidden_item_names) do
-       minetest.registered_aliases[name] = ""
-       register_alias_raw(name, "")
-end
-
-
--- Deprecated:
--- Aliases for minetest.register_alias (how ironic...)
---minetest.alias_node = minetest.register_alias
---minetest.alias_tool = minetest.register_alias
---minetest.alias_craftitem = minetest.register_alias
-
---
--- Built-in node definitions. Also defined in C.
---
-
-minetest.register_item(":unknown", {
-       type = "none",
-       description = "Unknown Item",
-       inventory_image = "unknown_item.png",
-       on_place = minetest.item_place,
-       on_drop = minetest.item_drop,
-       groups = {not_in_creative_inventory=1},
-       diggable = true,
-})
-
-minetest.register_node(":air", {
-       description = "Air (you hacker you!)",
-       inventory_image = "unknown_node.png",
-       wield_image = "unknown_node.png",
-       drawtype = "airlike",
-       paramtype = "light",
-       sunlight_propagates = true,
-       walkable = false,
-       pointable = false,
-       diggable = false,
-       buildable_to = true,
-       air_equivalent = true,
-       drop = "",
-       groups = {not_in_creative_inventory=1},
-})
-
-minetest.register_node(":ignore", {
-       description = "Ignore (you hacker you!)",
-       inventory_image = "unknown_node.png",
-       wield_image = "unknown_node.png",
-       drawtype = "airlike",
-       paramtype = "none",
-       sunlight_propagates = false,
-       walkable = false,
-       pointable = false,
-       diggable = false,
-       buildable_to = true, -- A way to remove accidentally placed ignores
-       air_equivalent = true,
-       drop = "",
-       groups = {not_in_creative_inventory=1},
-})
-
--- The hand (bare definition)
-minetest.register_item(":", {
-       type = "none",
-       groups = {not_in_creative_inventory=1},
-})
-
-
-function minetest.override_item(name, redefinition)
-       if redefinition.name ~= nil then
-               error("Attempt to redefine name of "..name.." to "..dump(redefinition.name), 2)
-       end
-       if redefinition.type ~= nil then
-               error("Attempt to redefine type of "..name.." to "..dump(redefinition.type), 2)
-       end
-       local item = minetest.registered_items[name]
-       if not item then
-               error("Attempt to override non-existent item "..name, 2)
-       end
-       for k, v in pairs(redefinition) do
-               rawset(item, k, v)
-       end
-       register_item_raw(item)
-end
-
-
-function minetest.run_callbacks(callbacks, mode, ...)
-       assert(type(callbacks) == "table")
-       local cb_len = #callbacks
-       if cb_len == 0 then
-               if mode == 2 or mode == 3 then
-                       return true
-               elseif mode == 4 or mode == 5 then
-                       return false
-               end
-       end
-       local ret = nil
-       for i = 1, cb_len do
-               local cb_ret = callbacks[i](...)
-
-               if mode == 0 and i == 1 then
-                       ret = cb_ret
-               elseif mode == 1 and i == cb_len then
-                       ret = cb_ret
-               elseif mode == 2 then
-                       if not cb_ret or i == 1 then
-                               ret = cb_ret
-                       end
-               elseif mode == 3 then
-                       if cb_ret then
-                               return cb_ret
-                       end
-                       ret = cb_ret
-               elseif mode == 4 then
-                       if (cb_ret and not ret) or i == 1 then
-                               ret = cb_ret
-                       end
-               elseif mode == 5 and cb_ret then
-                       return cb_ret
-               end
-       end
-       return ret
-end
-
---
--- Callback registration
---
-
-local function make_registration()
-       local t = {}
-       local registerfunc = function(func) table.insert(t, func) end
-       return t, registerfunc
-end
-
-local function make_registration_reverse()
-       local t = {}
-       local registerfunc = function(func) table.insert(t, 1, func) end
-       return t, registerfunc
-end
-
-minetest.registered_on_chat_messages, minetest.register_on_chat_message = make_registration()
-minetest.registered_globalsteps, minetest.register_globalstep = make_registration()
-minetest.registered_playerevents, minetest.register_playerevent = make_registration()
-minetest.registered_on_mapgen_inits, minetest.register_on_mapgen_init = make_registration()
-minetest.registered_on_shutdown, minetest.register_on_shutdown = make_registration()
-minetest.registered_on_punchnodes, minetest.register_on_punchnode = make_registration()
-minetest.registered_on_placenodes, minetest.register_on_placenode = make_registration()
-minetest.registered_on_dignodes, minetest.register_on_dignode = make_registration()
-minetest.registered_on_generateds, minetest.register_on_generated = make_registration()
-minetest.registered_on_newplayers, minetest.register_on_newplayer = make_registration()
-minetest.registered_on_dieplayers, minetest.register_on_dieplayer = make_registration()
-minetest.registered_on_respawnplayers, minetest.register_on_respawnplayer = make_registration()
-minetest.registered_on_prejoinplayers, minetest.register_on_prejoinplayer = make_registration()
-minetest.registered_on_joinplayers, minetest.register_on_joinplayer = make_registration()
-minetest.registered_on_leaveplayers, minetest.register_on_leaveplayer = make_registration()
-minetest.registered_on_player_receive_fields, minetest.register_on_player_receive_fields = make_registration_reverse()
-minetest.registered_on_cheats, minetest.register_on_cheat = make_registration()
-minetest.registered_on_crafts, minetest.register_on_craft = make_registration()
-minetest.registered_craft_predicts, minetest.register_craft_predict = make_registration()
-minetest.registered_on_protection_violation, minetest.register_on_protection_violation = make_registration()
-
diff --git a/builtin/mm_menubar.lua b/builtin/mm_menubar.lua
deleted file mode 100644 (file)
index 2e4d5f8..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
---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.
-
-menubar = {}
-
---------------------------------------------------------------------------------
-function menubar.handle_buttons(fields)
-       for i=1,#menubar.buttons,1 do
-               if fields[menubar.buttons[i].btn_name] ~= nil then
-                       menu.last_game = menubar.buttons[i].index
-                       engine.setting_set("main_menu_last_game_idx",menu.last_game)
-                       menu.update_gametype()
-               end
-       end
-end
-
---------------------------------------------------------------------------------
-function menubar.refresh()
-
-       menubar.formspec = "box[-0.3,5.625;12.4,1.2;#000000]" ..
-                                          "box[-0.3,5.6;12.4,0.05;#FFFFFF]"
-       menubar.buttons = {}
-
-       local button_base = -0.08
-       
-       local maxbuttons = #gamemgr.games
-       
-       if maxbuttons > 11 then
-               maxbuttons = 11
-       end
-       
-       for i=1,maxbuttons,1 do
-
-               local btn_name = "menubar_btn_" .. gamemgr.games[i].id
-               local buttonpos = button_base + (i-1) * 1.1
-               if gamemgr.games[i].menuicon_path ~= nil and
-                       gamemgr.games[i].menuicon_path ~= "" then
-
-                       menubar.formspec = menubar.formspec ..
-                               "image_button[" .. buttonpos ..  ",5.72;1.165,1.175;"  ..
-                               engine.formspec_escape(gamemgr.games[i].menuicon_path) .. ";" ..
-                               btn_name .. ";;true;false]"
-               else
-               
-                       local part1 = gamemgr.games[i].id:sub(1,5)
-                       local part2 = gamemgr.games[i].id:sub(6,10)
-                       local part3 = gamemgr.games[i].id:sub(11)
-                       
-                       local text = part1 .. "\n" .. part2
-                       if part3 ~= nil and
-                               part3 ~= "" then
-                               text = text .. "\n" .. part3
-                       end
-                       menubar.formspec = menubar.formspec ..
-                               "image_button[" .. buttonpos ..  ",5.72;1.165,1.175;;" ..btn_name ..
-                               ";" .. text .. ";true;true]"
-               end
-               
-               local toadd = {
-                       btn_name = btn_name,
-                       index = i,
-               }
-               
-               table.insert(menubar.buttons,toadd)
-       end
-end
diff --git a/builtin/mm_textures.lua b/builtin/mm_textures.lua
deleted file mode 100644 (file)
index 998fc21..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
---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.
-
-
-mm_texture = {}
-
---------------------------------------------------------------------------------
-function mm_texture.init()
-       mm_texture.defaulttexturedir = engine.get_texturepath() .. DIR_DELIM .. "base" .. 
-                                               DIR_DELIM .. "pack" .. DIR_DELIM
-       mm_texture.basetexturedir = mm_texture.defaulttexturedir
-       
-       mm_texture.texturepack = engine.setting_get("texture_path")
-       
-       mm_texture.gameid = nil
-end
-
---------------------------------------------------------------------------------
-function mm_texture.update(tab,gamedetails)
-       if tab ~= "singleplayer" then
-               mm_texture.reset()
-               return
-       end
-
-       if gamedetails == nil then
-               return
-       end
-       
-       mm_texture.update_game(gamedetails)
-end
-
---------------------------------------------------------------------------------
-function mm_texture.reset()
-       mm_texture.gameid = nil
-       local have_bg      = false
-       local have_overlay = mm_texture.set_generic("overlay")
-       
-       if not have_overlay then
-               have_bg = mm_texture.set_generic("background")
-       end
-       
-       mm_texture.clear("header")
-       mm_texture.clear("footer")
-       engine.set_clouds(false)
-       
-       mm_texture.set_generic("footer")
-       mm_texture.set_generic("header")
-       
-       if not have_bg and
-               engine.setting_getbool("enable_clouds") then
-                       engine.set_clouds(true)
-       end
-end
-
---------------------------------------------------------------------------------
-function mm_texture.update_game(gamedetails)
-       if mm_texture.gameid == gamedetails.id then
-               return
-       end
-       
-       local have_bg      = false 
-       local have_overlay = mm_texture.set_game("overlay",gamedetails)
-       
-       if not have_overlay then
-               have_bg = mm_texture.set_game("background",gamedetails)
-       end
-       
-       mm_texture.clear("header")
-       mm_texture.clear("footer")
-       engine.set_clouds(false)
-       
-       if not have_bg and
-               engine.setting_getbool("enable_clouds") then
-                       engine.set_clouds(true)
-       end
-       
-       mm_texture.set_game("footer",gamedetails)
-       mm_texture.set_game("header",gamedetails)
-       
-       mm_texture.gameid = gamedetails.id
-end
-
---------------------------------------------------------------------------------
-function mm_texture.clear(identifier)
-       engine.set_background(identifier,"")
-end
-
---------------------------------------------------------------------------------
-function mm_texture.set_generic(identifier)
-       --try texture pack first
-       if mm_texture.texturepack ~= nil then
-               local path = mm_texture.texturepack .. DIR_DELIM .."menu_" .. 
-                                                                               identifier .. ".png"
-               if engine.set_background(identifier,path) then
-                       return true
-               end
-       end
-       
-       if mm_texture.defaulttexturedir ~= nil then
-               local path = mm_texture.defaulttexturedir .. DIR_DELIM .."menu_" .. 
-                                                                               identifier .. ".png"
-               if engine.set_background(identifier,path) then
-                       return true
-               end
-       end
-       
-       return false
-end
-
---------------------------------------------------------------------------------
-function mm_texture.set_game(identifier,gamedetails)
-       
-       if gamedetails == nil then
-               return false
-       end
-
-       if mm_texture.texturepack ~= nil then
-               local path = mm_texture.texturepack .. DIR_DELIM ..
-                                               gamedetails.id .. "_menu_" .. identifier .. ".png"
-               if engine.set_background(identifier,path) then
-                       return true
-               end
-       end
-       
-       local path = gamedetails.path .. DIR_DELIM .."menu" .. 
-                                                                        DIR_DELIM .. identifier .. ".png"
-       if engine.set_background(identifier,path) then
-               return true
-       end
-       
-       return false
-end
diff --git a/builtin/modmgr.lua b/builtin/modmgr.lua
deleted file mode 100644 (file)
index eeb65ad..0000000
+++ /dev/null
@@ -1,1130 +0,0 @@
---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,modpack)
-
-       local mods = engine.get_dirlist(path,true)
-       for i=1,#mods,1 do
-               local toadd = {}
-               local modpackfile = nil
-
-               toadd.name              = mods[i]
-               toadd.path              = path .. DIR_DELIM .. mods[i] .. DIR_DELIM
-               if modpack ~= nil and
-                       modpack ~= "" then
-                       toadd.modpack   = modpack
-               else
-                       local filename = path .. DIR_DELIM .. mods[i] .. DIR_DELIM .. "modpack.txt"
-                       local error = nil
-                       modpackfile,error = io.open(filename,"r")
-               end
-
-               if modpackfile ~= nil then
-                       modpackfile:close()
-                       toadd.is_modpack = true
-                       table.insert(retval,toadd)
-                       get_mods(path .. DIR_DELIM .. mods[i],retval,mods[i])
-               else
-                       table.insert(retval,toadd)
-               end
-       end
-end
-
---modmanager implementation
-modmgr = {}
-
---------------------------------------------------------------------------------
-function modmgr.extract(modfile)
-       if modfile.type == "zip" then
-               local tempfolder = os.tempfolder()
-
-               if tempfolder ~= nil and
-                       tempfolder ~= "" then
-                       engine.create_dir(tempfolder)
-                       if engine.extract_zip(modfile.name,tempfolder) then
-                               return tempfolder
-                       end
-               end
-       end
-       return nil
-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
-                               local retval = item:sub(1,pos3-1)
-                               if retval ~= nil and
-                                       retval ~= "" then
-                                       return retval
-                               end
-                       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.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.global_mods == nil then
-               modmgr.refresh_globals()
-       end
-
-       if modmgr.selected_mod == nil then
-               modmgr.selected_mod = 1
-       end
-
-       local retval =
-               "vertlabel[0,-0.25;".. fgettext("MODS") .. "]" ..
-               "label[0.8,-0.25;".. fgettext("Installed Mods:") .. "]" ..
-               "textlist[0.75,0.25;4.5,4;modlist;" ..
-               modmgr.render_modlist(modmgr.global_mods) ..
-               ";" .. modmgr.selected_mod .. "]"
-
-       retval = retval ..
-               "label[0.8,4.2;" .. fgettext("Add mod:") .. "]" ..
---             TODO Disabled due to upcoming release 0.4.8 and irrlicht messing up localization
---             "button[0.75,4.85;1.8,0.5;btn_mod_mgr_install_local;".. fgettext("Local install") .. "]" ..
-               "button[2.45,4.85;3.05,0.5;btn_mod_mgr_download;".. fgettext("Online mod repository") .. "]"
-
-       local selected_mod = nil
-
-       if filterlist.size(modmgr.global_mods) >= modmgr.selected_mod then
-               selected_mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
-       end
-
-       if selected_mod ~= nil then
-               local modscreenshot = nil
-
-               --check for screenshot beeing available
-               local screenshotfilename = selected_mod.path .. DIR_DELIM .. "screenshot.png"
-               local error = nil
-               screenshotfile,error = io.open(screenshotfilename,"r")
-               if error == nil then
-                       screenshotfile:close()
-                       modscreenshot = screenshotfilename
-               end
-
-               if modscreenshot == nil then
-                               modscreenshot = modstore.basetexturedir .. "no_screenshot.png"
-               end
-
-               retval = retval
-                               .. "image[5.5,0;3,2;" .. engine.formspec_escape(modscreenshot) .. "]"
-                               .. "label[8.25,0.6;" .. selected_mod.name .. "]"
-
-               local descriptionlines = nil
-               error = nil
-               local descriptionfilename = selected_mod.path .. "description.txt"
-               descriptionfile,error = io.open(descriptionfilename,"r")
-               if error == nil then
-                       descriptiontext = descriptionfile:read("*all")
-
-                       descriptionlines = engine.splittext(descriptiontext,42)
-                       descriptionfile:close()
-               else
-                       descriptionlines = {}
-                       table.insert(descriptionlines,fgettext("No mod description available"))
-               end
-
-               retval = retval ..
-                       "label[5.5,1.7;".. fgettext("Mod information:") .. "]" ..
-                       "textlist[5.5,2.2;6.2,2.4;description;"
-
-               for i=1,#descriptionlines,1 do
-                       retval = retval .. engine.formspec_escape(descriptionlines[i]) .. ","
-               end
-
-
-               if selected_mod.is_modpack then
-                       retval = retval .. ";0]" ..
-                               "button[10,4.85;2,0.5;btn_mod_mgr_rename_modpack;" ..
-                               fgettext("Rename") .. "]"
-                       retval = retval .. "button[5.5,4.85;4.5,0.5;btn_mod_mgr_delete_mod;"
-                               .. fgettext("Uninstall selected modpack") .. "]"
-               else
-                       --show dependencies
-
-                       retval = retval .. ",Depends:,"
-
-                       toadd = modmgr.get_dependencies(selected_mod.path)
-
-                       retval = retval .. toadd .. ";0]"
-
-                       retval = retval .. "button[5.5,4.85;4.5,0.5;btn_mod_mgr_delete_mod;"
-                               .. fgettext("Uninstall selected mod") .. "]"
-               end
-       end
-       return retval
-end
-
---------------------------------------------------------------------------------
-function modmgr.dialog_rename_modpack()
-
-       local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
-
-       local retval =
-               "label[1.75,1;".. fgettext("Rename Modpack:") .. "]"..
-               "field[4.5,1.4;6,0.5;te_modpack_name;;" ..
-               mod.name ..
-               "]" ..
-               "button[5,4.2;2.6,0.5;dlg_rename_modpack_confirm;"..
-                               fgettext("Accept") .. "]" ..
-               "button[7.5,4.2;2.8,0.5;dlg_rename_modpack_cancel;"..
-                               fgettext("Cancel") .. "]"
-
-       return retval
-end
-
---------------------------------------------------------------------------------
-function modmgr.precheck()
-
-       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
-
-       if modmgr.hide_modpackcontents == nil then
-               modmgr.hide_modpackcontents = true
-       end
-end
-
---------------------------------------------------------------------------------
-function modmgr.render_modlist(render_list)
-       local retval = ""
-
-       if render_list == nil then
-               if modmgr.global_mods == nil then
-                       modmgr.refresh_globals()
-               end
-               render_list = modmgr.global_mods
-       end
-
-       local list = filterlist.get_list(render_list)
-       local last_modpack = nil
-
-       for i,v in ipairs(list) do
-               if retval ~= "" then
-                       retval = retval ..","
-               end
-
-               local color = ""
-
-               if v.is_modpack then
-                       local rawlist = filterlist.get_raw_list(render_list)
-
-                       local all_enabled = true
-                       for j=1,#rawlist,1 do
-                               if rawlist[j].modpack == list[i].name and
-                                       rawlist[j].enabled ~= true then
-                                               all_enabled = false
-                                               break
-                               end
-                       end
-
-                       if all_enabled == false then
-                               color = mt_color_grey
-                       else
-                               color = mt_color_dark_green
-                       end
-               end
-
-               if v.typ == "game_mod" then
-                       color = mt_color_blue
-               else
-                       if v.enabled then
-                               color = mt_color_green
-                       end
-               end
-
-               retval = retval .. color
-               if v.modpack  ~= nil then
-                       retval = retval .. "    "
-               end
-               retval = retval .. v.name
-       end
-
-       return retval
-end
-
---------------------------------------------------------------------------------
-function modmgr.dialog_configure_world()
-       modmgr.precheck()
-
-       local worldspec = engine.get_worlds()[modmgr.world_config_selected_world]
-       local mod = filterlist.get_list(modmgr.modlist)[modmgr.world_config_selected_mod]
-
-       local retval =
-               "size[11,6.5,true]" ..
-               "label[0.5,-0.25;" .. fgettext("World:") .. "]" ..
-               "label[1.75,-0.25;" .. worldspec.name .. "]"
-
-       if modmgr.hide_gamemods then
-               retval = retval .. "checkbox[0,5.75;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";true]"
-       else
-               retval = retval .. "checkbox[0,5.75;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";false]"
-       end
-
-       if modmgr.hide_modpackcontents then
-               retval = retval .. "checkbox[2,5.75;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";true]"
-       else
-               retval = retval .. "checkbox[2,5.75;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";false]"
-       end
-
-       if mod == nil then
-               mod = {name=""}
-       end
-       retval = retval ..
-               "label[0,0.45;" .. fgettext("Mod:") .. "]" ..
-               "label[0.75,0.45;" .. mod.name .. "]" ..
-               "label[0,1;" .. fgettext("Depends:") .. "]" ..
-               "textlist[0,1.5;5,4.25;world_config_depends;" ..
-               modmgr.get_dependencies(mod.path) .. ";0]" ..
-               "button[9.25,6.35;2,0.5;btn_config_world_save;" .. fgettext("Save") .. "]" ..
-               "button[7.4,6.35;2,0.5;btn_config_world_cancel;" .. fgettext("Cancel") .. "]"
-
-       if mod ~= nil and mod.name ~= "" and mod.typ ~= "game_mod" then
-               if mod.is_modpack then
-                       local rawlist = filterlist.get_raw_list(modmgr.modlist)
-
-                       local all_enabled = true
-                       for j=1,#rawlist,1 do
-                               if rawlist[j].modpack == mod.name and
-                                       rawlist[j].enabled ~= true then
-                                               all_enabled = false
-                                               break
-                               end
-                       end
-
-                       if all_enabled == false then
-                               retval = retval .. "button[5.5,-0.125;2,0.5;btn_mp_enable;" .. fgettext("Enable MP") .. "]"
-                       else
-                               retval = retval .. "button[5.5,-0.125;2,0.5;btn_mp_disable;" .. fgettext("Disable MP") .. "]"
-                       end
-               else
-                       if mod.enabled then
-                               retval = retval .. "checkbox[5.5,-0.375;cb_mod_enable;" .. fgettext("enabled") .. ";true]"
-                       else
-                               retval = retval .. "checkbox[5.5,-0.375;cb_mod_enable;" .. fgettext("enabled") .. ";false]"
-                       end
-               end
-       end
-
-       retval = retval ..
-               "button[8.5,-0.125;2.5,0.5;btn_all_mods;" .. fgettext("Enable all") .. "]" ..
-               "textlist[5.5,0.5;5.5,5.75;world_config_modlist;"
-
-       retval = retval .. modmgr.render_modlist(modmgr.modlist)
-
-       retval = retval .. ";" .. modmgr.world_config_selected_mod .."]"
-
-       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_dependencies(modfolder)
-       local toadd = ""
-       if modfolder ~= nil then
-               local filename = modfolder ..
-                                       DIR_DELIM .. "depends.txt"
-
-               local dependencyfile = io.open(filename,"r")
-
-               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()
-               end
-       end
-
-       return toadd
-end
-
-
---------------------------------------------------------------------------------
-function modmgr.get_worldconfig(worldpath)
-       local filename = worldpath ..
-                               DIR_DELIM .. "world.mt"
-
-       local worldfile = Settings(filename)
-
-       local worldconfig = {}
-       worldconfig.global_mods = {}
-       worldconfig.game_mods = {}
-
-       for key,value in pairs(worldfile:to_table()) do
-               if key == "gameid" then
-                       worldconfig.id = value
-               else
-                       worldconfig.global_mods[key] = engine.is_yes(value)
-               end
-       end
-
-       --read gamemods
-       local gamespec = gamemgr.find_by_gameid(worldconfig.id)
-       gamemgr.get_game_mods(gamespec, 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 = engine.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",fgettext("Select Mod File:"))
-       end
-
-       if fields["btn_mod_mgr_download"] ~= nil then
-               modstore.update_modlist()
-               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 = modmgr.identify_filetype(modfilename)
-       local modpath = modmgr.extract(modfile)
-
-       if modpath == nil then
-               gamedata.errormessage = fgettext("Install Mod: file: \"$1\"", modfile.name) ..
-                       fgettext("\nInstall Mod: unsupported filetype \"$1\" or broken archive", 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
-                       if not engine.copy_dir(basefolder.path,targetpath) then
-                               gamedata.errormessage = fgettext("Failed to install $1 to $2", basename, targetpath)
-                       end
-               else
-                       gamedata.errormessage = fgettext("Install Mod: unable to find suitable foldername for modpack $1", 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 = fgettext("Install Mod: unable to find real modname for: $1", modfilename)
-               end
-       end
-
-       engine.delete_dir(modpath)
-
-       modmgr.refresh_globals()
-
-end
-
---------------------------------------------------------------------------------
-function modmgr.handle_rename_modpack_buttons(fields)
-
-       if fields["dlg_rename_modpack_confirm"] ~= nil then
-               local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
-               local oldpath = engine.get_modpath() .. DIR_DELIM .. mod.name
-               local targetpath = engine.get_modpath() .. DIR_DELIM .. fields["te_modpack_name"]
-               engine.copy_dir(oldpath,targetpath,false)
-               modmgr.refresh_globals()
-               modmgr.selected_mod = filterlist.get_current_index(modmgr.global_mods,
-                       filterlist.raw_index_by_uid(modmgr.global_mods, fields["te_modpack_name"]))
-       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 = engine.explode_textlist_event(fields["world_config_modlist"])
-               modmgr.world_config_selected_mod = event.index
-
-               if event.type == "DCL" then
-                       modmgr.world_config_enable_mod(nil)
-               end
-       end
-
-       if fields["key_enter"] ~= nil then
-               modmgr.world_config_enable_mod(nil)
-       end
-
-       if fields["cb_mod_enable"] ~= nil then
-               local toset = engine.is_yes(fields["cb_mod_enable"])
-               modmgr.world_config_enable_mod(toset)
-       end
-
-       if fields["btn_mp_enable"] ~= nil or
-               fields["btn_mp_disable"] then
-               local toset = (fields["btn_mp_enable"] ~= nil)
-               modmgr.world_config_enable_mod(toset)
-       end
-
-       if fields["cb_hide_gamemods"] ~= nil then
-               local current = filterlist.get_filtercriteria(modmgr.modlist)
-
-               if current == nil then
-                       current = {}
-               end
-
-               if engine.is_yes(fields["cb_hide_gamemods"]) then
-                       current.hide_game = true
-                       modmgr.hide_gamemods = true
-               else
-                       current.hide_game = false
-                       modmgr.hide_gamemods = false
-               end
-
-               filterlist.set_filtercriteria(modmgr.modlist,current)
-       end
-
-               if fields["cb_hide_mpcontent"] ~= nil then
-               local current = filterlist.get_filtercriteria(modmgr.modlist)
-
-               if current == nil then
-                       current = {}
-               end
-
-               if engine.is_yes(fields["cb_hide_mpcontent"]) then
-                       current.hide_modpackcontents = true
-                       modmgr.hide_modpackcontents = true
-               else
-                       current.hide_modpackcontents = false
-                       modmgr.hide_modpackcontents = false
-               end
-
-               filterlist.set_filtercriteria(modmgr.modlist,current)
-       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 = Settings(filename)
-               local mods = worldfile:to_table()
-
-               local rawlist = filterlist.get_raw_list(modmgr.modlist)
-
-               local i,mod
-               for i,mod in ipairs(rawlist) do
-                       if not mod.is_modpack and
-                                       mod.typ ~= "game_mod" then
-                               if mod.enabled then
-                                       worldfile:set("load_mod_"..mod.name, "true")
-                               else
-                                       worldfile:set("load_mod_"..mod.name, "false")
-                               end
-                               mods["load_mod_"..mod.name] = nil
-                       end
-               end
-
-               -- Remove mods that are not present anymore
-               for key,value in pairs(mods) do
-                       if key:sub(1,9) == "load_mod_" then
-                               worldfile:remove(key)
-                       end
-               end
-
-               if not worldfile:write() then
-                       engine.log("error", "Failed to write world config file")
-               end
-
-               modmgr.modlist = nil
-               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_all_mods"] then
-               local list = filterlist.get_raw_list(modmgr.modlist)
-
-               for i=1,#list,1 do
-                       if list[i].typ ~= "game_mod" and
-                               not list[i].is_modpack then
-                               list[i].enabled = true
-                       end
-               end
-       end
-
-
-
-       return nil
-end
---------------------------------------------------------------------------------
-function modmgr.world_config_enable_mod(toset)
-       local mod = filterlist.get_list(modmgr.modlist)
-               [engine.get_textlist_index("world_config_modlist")]
-
-       if mod.typ == "game_mod" then
-               -- game mods can't be enabled or disabled
-       elseif not mod.is_modpack then
-               if toset == nil then
-                       mod.enabled = not mod.enabled
-               else
-                       mod.enabled = toset
-               end
-       else
-               local list = filterlist.get_raw_list(modmgr.modlist)
-               for i=1,#list,1 do
-                       if list[i].modpack == mod.name then
-                               if toset == nil then
-                                       toset = not list[i].enabled
-                               end
-                               list[i].enabled = toset
-                       end
-               end
-       end
-end
---------------------------------------------------------------------------------
-function modmgr.handle_delete_mod_buttons(fields)
-       local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
-
-       if fields["dlg_delete_mod_confirm"] ~= nil then
-
-               if mod.path ~= nil and
-                       mod.path ~= "" and
-                       mod.path ~= engine.get_modpath() then
-                       if not engine.delete_dir(mod.path) then
-                               gamedata.errormessage = fgettext("Modmgr: failed to delete \"$1\"", mod.path)
-                       end
-                       modmgr.refresh_globals()
-               else
-                       gamedata.errormessage = fgettext("Modmgr: invalid modpath \"$1\"", mod.path)
-               end
-       end
-
-       return {
-               is_dialog = false,
-               show_buttons = true,
-               current_tab = engine.setting_get("main_menu_tab")
-               }
-end
-
---------------------------------------------------------------------------------
-function modmgr.dialog_delete_mod()
-
-       local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod]
-
-       local retval =
-               "field[1.75,1;10,3;;" .. fgettext("Are you sure you want to delete \"$1\"?", mod.name) ..  ";]"..
-               "button[4,4.2;1,0.5;dlg_delete_mod_confirm;" .. fgettext("Yes") .. "]" ..
-               "button[6.5,4.2;3,0.5;dlg_delete_mod_cancel;" .. fgettext("No of course not!") .. "]"
-
-       return retval
-end
-
---------------------------------------------------------------------------------
-function modmgr.preparemodlist(data)
-       local retval = {}
-
-       local global_mods = {}
-       local game_mods = {}
-
-       --read global mods
-       local modpath = engine.get_modpath()
-
-       if modpath ~= nil and
-               modpath ~= "" then
-               get_mods(modpath,global_mods)
-       end
-
-       for i=1,#global_mods,1 do
-               global_mods[i].typ = "global_mod"
-               table.insert(retval,global_mods[i])
-       end
-
-       --read game mods
-       local gamespec = gamemgr.find_by_gameid(data.gameid)
-       gamemgr.get_game_mods(gamespec, game_mods)
-
-       for i=1,#game_mods,1 do
-               game_mods[i].typ = "game_mod"
-               table.insert(retval,game_mods[i])
-       end
-
-       if data.worldpath == nil then
-               return retval
-       end
-
-       --read world mod configuration
-       local filename = data.worldpath ..
-                               DIR_DELIM .. "world.mt"
-
-       local worldfile = Settings(filename)
-
-       for key,value in pairs(worldfile:to_table()) do
-               if key:sub(1, 9) == "load_mod_" then
-                       key = key:sub(10)
-                       local element = nil
-                       for i=1,#retval,1 do
-                               if retval[i].name == key then
-                                       element = retval[i]
-                                       break
-                               end
-                       end
-                       if element ~= nil then
-                               element.enabled = engine.is_yes(value)
-                       else
-                               engine.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
-                       end
-               end
-       end
-
-       return retval
-end
-
---------------------------------------------------------------------------------
-function modmgr.init_worldconfig()
-       modmgr.precheck()
-       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
-
-               modmgr.modlist = filterlist.create(
-                                               modmgr.preparemodlist, --refresh
-                                               modmgr.comparemod, --compare
-                                               function(element,uid) --uid match
-                                                       if element.name == uid then
-                                                               return true
-                                                       end
-                                               end,
-                                               function(element,criteria)
-                                                       if criteria.hide_game and
-                                                               element.typ == "game_mod" then
-                                                                       return false
-                                                       end
-
-                                                       if criteria.hide_modpackcontents and
-                                                               element.modpack ~= nil then
-                                                                       return false
-                                                               end
-                                                       return true
-                                               end, --filter
-                                               { worldpath= worldspec.path,
-                                                 gameid = worldspec.gameid }
-                                       )
-
-               filterlist.set_filtercriteria(modmgr.modlist, {
-                                                                       hide_game=modmgr.hide_gamemods,
-                                                                       hide_modpackcontents= modmgr.hide_modpackcontents
-                                                                       })
-               filterlist.add_sort_mechanism(modmgr.modlist, "alphabetic", sort_mod_list)
-               filterlist.set_sortmode(modmgr.modlist, "alphabetic")
-
-               return true
-       end
-
-       return false
-end
-
---------------------------------------------------------------------------------
-function modmgr.comparemod(elem1,elem2)
-       if elem1 == nil or elem2 == nil then
-               return false
-       end
-       if elem1.name ~= elem2.name then
-               return false
-       end
-       if elem1.is_modpack ~= elem2.is_modpack then
-               return false
-       end
-       if elem1.typ ~= elem2.typ then
-               return false
-       end
-       if elem1.modpack ~= elem2.modpack then
-               return false
-       end
-
-       if elem1.path ~= elem2.path then
-               return false
-       end
-
-       return true
-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
-
---------------------------------------------------------------------------------
-function modmgr.mod_exists(basename)
-
-       if modmgr.global_mods == nil then
-               modmgr.refresh_globals()
-       end
-
-       if filterlist.raw_index_by_uid(modmgr.global_mods,basename) > 0 then
-               return true
-       end
-
-       return false
-end
-
---------------------------------------------------------------------------------
-function modmgr.get_global_mod(idx)
-
-       if modmgr.global_mods == nil then
-               return nil
-       end
-
-       if idx == nil or idx < 1 or idx > filterlist.size(modmgr.global_mods) then
-               return nil
-       end
-
-       return filterlist.get_list(modmgr.global_mods)[idx]
-end
-
---------------------------------------------------------------------------------
-function modmgr.refresh_globals()
-       modmgr.global_mods = filterlist.create(
-                                       modmgr.preparemodlist, --refresh
-                                       modmgr.comparemod, --compare
-                                       function(element,uid) --uid match
-                                               if element.name == uid then
-                                                       return true
-                                               end
-                                       end,
-                                       nil, --filter
-                                       {}
-                                       )
-       filterlist.add_sort_mechanism(modmgr.global_mods, "alphabetic", sort_mod_list)
-       filterlist.set_sortmode(modmgr.global_mods, "alphabetic")
-end
-
---------------------------------------------------------------------------------
-function modmgr.identify_filetype(name)
-
-       if name:sub(-3):lower() == "zip" then
-               return {
-                               name = name,
-                               type = "zip"
-                               }
-       end
-
-       if name:sub(-6):lower() == "tar.gz" or
-               name:sub(-3):lower() == "tgz"then
-               return {
-                               name = name,
-                               type = "tgz"
-                               }
-       end
-
-       if name:sub(-6):lower() == "tar.bz2" then
-               return {
-                               name = name,
-                               type = "tbz"
-                               }
-       end
-
-       if name:sub(-2):lower() == "7z" then
-               return {
-                               name = name,
-                               type = "7z"
-                               }
-       end
-
-       return {
-               name = name,
-               type = "ukn"
-       }
-end
diff --git a/builtin/modstore.lua b/builtin/modstore.lua
deleted file mode 100644 (file)
index ef7fd01..0000000
+++ /dev/null
@@ -1,615 +0,0 @@
---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 [parent=#modstore] init
-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_texturepath() .. DIR_DELIM .. "base" ..
-                                               DIR_DELIM .. "pack" .. DIR_DELIM
-
-       modstore.lastmodtitle = ""
-       modstore.last_search = ""
-       
-       modstore.searchlist = filterlist.create(
-               function()
-                       if modstore.modlist_unsorted ~= nil and
-                               modstore.modlist_unsorted.data ~= nil then
-                               return modstore.modlist_unsorted.data
-                       end
-                       return {}
-               end,
-               function(element,modid)
-                       if element.id == modid then
-                               return true
-                       end
-                       return false
-               end, --compare fct
-               nil, --uid match fct
-               function(element,substring)
-                       if substring == nil or
-                               substring == "" then
-                               return false
-                       end
-                       substring = substring:upper()
-                       
-                       if element.title ~= nil and
-                               element.title:upper():find(substring) ~= nil then
-                               return true
-                       end
-                       
-                       if element.details ~= nil and
-                               element.details.author ~= nil and
-                               element.details.author:upper():find(substring) ~= nil then
-                               return true
-                       end
-                       
-                       if element.details ~= nil and
-                               element.details.description ~= nil and
-                               element.details.description:upper():find(substring) ~= nil then
-                               return true
-                       end
-                       return false
-               end --filter fct
-               )
-
-       modstore.current_list = nil
-end
-
---------------------------------------------------------------------------------
--- @function [parent=#modstore] nametoindex
-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 [parent=#modstore] getsuccessfuldialog
-function modstore.getsuccessfuldialog()
-       local retval = ""
-       retval = retval .. "size[6,2,true]"
-       if modstore.lastmodentry ~= nil then
-               retval = retval .. "label[0,0.25;" .. fgettext("Successfully installed:") .. "]"
-               retval = retval .. "label[3,0.25;" .. modstore.lastmodentry.moddetails.title .. "]"
-       
-               
-               retval = retval .. "label[0,0.75;" .. fgettext("Shortname:") .. "]"
-               retval = retval .. "label[3,0.75;" .. engine.formspec_escape(modstore.lastmodentry.moddetails.basename) .. "]"
-
-       end
-       retval = retval .. "button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;" .. fgettext("ok") .. "]"
-                               
-                               
-       return retval
-end
-
---------------------------------------------------------------------------------
--- @function [parent=#modstore] gettab
-function modstore.gettab(tabname)
-       local retval = ""
-
-       local is_modstore_tab = false
-
-       if tabname == "dialog_modstore_unsorted" then
-               modstore.modsperpage = 5
-               retval = modstore.getmodlist(modstore.modlist_unsorted)
-               is_modstore_tab = true
-       end
-
-       if tabname == "dialog_modstore_search" then
-               retval = modstore.getsearchpage()
-               is_modstore_tab = true
-       end
-
-       if is_modstore_tab then
-               return modstore.tabheader(tabname) .. retval
-       end
-
-       if tabname == "modstore_mod_installed" then
-               return modstore.getsuccessfuldialog()
-       end
-
-       if tabname == "modstore_downloading" then
-               return "size[6,2]label[0.25,0.75;" .. fgettext("Downloading") ..
-                               " " .. modstore.lastmodtitle .. " " ..
-                               fgettext("please wait...") .. "]"
-       end
-
-       return ""
-end
-
---------------------------------------------------------------------------------
--- @function [parent=#modstore] tabheader
-function modstore.tabheader(tabname)
-       local retval  = "size[12,10.25,true]"
-       retval = retval .. "tabheader[-0.3,-0.99;modstore_tab;" ..
-                               "Unsorted,Search;" ..
-                               modstore.nametoindex(tabname) .. ";true;false]" ..
-                               "button[4,9.9;4,0.5;btn_modstore_close;" ..
-                               fgettext("Close modstore") .. "]"
-
-       return retval
-end
-
---------------------------------------------------------------------------------
--- @function [parent=#modstore] handle_buttons
-function modstore.handle_buttons(current_tab,fields)
-
-       if fields["modstore_tab"] then
-               local index = tonumber(fields["modstore_tab"])
-
-               if index > 0 and
-                       index <= #modstore.tabnames then
-                       if modstore.tabnames[index] == "dialog_modstore_search" then
-                               filterlist.set_filtercriteria(modstore.searchlist,modstore.last_search)
-                               filterlist.refresh(modstore.searchlist)
-                               modstore.modsperpage = 4
-                               modstore.currentlist = {
-                                       page = 0,
-                                       pagecount =
-                                               math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage),
-                                       data = filterlist.get_list(modstore.searchlist),
-                               }
-                       end
-                       
-                       return {
-                                       current_tab = modstore.tabnames[index],
-                                       is_dialog = true,
-                                       show_buttons = false
-                       }
-               end
-               
-       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-1 then
-                       modstore.current_list.page = modstore.current_list.page +1
-               end
-       end
-
-       if fields["btn_hidden_close_download"] ~= nil then
-               if fields["btn_hidden_close_download"].successfull then
-                       modstore.lastmodentry = fields["btn_hidden_close_download"]
-                       return {
-                                       current_tab = "modstore_mod_installed",
-                                       is_dialog = true,
-                                       show_buttons = false
-                       }
-               else
-                       modstore.lastmodtitle = ""
-                       return {
-                                               current_tab = modstore.tabnames[1],
-                                               is_dialog = true,
-                                               show_buttons = false
-                               }
-               end
-       end
-
-       if fields["btn_confirm_mod_successfull"] then
-               modstore.lastmodentry = nil
-               modstore.lastmodtitle = ""
-               return {
-                                       current_tab = modstore.tabnames[1],
-                                       is_dialog = true,
-                                       show_buttons = false
-                       }
-       end
-
-       if fields["btn_modstore_search"] or
-               (fields["key_enter"] and fields["te_modstore_search"] ~= nil) then
-               modstore.last_search = fields["te_modstore_search"]
-               filterlist.set_filtercriteria(modstore.searchlist,fields["te_modstore_search"])
-               filterlist.refresh(modstore.searchlist)
-               modstore.currentlist = {
-                       page = 0,
-                       pagecount =  math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage),
-                       data = filterlist.get_list(modstore.searchlist),
-               }
-       end
-       
-       
-       if fields["btn_modstore_close"] then
-               return {
-                       is_dialog = false,
-                       show_buttons = true,
-                       current_tab = engine.setting_get("main_menu_tab")
-               }
-       end
-       
-       for key,value in pairs(fields) do
-               local foundat = key:find("btn_install_mod_")
-               if ( foundat == 1) then
-                       local modid = tonumber(key:sub(17))
-                       for i=1,#modstore.modlist_unsorted.data,1 do
-                               if modstore.modlist_unsorted.data[i].id == modid then
-                                       local moddetails = modstore.modlist_unsorted.data[i].details
-
-                                       if modstore.lastmodtitle ~= "" then
-                                               modstore.lastmodtitle = modstore.lastmodtitle .. ", "
-                                       end
-       
-                                       modstore.lastmodtitle = modstore.lastmodtitle .. moddetails.title
-       
-                                       engine.handle_async(
-                                               function(param)
-                                               
-                                                       local fullurl = engine.setting_get("modstore_download_url") ..
-                                                                                       param.moddetails.download_url
-                                                                                       
-                                                       if param.version ~= nil then
-                                                               local found = false
-                                                               for i=1,#param.moddetails.versions, 1 do
-                                                                       if param.moddetails.versions[i].date:sub(1,10) == param.version then
-                                                                               fullurl = engine.setting_get("modstore_download_url") ..
-                                                                                                               param.moddetails.versions[i].download_url
-                                                                               found = true
-                                                                       end
-                                                               end
-                                                               
-                                                               if not found then
-                                                                       return {
-                                                                               moddetails = param.moddetails,
-                                                                               successfull = false
-                                                                       }
-                                                               end
-                                                       end
-       
-                                                       if engine.download_file(fullurl,param.filename) then
-                                                               return {
-                                                                       texturename = param.texturename,
-                                                                       moddetails = param.moddetails,
-                                                                       filename = param.filename,
-                                                                       successfull = true
-                                                               }
-                                                       else
-                                                               return {
-                                                                       moddetails = param.moddetails,
-                                                                       successfull = false
-                                                               }
-                                                       end
-                                               end,
-                                               {
-                                                       moddetails = moddetails,
-                                                       version = fields["dd_version" .. modid],
-                                                       filename = os.tempfolder() .. "_MODNAME_" .. moddetails.basename .. ".zip",
-                                                       texturename = modstore.modlist_unsorted.data[i].texturename
-                                               },
-                                               function(result)
-                                                       if result.successfull then
-                                                               modmgr.installmod(result.filename,result.moddetails.basename)
-                                                               os.remove(result.filename)
-                                                       else
-                                                               gamedata.errormessage = "Failed to download " .. result.moddetails.title
-                                                       end
-       
-                                                       if gamedata.errormessage == nil then
-                                                               engine.button_handler({btn_hidden_close_download=result})
-                                                       else
-                                                               engine.button_handler({btn_hidden_close_download={successfull=false}})
-                                                       end
-                                               end
-                                       )
-       
-                                       return {
-                                               current_tab = "modstore_downloading",
-                                               is_dialog = true,
-                                               show_buttons = false,
-                                               ignore_menu_quit = true
-                                       }
-                               end
-                       end
-                       break
-               end
-       end
-end
-
---------------------------------------------------------------------------------
--- @function [parent=#modstore] update_modlist
-function modstore.update_modlist()
-       modstore.modlist_unsorted = {}
-       modstore.modlist_unsorted.data = {}
-       modstore.modlist_unsorted.pagecount = 1
-       modstore.modlist_unsorted.page = 0
-
-       engine.handle_async(
-               function(param)
-                       return engine.get_modstore_list()
-               end,
-               nil,
-               function(result)
-                       if result ~= nil then
-                               modstore.modlist_unsorted = {}
-                               modstore.modlist_unsorted.data = result
-
-                               if modstore.modlist_unsorted.data ~= nil then
-                                       modstore.modlist_unsorted.pagecount =
-                                               math.ceil((#modstore.modlist_unsorted.data / modstore.modsperpage))
-                               else
-                                       modstore.modlist_unsorted.data = {}
-                                       modstore.modlist_unsorted.pagecount = 1
-                               end
-                               modstore.modlist_unsorted.page = 0
-                               modstore.fetchdetails()
-                               engine.event_handler("Refresh")
-                       end
-               end
-       )
-end
-
---------------------------------------------------------------------------------
--- @function [parent=#modstore] fetchdetails
-function modstore.fetchdetails()
-
-       for i=1,#modstore.modlist_unsorted.data,1 do
-               engine.handle_async(
-               function(param)
-                       param.details = engine.get_modstore_details(tostring(param.modid))
-                       return param
-               end,
-               {
-                       modid=modstore.modlist_unsorted.data[i].id,
-                       listindex=i
-               },
-               function(result)
-                       if result ~= nil and
-                               modstore.modlist_unsorted ~= nil
-                               and modstore.modlist_unsorted.data ~= nil and
-                               modstore.modlist_unsorted.data[result.listindex] ~= nil and
-                               modstore.modlist_unsorted.data[result.listindex].id ~= nil then
-
-                               modstore.modlist_unsorted.data[result.listindex].details = result.details
-                               engine.event_handler("Refresh")
-                       end
-               end
-               )
-       end
-end
-
---------------------------------------------------------------------------------
--- @function [parent=#modstore] getscreenshot
-function modstore.getscreenshot(ypos,listentry)
-
-       if      listentry.details ~= nil and
-               (listentry.details.screenshot_url == nil or
-               listentry.details.screenshot_url == "") then
-               
-               if listentry.texturename == nil then
-                       listentry.texturename = modstore.basetexturedir .. "no_screenshot.png"
-               end
-               
-               return "image[0,".. ypos .. ";3,2;" ..
-                       engine.formspec_escape(listentry.texturename) .. "]"
-       end
-       
-       if listentry.details ~= nil and
-               listentry.texturename == nil then
-               --make sure we don't download multiple times
-               listentry.texturename = "in progress"
-       
-               --prepare url and filename
-               local fullurl = engine.setting_get("modstore_download_url") ..
-                                       listentry.details.screenshot_url
-               local filename = os.tempfolder() .. "_MID_" .. listentry.id
-               
-               --trigger download
-               engine.handle_async(
-                       --first param is downloadfct
-                       function(param)
-                               param.successfull = engine.download_file(param.fullurl,param.filename)
-                               return param
-                       end,
-                       --second parameter is data passed to async job
-                       {
-                               fullurl = fullurl,
-                               filename = filename,
-                               modid = listentry.id
-                       },
-                       --integrate result to raw list
-                       function(result)
-                               if result.successfull then
-                                       local found = false
-                                       for i=1,#modstore.modlist_unsorted.data,1 do
-                                               if modstore.modlist_unsorted.data[i].id == result.modid then
-                                                       found = true
-                                                       modstore.modlist_unsorted.data[i].texturename = result.filename
-                                                       break
-                                               end
-                                       end
-                                       if found then
-                                               engine.event_handler("Refresh")
-                                       else
-                                               engine.log("error","got screenshot but didn't find matching mod: " .. result.modid)
-                                       end
-                               end
-                       end
-               )
-       end
-
-       if listentry.texturename ~= nil and
-               listentry.texturename ~= "in progress" then
-               return "image[0,".. ypos .. ";3,2;" ..
-                       engine.formspec_escape(listentry.texturename) .. "]"
-       end
-       
-       return ""
-end
-
---------------------------------------------------------------------------------
---@function [parent=#modstore] getshortmodinfo
-function modstore.getshortmodinfo(ypos,listentry,details)
-       local retval = ""
-       
-       retval = retval .. "box[0," .. ypos .. ";11.4,1.75;#FFFFFF]"
-
-       --screenshot
-       retval = retval .. modstore.getscreenshot(ypos,listentry)
-
-       --title + author
-       retval = retval .."label[2.75," .. ypos .. ";" ..
-               engine.formspec_escape(details.title) .. " (" .. details.author .. ")]"
-
-       --description
-       local descriptiony = ypos + 0.5
-       retval = retval .. "textarea[3," .. descriptiony .. ";6.5,1.55;;" ..
-               engine.formspec_escape(details.description) .. ";]"
-               
-       --rating
-       local ratingy = ypos
-       retval = retval .."label[7," .. ratingy .. ";" ..
-                                       fgettext("Rating") .. ":]"
-       retval = retval .. "label[8.7," .. ratingy .. ";" .. details.rating .."]"
-       
-       --versions (IMPORTANT has to be defined AFTER rating)
-       if details.versions ~= nil and
-               #details.versions > 1 then
-               local versiony = ypos + 0.05
-               retval = retval .. "dropdown[9.1," .. versiony .. ";2.48,0.25;dd_version" .. details.id .. ";"
-               local versions = ""
-               for i=1,#details.versions , 1 do
-                       if versions ~= "" then
-                               versions = versions .. ","
-                       end
-                       
-                       versions = versions .. details.versions[i].date:sub(1,10)
-               end
-               retval = retval .. versions .. ";1]"
-       end
-
-       if details.basename then
-               --install button
-               local buttony = ypos + 1.2
-               retval = retval .."button[9.1," .. buttony .. ";2.5,0.5;btn_install_mod_" .. details.id .. ";"
-
-               if modmgr.mod_exists(details.basename) then
-                       retval = retval .. fgettext("re-Install") .."]"
-               else
-                       retval = retval .. fgettext("Install") .."]"
-               end
-       end
-       
-       return retval
-end
-
---------------------------------------------------------------------------------
---@function [parent=#modstore] getmodlist
-function modstore.getmodlist(list,yoffset)
-
-       modstore.current_list = list
-       
-       if #list.data == 0 then
-               return ""
-       end
-       
-       if yoffset == nil then
-               yoffset = 0
-       end
-
-       local scrollbar = ""
-       scrollbar = scrollbar .. "label[0.1,9.5;"
-                               .. fgettext("Page $1 of $2", list.page+1, list.pagecount) .. "]"
-       scrollbar = scrollbar .. "box[11.6," .. (yoffset + 0.35) .. ";0.28,"
-                               .. (8.6 - yoffset) .. ";#000000]"
-       local scrollbarpos = (yoffset + 0.75) +
-                               ((7.7 -yoffset)/(list.pagecount-1)) * list.page
-       scrollbar = scrollbar .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;#32CD32]"
-       scrollbar = scrollbar .. "button[11.6," .. (yoffset + (0.3))
-                               .. ";0.5,0.5;btn_modstore_page_up;^]"
-       scrollbar = scrollbar .. "button[11.6," .. 9.0
-                               .. ";0.5,0.5;btn_modstore_page_down;v]"
-
-       local retval = ""
-
-       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 = list.data[i].details
-
-               if details == nil then
-                       details = {}
-                       details.title = list.data[i].title
-                       details.author = ""
-                       details.rating = -1
-                       details.description = ""
-               end
-
-               if details ~= nil then
-                       local screenshot_ypos =
-                               yoffset +(i-1 - (list.page * modstore.modsperpage))*1.9 +0.2
-                               
-                       retval = retval .. modstore.getshortmodinfo(screenshot_ypos,
-                                                                                                               list.data[i],
-                                                                                                               details)
-               end
-       end
-       
-       return retval .. scrollbar
-end
-
---------------------------------------------------------------------------------
---@function [parent=#modstore] getsearchpage
-function modstore.getsearchpage()
-       local retval = ""
-       local search = ""
-       
-       if modstore.last_search ~= nil then
-               search = modstore.last_search
-       end
-
-       retval = retval ..
-               "button[9.5,0.2;2.5,0.5;btn_modstore_search;".. fgettext("Search") .. "]" ..
-               "field[0.5,0.5;9,0.5;te_modstore_search;;" .. search .. "]"
-
-       
-       --show 4 mods only
-       modstore.modsperpage = 4
-       retval = retval ..
-               modstore.getmodlist(
-                       modstore.currentlist,
-                       1.75)
-
-       return retval;
-end
-
diff --git a/builtin/privileges.lua b/builtin/privileges.lua
deleted file mode 100644 (file)
index 244aa45..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
--- Minetest: builtin/privileges.lua
-
---
--- Privileges
---
-
-minetest.registered_privileges = {}
-
-function minetest.register_privilege(name, param)
-       local function fill_defaults(def)
-               if def.give_to_singleplayer == nil then
-                       def.give_to_singleplayer = true
-               end
-               if def.description == nil then
-                       def.description = "(no description)"
-               end
-       end
-       local def = {}
-       if type(param) == "table" then
-               def = param
-       else
-               def = {description = param}
-       end
-       fill_defaults(def)
-       minetest.registered_privileges[name] = def
-end
-
-minetest.register_privilege("interact", "Can interact with things and modify the world")
-minetest.register_privilege("teleport", "Can use /teleport command")
-minetest.register_privilege("bring", "Can teleport other players")
-minetest.register_privilege("settime", "Can use /time")
-minetest.register_privilege("privs", "Can modify privileges")
-minetest.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges")
-minetest.register_privilege("server", "Can do server maintenance stuff")
-minetest.register_privilege("shout", "Can speak in chat")
-minetest.register_privilege("ban", "Can ban and unban players")
-minetest.register_privilege("kick", "Can kick players")
-minetest.register_privilege("give", "Can use /give and /giveme")
-minetest.register_privilege("password", "Can use /setpassword and /clearpassword")
-minetest.register_privilege("fly", {
-       description = "Can fly using the free_move mode",
-       give_to_singleplayer = false,
-})
-minetest.register_privilege("fast", {
-       description = "Can walk fast using the fast_move mode",
-       give_to_singleplayer = false,
-})
-minetest.register_privilege("noclip", {
-       description = "Can fly through walls",
-       give_to_singleplayer = false,
-})
-minetest.register_privilege("rollback", "Can use the rollback functionality")
-
diff --git a/builtin/serialize.lua b/builtin/serialize.lua
deleted file mode 100644 (file)
index 93fffe8..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
--- Minetest: builtin/serialize.lua
-
--- https://github.com/fab13n/metalua/blob/no-dll/src/lib/serialize.lua
--- Copyright (c) 2006-2997 Fabien Fleutot <metalua@gmail.com>
--- License: MIT
---------------------------------------------------------------------------------
--- Serialize an object into a source code string. This string, when passed as
--- an argument to deserialize(), returns an object structurally identical
--- to the original one. The following are currently supported:
--- * strings, numbers, booleans, nil
--- * tables thereof. Tables can have shared part, but can't be recursive yet.
--- Caveat: metatables and environments aren't saved.
---------------------------------------------------------------------------------
-
-local no_identity = { number=1, boolean=1, string=1, ['nil']=1 }
-
-function minetest.serialize(x)
-
-       local gensym_max   =  0  -- index of the gensym() symbol generator
-       local seen_once    = { } -- element->true set of elements seen exactly once in the table
-       local multiple     = { } -- element->varname set of elements seen more than once
-       local nested       = { } -- transient, set of elements currently being traversed
-       local nest_points  = { }
-       local nest_patches = { }
-       
-       local function gensym()
-               gensym_max = gensym_max + 1 ;  return gensym_max
-       end
-
-       -----------------------------------------------------------------------------
-       -- nest_points are places where a table appears within itself, directly or not.
-       -- for instance, all of these chunks create nest points in table x:
-       -- "x = { }; x[x] = 1", "x = { }; x[1] = x", "x = { }; x[1] = { y = { x } }".
-       -- To handle those, two tables are created by mark_nest_point:
-       -- * nest_points [parent] associates all keys and values in table parent which
-       --   create a nest_point with boolean `true'
-       -- * nest_patches contain a list of { parent, key, value } tuples creating
-       --   a nest point. They're all dumped after all the other table operations
-       --   have been performed.
-       --
-       -- mark_nest_point (p, k, v) fills tables nest_points and nest_patches with
-       -- informations required to remember that key/value (k,v) create a nest point
-       -- in table parent. It also marks `parent' as occuring multiple times, since
-       -- several references to it will be required in order to patch the nest
-       -- points.
-       -----------------------------------------------------------------------------
-       local function mark_nest_point (parent, k, v)
-               local nk, nv = nested[k], nested[v]
-               assert (not nk or seen_once[k] or multiple[k])
-               assert (not nv or seen_once[v] or multiple[v])
-               local mode = (nk and nv and "kv") or (nk and "k") or ("v")
-               local parent_np = nest_points [parent]
-               local pair = { k, v }
-               if not parent_np then parent_np = { }; nest_points [parent] = parent_np end
-               parent_np [k], parent_np [v] = nk, nv
-               table.insert (nest_patches, { parent, k, v })
-               seen_once [parent], multiple [parent]  = nil, true
-       end
-
-       -----------------------------------------------------------------------------
-       -- First pass, list the tables and functions which appear more than once in x
-       -----------------------------------------------------------------------------
-       local function mark_multiple_occurences (x)
-               if no_identity [type(x)] then return end
-               if     seen_once [x]     then seen_once [x], multiple [x] = nil, true
-               elseif multiple  [x]     then -- pass
-               else   seen_once [x] = true end
-               
-               if type (x) == 'table' then
-                       nested [x] = true
-                       for k, v in pairs (x) do
-                               if nested[k] or nested[v] then mark_nest_point (x, k, v) else
-                                       mark_multiple_occurences (k)
-                                       mark_multiple_occurences (v)
-                               end
-                       end
-                       nested [x] = nil
-               end
-       end
-
-       local dumped    = { } -- multiply occuring values already dumped in localdefs
-       local localdefs = { } -- already dumped local definitions as source code lines
-
-       -- mutually recursive functions:
-       local dump_val, dump_or_ref_val
-
-       --------------------------------------------------------------------
-       -- if x occurs multiple times, dump the local var rather than the
-       -- value. If it's the first time it's dumped, also dump the content
-       -- in localdefs.
-       --------------------------------------------------------------------
-       function dump_or_ref_val (x)
-               if nested[x] then return 'false' end -- placeholder for recursive reference
-               if not multiple[x] then return dump_val (x) end
-               local var = dumped [x]
-               if var then return "_[" .. var .. "]" end -- already referenced
-               local val = dump_val(x) -- first occurence, create and register reference
-               var = gensym()
-               table.insert(localdefs, "_["..var.."]="..val)
-               dumped [x] = var
-               return "_[" .. var .. "]"
-       end
-
-       -----------------------------------------------------------------------------
-       -- Second pass, dump the object; subparts occuring multiple times are dumped
-       -- in local variables which can be referenced multiple times;
-       -- care is taken to dump locla vars in asensible order.
-       -----------------------------------------------------------------------------
-       function dump_val(x)
-               local  t = type(x)
-               if     x==nil        then return 'nil'
-               elseif t=="number"   then return tostring(x)
-               elseif t=="string"   then return string.format("%q", x)
-               elseif t=="boolean"  then return x and "true" or "false"
-               elseif t=="function" then
-                       return "loadstring("..string.format("%q", string.dump(x))..")"
-               elseif t=="table" then
-                       local acc        = { }
-                       local idx_dumped = { }
-                       local np         = nest_points [x]
-                       for i, v in ipairs(x) do
-                               if np and np[v] then
-                                       table.insert (acc, 'false') -- placeholder
-                               else
-                                       table.insert (acc, dump_or_ref_val(v))
-                               end
-                               idx_dumped[i] = true
-                       end
-                       for k, v in pairs(x) do
-                               if np and (np[k] or np[v]) then
-                                       --check_multiple(k); check_multiple(v) -- force dumps in localdefs
-                               elseif not idx_dumped[k] then
-                                       table.insert (acc, "[" .. dump_or_ref_val(k) .. "] = " .. dump_or_ref_val(v))
-                               end
-                       end
-                       return "{ "..table.concat(acc,", ").." }"
-               else
-                       error ("Can't serialize data of type "..t)
-               end
-       end
-       
-       local function dump_nest_patches()
-               for _, entry in ipairs(nest_patches) do
-                       local p, k, v = unpack (entry)
-                       assert (multiple[p])
-                       local set = dump_or_ref_val (p) .. "[" .. dump_or_ref_val (k) .. "] = " .. 
-                               dump_or_ref_val (v) .. " -- rec "
-                       table.insert (localdefs, set)
-               end
-       end
-
-       mark_multiple_occurences (x)
-       local toplevel = dump_or_ref_val (x)
-       dump_nest_patches()
-
-       if next (localdefs) then
-               return "local _={ }\n" ..
-                       table.concat (localdefs, "\n") .. 
-                       "\nreturn " .. toplevel
-       else
-               return "return " .. toplevel
-       end
-end
-
--- Deserialization.
--- http://stackoverflow.com/questions/5958818/loading-serialized-data-into-a-table
---
-
-local env = {
-       loadstring = loadstring,
-}
-
-local function noop() end
-
-local safe_env = {
-       loadstring = noop,
-}
-
-local function stringtotable(sdata, safe)
-       if sdata:byte(1) == 27 then return nil, "binary bytecode prohibited" end
-       local f, message = assert(loadstring(sdata))
-       if not f then return nil, message end
-       if safe then
-               setfenv(f, safe_env)
-       else
-               setfenv(f, env)
-       end
-       return f()
-end
-
-function minetest.deserialize(sdata, safe)
-       local table = {}
-       local okay, results = pcall(stringtotable, sdata, safe)
-       if okay then
-               return results
-       end
-       minetest.log('error', 'minetest.deserialize(): '.. results)
-       return nil
-end
-
--- Run some unit tests
-local function unit_test()
-       function unitTest(name, success)
-               if not success then
-                       error(name .. ': failed')
-               end
-       end
-
-       unittest_input = {cat={sound="nyan", speed=400}, dog={sound="woof"}}
-       unittest_output = minetest.deserialize(minetest.serialize(unittest_input))
-
-       unitTest("test 1a", unittest_input.cat.sound == unittest_output.cat.sound)
-       unitTest("test 1b", unittest_input.cat.speed == unittest_output.cat.speed)
-       unitTest("test 1c", unittest_input.dog.sound == unittest_output.dog.sound)
-
-       unittest_input = {escapechars="\n\r\t\v\\\"\'", noneuropean="θשׁ٩∂"}
-       unittest_output = minetest.deserialize(minetest.serialize(unittest_input))
-       unitTest("test 3a", unittest_input.escapechars == unittest_output.escapechars)
-       unitTest("test 3b", unittest_input.noneuropean == unittest_output.noneuropean)
-end
-unit_test() -- Run it
-unit_test = nil -- Hide it
-
diff --git a/builtin/statbars.lua b/builtin/statbars.lua
deleted file mode 100644 (file)
index ca656a9..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-
-local health_bar_definition =
-{
-       hud_elem_type = "statbar",
-       position = { x=0.5, y=1 },
-       text = "heart.png",
-       number = 20,
-       direction = 0,
-       size = { x=24, y=24 },
-       offset = { x=(-10*24)-25, y=-(48+24+10)},
-}
-
-local breath_bar_definition =
-{
-       hud_elem_type = "statbar",
-       position = { x=0.5, y=1 },
-       text = "bubble.png",
-       number = 20,
-       direction = 0,
-       size = { x=24, y=24 },
-       offset = {x=25,y=-(48+24+10)},
-}
-
-local hud_ids = {}
-
-local function initialize_builtin_statbars(player)
-
-       if not player:is_player() then
-               return
-       end
-
-       local name = player:get_player_name()
-
-       if name == "" then
-               return
-       end
-
-       if (hud_ids[name] == nil) then
-               hud_ids[name] = {}
-       end
-
-       if player:hud_get_flags().healthbar then
-               if hud_ids[name].id_healthbar == nil then
-                       health_bar_definition.number = player:get_hp()
-                       hud_ids[name].id_healthbar  = player:hud_add(health_bar_definition)
-               end
-       else
-               if hud_ids[name].id_healthbar ~= nil then
-                       player:hud_remove(hud_ids[name].id_healthbar)
-                       hud_ids[name].id_healthbar = nil
-               end
-       end
-
-       if (player:get_breath() < 11) then
-               if player:hud_get_flags().breathbar then
-                       if hud_ids[name].id_breathbar == nil then
-                               hud_ids[name].id_breathbar = player:hud_add(breath_bar_definition)
-                       end
-               else
-                       if hud_ids[name].id_breathbar ~= nil then
-                               player:hud_remove(hud_ids[name].id_breathbar)
-                               hud_ids[name].id_breathbar = nil
-                       end
-               end
-       elseif hud_ids[name].id_breathbar ~= nil then
-               player:hud_remove(hud_ids[name].id_breathbar)
-               hud_ids[name].id_breathbar = nil
-       end
-end
-
-local function cleanup_builtin_statbars(player)
-
-       if not player:is_player() then
-               return
-       end
-
-       local name = player:get_player_name()
-
-       if name == "" then
-               return
-       end
-
-       hud_ids[name] = nil
-end
-
-local function player_event_handler(player,eventname)
-       assert(player:is_player())
-
-       local name = player:get_player_name()
-
-       if name == "" then
-               return
-       end
-
-       if eventname == "health_changed" then
-               initialize_builtin_statbars(player)
-
-               if hud_ids[name].id_healthbar ~= nil then
-                       player:hud_change(hud_ids[name].id_healthbar,"number",player:get_hp())
-                       return true
-               end
-       end
-
-       if eventname == "breath_changed" then
-               initialize_builtin_statbars(player)
-
-               if hud_ids[name].id_breathbar ~= nil then
-                       player:hud_change(hud_ids[name].id_breathbar,"number",player:get_breath()*2)
-                       return true
-               end
-       end
-
-       if eventname == "hud_changed" then
-               initialize_builtin_statbars(player)
-               return true
-       end
-
-       return false
-end
-
-function minetest.hud_replace_builtin(name, definition)
-
-       if definition == nil or
-               type(definition) ~= "table" or
-               definition.hud_elem_type ~= "statbar" then
-               return false
-       end
-
-       if name == "health" then
-               health_bar_definition = definition
-
-               for name,ids in pairs(hud_ids) do
-                       local player = minetest.get_player_by_name(name)
-                       if  player and hud_ids[name].id_healthbar then
-                               player:hud_remove(hud_ids[name].id_healthbar)
-                               initialize_builtin_statbars(player)
-                       end
-               end
-               return true
-       end
-
-       if name == "breath" then
-               breath_bar_definition = definition
-
-               for name,ids in pairs(hud_ids) do
-                       local player = minetest.get_player_by_name(name)
-                       if  player and hud_ids[name].id_breathbar then
-                               player:hud_remove(hud_ids[name].id_breathbar)
-                               initialize_builtin_statbars(player)
-                       end
-               end
-               return true
-       end
-
-       return false
-end
-
-minetest.register_on_joinplayer(initialize_builtin_statbars)
-minetest.register_on_leaveplayer(cleanup_builtin_statbars)
-minetest.register_playerevent(player_event_handler)
diff --git a/builtin/static_spawn.lua b/builtin/static_spawn.lua
deleted file mode 100644 (file)
index e8c107d..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
--- Minetest: builtin/static_spawn.lua
-
-local function warn_invalid_static_spawnpoint()
-       if minetest.setting_get("static_spawnpoint") and
-                       not minetest.setting_get_pos("static_spawnpoint") then
-               minetest.log('error', "The static_spawnpoint setting is invalid: \""..
-                               minetest.setting_get("static_spawnpoint").."\"")
-       end
-end
-
-warn_invalid_static_spawnpoint()
-
-local function put_player_in_spawn(obj)
-       warn_invalid_static_spawnpoint()
-       local static_spawnpoint = minetest.setting_get_pos("static_spawnpoint")
-       if not static_spawnpoint then
-               return false
-       end
-       minetest.log('action', "Moving "..obj:get_player_name()..
-                       " to static spawnpoint at "..
-                       minetest.pos_to_string(static_spawnpoint))
-       obj:setpos(static_spawnpoint)
-       return true
-end
-
-minetest.register_on_newplayer(function(obj)
-       put_player_in_spawn(obj)
-end)
-
-minetest.register_on_respawnplayer(function(obj)
-       return put_player_in_spawn(obj)
-end)
-
diff --git a/builtin/vector.lua b/builtin/vector.lua
deleted file mode 100644 (file)
index 77944b6..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-
-vector = {}
-
-local function assert_vector(v)
-       assert(type(v) == "table" and v.x and v.y and v.z, "Invalid vector")
-end
-
-function vector.new(a, b, c)
-       if type(a) == "table" then
-               assert(a.x and a.y and a.z, "Invalid vector passed to vector.new()")
-               return {x=a.x, y=a.y, z=a.z}
-       elseif a then
-               assert(b and c, "Invalid arguments for vector.new()")
-               return {x=a, y=b, z=c}
-       end
-       return {x=0, y=0, z=0}
-end
-
-function vector.equals(a, b)
-       assert_vector(a)
-       assert_vector(b)
-       return a.x == b.x and
-              a.y == b.y and
-              a.z == b.z
-end
-
-function vector.length(v)
-       assert_vector(v)
-       return math.hypot(v.x, math.hypot(v.y, v.z))
-end
-
-function vector.normalize(v)
-       assert_vector(v)
-       local len = vector.length(v)
-       if len == 0 then
-               return {x=0, y=0, z=0}
-       else
-               return vector.divide(v, len)
-       end
-end
-
-function vector.round(v)
-       assert_vector(v)
-       return {
-               x = math.floor(v.x + 0.5),
-               y = math.floor(v.y + 0.5),
-               z = math.floor(v.z + 0.5)
-       }
-end
-
-function vector.distance(a, b)
-       assert_vector(a)
-       assert_vector(b)
-       local x = a.x - b.x
-       local y = a.y - b.y
-       local z = a.z - b.z
-       return math.hypot(x, math.hypot(y, z))
-end
-
-function vector.direction(pos1, pos2)
-       assert_vector(pos1)
-       assert_vector(pos2)
-       local x_raw = pos2.x - pos1.x
-       local y_raw = pos2.y - pos1.y
-       local z_raw = pos2.z - pos1.z
-       local x_abs = math.abs(x_raw)
-       local y_abs = math.abs(y_raw)
-       local z_abs = math.abs(z_raw)
-       if x_abs >= y_abs and
-          x_abs >= z_abs then
-               y_raw = y_raw * (1 / x_abs)
-               z_raw = z_raw * (1 / x_abs)
-               x_raw = x_raw / x_abs
-       end
-       if y_abs >= x_abs and
-          y_abs >= z_abs then
-               x_raw = x_raw * (1 / y_abs)
-               z_raw = z_raw * (1 / y_abs)
-               y_raw = y_raw / y_abs
-       end
-       if z_abs >= y_abs and
-          z_abs >= x_abs then
-               x_raw = x_raw * (1 / z_abs)
-               y_raw = y_raw * (1 / z_abs)
-               z_raw = z_raw / z_abs
-       end
-       return {x=x_raw, y=y_raw, z=z_raw}
-end
-
-
-function vector.add(a, b)
-       assert_vector(a)
-       if type(b) == "table" then
-           assert_vector(b)
-               return {x = a.x + b.x,
-                       y = a.y + b.y,
-                       z = a.z + b.z}
-       else
-               return {x = a.x + b,
-                       y = a.y + b,
-                       z = a.z + b}
-       end
-end
-
-function vector.subtract(a, b)
-       assert_vector(a)
-       if type(b) == "table" then
-           assert_vector(b)
-               return {x = a.x - b.x,
-                       y = a.y - b.y,
-                       z = a.z - b.z}
-       else
-               return {x = a.x - b,
-                       y = a.y - b,
-                       z = a.z - b}
-       end
-end
-
-function vector.multiply(a, b)
-       assert_vector(a)
-       if type(b) == "table" then
-           assert_vector(b)
-               return {x = a.x * b.x,
-                       y = a.y * b.y,
-                       z = a.z * b.z}
-       else
-               return {x = a.x * b,
-                       y = a.y * b,
-                       z = a.z * b}
-       end
-end
-
-function vector.divide(a, b)
-       assert_vector(a)
-       if type(b) == "table" then
-        assert_vector(b)
-               return {x = a.x / b.x,
-                       y = a.y / b.y,
-                       z = a.z / b.z}
-       else
-               return {x = a.x / b,
-                       y = a.y / b,
-                       z = a.z / b}
-       end
-end
-
diff --git a/builtin/voxelarea.lua b/builtin/voxelarea.lua
deleted file mode 100644 (file)
index 93bbf73..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-VoxelArea = {
-       MinEdge = {x=1, y=1, z=1},
-       MaxEdge = {x=0, y=0, z=0},
-       ystride = 0,
-       zstride = 0,
-}
-
-function VoxelArea:new(o)
-       o = o or {}
-       setmetatable(o, self)
-       self.__index = self
-
-       local e = o:getExtent()
-       o.ystride = e.x
-       o.zstride = e.x * e.y
-
-       return o
-end
-
-function VoxelArea:getExtent()
-       return {
-               x = self.MaxEdge.x - self.MinEdge.x + 1,
-               y = self.MaxEdge.y - self.MinEdge.y + 1,
-               z = self.MaxEdge.z - self.MinEdge.z + 1,
-       }
-end
-
-function VoxelArea:getVolume()
-       local e = self:getExtent()
-       return e.x * e.y * e.z
-end
-
-function VoxelArea:index(x, y, z)
-       local i = (z - self.MinEdge.z) * self.zstride +
-                         (y - self.MinEdge.y) * self.ystride +
-                         (x - self.MinEdge.x) + 1
-       return math.floor(i)
-end
-
-function VoxelArea:indexp(p)
-       local i = (p.z - self.MinEdge.z) * self.zstride +
-                         (p.y - self.MinEdge.y) * self.ystride +
-                         (p.x - self.MinEdge.x) + 1
-       return math.floor(i)
-end
-
-function VoxelArea:position(i)
-       local p = {}
-       i = i - 1
-
-       p.z = math.floor(i / self.zstride) + self.MinEdge.z
-       i = i % self.zstride
-
-       p.y = math.floor(i / self.ystride) + self.MinEdge.y
-       i = i % self.ystride
-
-       p.x = math.floor(i) + self.MinEdge.x
-
-       return p
-end
-
-function VoxelArea:contains(x, y, z)
-       return (x >= self.MinEdge.x) and (x <= self.MaxEdge.x) and
-                  (y >= self.MinEdge.y) and (y <= self.MaxEdge.y) and
-                  (z >= self.MinEdge.z) and (z <= self.MaxEdge.z)
-end
-
-function VoxelArea:containsp(p)
-       return (p.x >= self.MinEdge.x) and (p.x <= self.MaxEdge.x) and
-                  (p.y >= self.MinEdge.y) and (p.y <= self.MaxEdge.y) and
-                  (p.z >= self.MinEdge.z) and (p.z <= self.MaxEdge.z)
-end
-
-function VoxelArea:containsi(i)
-       return (i >= 1) and (i <= self:getVolume())
-end
-
-function VoxelArea:iter(minx, miny, minz, maxx, maxy, maxz)
-       local i = self:index(minx, miny, minz) - 1
-       local last = self:index(maxx, maxy, maxz)
-       local ystride = self.ystride
-       local zstride = self.zstride
-       local yoff = (last+1) % ystride
-       local zoff = (last+1) % zstride
-       local ystridediff = (i - last) % ystride
-       local zstridediff = (i - last) % zstride
-       return function()
-               i = i + 1
-               if i % zstride == zoff then
-                       i = i + zstridediff
-               elseif i % ystride == yoff then
-                       i = i + ystridediff
-               end
-               if i <= last then
-                       return i
-               end
-       end
-end
-
-function VoxelArea:iterp(minp, maxp)
-       return self:iter(minp.x, minp.y, minp.z, maxp.x, maxp.y, maxp.z)
-end
index 3c17e650e76e4eb2b2c9f631a1760070a814a6da..945804ba82ac4c5b5c09905299cb1ff9dcd36a5d 100644 (file)
@@ -269,9 +269,9 @@ void set_default_settings(Settings *settings)
        settings->setDefault("enable_ipv6", "true");
        settings->setDefault("ipv6_server", "false");
 
-       settings->setDefault("main_menu_script","");
-       settings->setDefault("main_menu_mod_mgr","1");
-       settings->setDefault("main_menu_game_mgr","0");
+       settings->setDefault("main_menu_path", "");
+       settings->setDefault("main_menu_mod_mgr", "1");
+       settings->setDefault("main_menu_game_mgr", "0");
        settings->setDefault("modstore_download_url", "https://forum.minetest.net/media/");
        settings->setDefault("modstore_listmods_url", "https://forum.minetest.net/mmdb/mods/");
        settings->setDefault("modstore_details_url", "https://forum.minetest.net/mmdb/mod/*/");
index e32d629d0da826d84b3e0d2d5964be23ea717240..ef018021e8fa150a2e5717f8c37df89fe1b00dfa 100644 (file)
@@ -169,17 +169,16 @@ GUIEngine::GUIEngine(     irr::IrrlichtDevice* dev,
        m_formspecgui = new FormspecFormSource("");
 
        /* Create menu */
-       m_menu =
-               new GUIFormSpecMenu(    m_device,
-                                                               m_parent,
-                                                               -1,
-                                                               m_menumanager,
-                                                               0 /* &client */,
-                                                               0 /* gamedef */,
-                                                               m_texture_source,
-                                                               m_formspecgui,
-                                                               m_buttonhandler,
-                                                               NULL);
+       m_menu = new GUIFormSpecMenu(m_device,
+                       m_parent,
+                       -1,
+                       m_menumanager,
+                       NULL /* &client */,
+                       NULL /* gamedef */,
+                       m_texture_source,
+                       m_formspecgui,
+                       m_buttonhandler,
+                       NULL);
 
        m_menu->allowClose(false);
        m_menu->lockSize(true,v2u32(800,600));
@@ -216,43 +215,21 @@ GUIEngine::GUIEngine(     irr::IrrlichtDevice* dev,
 /******************************************************************************/
 bool GUIEngine::loadMainMenuScript()
 {
-       // Try custom menu script (main_menu_script)
+       // Try custom menu script (main_menu_path)
 
-       std::string menuscript = g_settings->get("main_menu_script");
-       if(menuscript != "") {
-               m_scriptdir = fs::RemoveLastPathComponent(menuscript);
-
-               if(m_script->loadMod(menuscript, "__custommenu")) {
-                       // custom menu script loaded
-                       return true;
-               }
-               else {
-                       infostream
-                               << "GUIEngine: execution of custom menu: \""
-                               << menuscript << "\" failed!"
-                               << std::endl
-                               << "\tfalling back to builtin menu"
-                               << std::endl;
-               }
+       m_scriptdir = g_settings->get("main_menu_path");
+       if (m_scriptdir.empty()) {
+               m_scriptdir = porting::path_share + DIR_DELIM "builtin" + DIR_DELIM "mainmenu";
        }
 
-       // Try builtin menu script (main_menu_script)
-
-       std::string builtin_menuscript =
-                       porting::path_share + DIR_DELIM + "builtin"
-                               + DIR_DELIM + "mainmenu.lua";
-
-       m_scriptdir = fs::RemoveRelativePathComponents(
-                       fs::RemoveLastPathComponent(builtin_menuscript));
-
-       if(m_script->loadMod(builtin_menuscript, "__builtinmenu")) {
-               // builtin menu script loaded
+       std::string script = porting::path_share + DIR_DELIM "builtin" + DIR_DELIM "init.lua";
+       if (m_script->loadScript(script)) {
+               // Menu script loaded
                return true;
-       }
-       else {
-               errorstream
-                       << "GUIEngine: unable to load builtin menu"
-                       << std::endl;
+       } else {
+               infostream
+                       << "GUIEngine: execution of menu script in: \""
+                       << m_scriptdir << "\" failed!" << std::endl;
        }
 
        return false;
index 4feed3e5652a4595691dacb86fa96b81ab915f96..64260fb3abe6ac8526ffbd82cb80d1ba2f6ccba3 100644 (file)
@@ -26,6 +26,7 @@ extern "C" {
 #include "lualib.h"
 }
 
+#include "server.h"
 #include "s_async.h"
 #include "log.h"
 #include "filesys.h"
@@ -233,9 +234,9 @@ AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobDispatcher,
        lua_pushstring(L, DIR_DELIM);
        lua_setglobal(L, "DIR_DELIM");
 
-       lua_pushstring(L,
-                       (porting::path_share + DIR_DELIM + "builtin").c_str());
-       lua_setglobal(L, "SCRIPTDIR");
+       // Push builtin initialization type
+       lua_pushstring(L, "async");
+       lua_setglobal(L, "INIT");
 
        jobDispatcher->prepareEnvironment(L, top);
 }
@@ -258,17 +259,16 @@ void* AsyncWorkerThread::Thread()
 
        porting::setThreadName((std::string("AsyncWorkTh_") + number).c_str());
 
-       std::string asyncscript = porting::path_share + DIR_DELIM + "builtin"
-                       + DIR_DELIM + "async_env.lua";
+       lua_State *L = getStack();
 
-       if (!loadScript(asyncscript)) {
+       std::string script = getServer()->getBuiltinLuaPath() + DIR_DELIM + "init.lua";
+       if (!loadScript(script)) {
                errorstream
                        << "AsyncWorkderThread execution of async base environment failed!"
                        << std::endl;
                abort();
        }
 
-       lua_State *L = getStack();
        // Main loop
        while (!StopRequested()) {
                // Wait for job
index 774b3f51d19b07e0465488effd216338f0b54ae4..1a172fd31f5137082e8cf187ce648781d4bafc55 100644 (file)
@@ -88,9 +88,9 @@ ScriptApiBase::ScriptApiBase()
        lua_pop(m_luastack, 1);
 #endif
 
-       m_server = 0;
-       m_environment = 0;
-       m_guiengine = 0;
+       m_server = NULL;
+       m_environment = NULL;
+       m_guiengine = NULL;
 }
 
 ScriptApiBase::~ScriptApiBase()
@@ -103,24 +103,14 @@ bool ScriptApiBase::loadMod(const std::string &scriptpath,
 {
        ModNameStorer modnamestorer(getStack(), modname);
 
-       if(!string_allowed(modname, MODNAME_ALLOWED_CHARS)){
+       if (!string_allowed(modname, MODNAME_ALLOWED_CHARS)) {
                errorstream<<"Error loading mod \""<<modname
                                <<"\": modname does not follow naming conventions: "
                                <<"Only chararacters [a-z0-9_] are allowed."<<std::endl;
                return false;
        }
 
-       bool success = false;
-
-       try{
-               success = loadScript(scriptpath);
-       }
-       catch(LuaError &e){
-               errorstream<<"Error loading mod \""<<modname
-                               <<"\": "<<e.what()<<std::endl;
-       }
-
-       return success;
+       return loadScript(scriptpath);
 }
 
 bool ScriptApiBase::loadScript(const std::string &scriptpath)
index 8ee2069b13e2d642fca4f6be1c930f1715619106..226620fc6322bba431128ba6bfd7952e6eb10fd5 100644 (file)
@@ -885,7 +885,7 @@ int ModApiMainMenu::l_extract_zip(lua_State *L)
 }
 
 /******************************************************************************/
-int ModApiMainMenu::l_get_scriptdir(lua_State *L)
+int ModApiMainMenu::l_get_mainmenu_path(lua_State *L)
 {
        GUIEngine* engine = getGuiEngine(L);
        assert(engine != 0);
@@ -1077,7 +1077,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
        API_FCT(delete_dir);
        API_FCT(copy_dir);
        API_FCT(extract_zip);
-       API_FCT(get_scriptdir);
+       API_FCT(get_mainmenu_path);
        API_FCT(show_file_open_dialog);
        API_FCT(get_version);
        API_FCT(download_file);
index b711f2f8655db2b51aecf38043a875c7baabac4b..7a9cafd1f4792b1e2ed8239718ac3d111c305e5b 100644 (file)
@@ -107,7 +107,7 @@ private:
 
        //filesystem
 
-       static int l_get_scriptdir(lua_State *L);
+       static int l_get_mainmenu_path(lua_State *L);
 
        static int l_get_modpath(lua_State *L);
 
index 531d044ef6f7787ba374429696112f638567d0a7..7770a5ff482afd504f3791ee35e3033f47734bc1 100644 (file)
@@ -350,14 +350,8 @@ int ModApiServer::l_get_modpath(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
        std::string modname = luaL_checkstring(L, 1);
-       // Do it
-       if(modname == "__builtin"){
-               std::string path = getServer(L)->getBuiltinLuaPath();
-               lua_pushstring(L, path.c_str());
-               return 1;
-       }
        const ModSpec *mod = getServer(L)->getModSpec(modname);
-       if(!mod){
+       if (!mod) {
                lua_pushnil(L);
                return 1;
        }
index f26a88a937005c8d639b743b478a02d6be4243ee..b30bab292ae7382fd7e5212dd85cff7a29efc31e 100644 (file)
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "common/c_content.h"
 #include "cpp_api/s_async.h"
 #include "debug.h"
+#include "porting.h"
 #include "log.h"
 #include "tool.h"
 #include "settings.h"
@@ -274,6 +275,14 @@ int ModApiUtil::l_is_yes(lua_State *L)
        return 1;
 }
 
+int ModApiUtil::l_get_builtin_path(lua_State *L)
+{
+       std::string path = porting::path_share + DIR_DELIM + "builtin";
+       lua_pushstring(L, path.c_str());
+       return 1;
+}
+
+
 void ModApiUtil::Initialize(lua_State *L, int top)
 {
        API_FCT(debug);
@@ -294,6 +303,8 @@ void ModApiUtil::Initialize(lua_State *L, int top)
        API_FCT(get_password_hash);
 
        API_FCT(is_yes);
+
+       API_FCT(get_builtin_path);
 }
 
 void ModApiUtil::InitializeAsync(AsyncEngine& engine)
@@ -311,4 +322,7 @@ void ModApiUtil::InitializeAsync(AsyncEngine& engine)
        ASYNC_API_FCT(write_json);
 
        ASYNC_API_FCT(is_yes);
+
+       ASYNC_API_FCT(get_builtin_path);
 }
+
index 13357587a73c97189c14afddd010f8f50a66911f..d72978dc6110475cd04b0c318798cba093584a3f 100644 (file)
@@ -79,6 +79,9 @@ private:
        // is_yes(arg)
        static int l_is_yes(lua_State *L);
 
+       // get_scriptdir()
+       static int l_get_builtin_path(lua_State *L);
+
 public:
        static void Initialize(lua_State *L, int top);
 
index b2c2150c66eebb062d0018fa75795ee9e90fbe61..fccd10722abf1a6cff293ec45ea31f3ce234eda4 100644 (file)
@@ -18,6 +18,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "scripting_game.h"
+#include "server.h"
+#include "filesys.h"
 #include "log.h"
 #include "cpp_api/s_internal.h"
 #include "lua_api/l_base.h"
@@ -54,6 +56,9 @@ GameScripting::GameScripting(Server* server)
 
        SCRIPTAPI_PRECHECKHEADER
 
+       lua_pushstring(L, DIR_DELIM);
+       lua_setglobal(L, "DIR_DELIM");
+
        // Create the main minetest table
        lua_newtable(L);
        lua_setglobal(L, "minetest");
@@ -70,6 +75,10 @@ GameScripting::GameScripting(Server* server)
        InitializeModApi(L, top);
        lua_pop(L, 1);
 
+       // Push builtin initialization type
+       lua_pushstring(L, "game");
+       lua_setglobal(L, "INIT");
+
        infostream << "SCRIPTAPI: Initialized game modules" << std::endl;
 }
 
index dbf1fc45e68443cd0dc747b0b5caf06cbd9bda88..9afddd1564f6725a96a1b0112ae06229f1f26113 100644 (file)
@@ -18,6 +18,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "scripting_mainmenu.h"
+#include "mods.h"
+#include "porting.h"
 #include "log.h"
 #include "filesys.h"
 #include "cpp_api/s_internal.h"
@@ -58,6 +60,10 @@ MainMenuScripting::MainMenuScripting(GUIEngine* guiengine)
        initializeModApi(L, top);
        lua_pop(L, 1);
 
+       // Push builtin initialization type
+       lua_pushstring(L, "mainmenu");
+       lua_setglobal(L, "INIT");
+
        infostream << "SCRIPTAPI: Initialized main menu modules" << std::endl;
 }
 
index ed99a72143487b4889dc9a6518c94182908c9da9..288f254ed5b815b27bb357253f5d41f90f76c085 100644 (file)
@@ -294,9 +294,6 @@ Server::Server(
                errorstream << std::endl;
        }
 
-       // Path to builtin.lua
-       std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
-
        // Lock environment
        JMutexAutoLock envlock(m_env_mutex);
 
@@ -305,16 +302,13 @@ Server::Server(
 
        m_script = new GameScripting(this);
 
+       std::string scriptpath = getBuiltinLuaPath() + DIR_DELIM "init.lua";
 
-       // Load and run builtin.lua
-       infostream<<"Server: Loading builtin.lua [\""
-                       <<builtinpath<<"\"]"<<std::endl;
-       bool success = m_script->loadMod(builtinpath, "__builtin");
-       if(!success){
-               errorstream<<"Server: Failed to load and run "
-                               <<builtinpath<<std::endl;
-               throw ModError("Failed to load and run "+builtinpath);
+       if (!m_script->loadScript(scriptpath)) {
+               throw ModError("Failed to load and run " + scriptpath);
        }
+
+
        // Print 'em
        infostream<<"Server: Loading mods: ";
        for(std::vector<ModSpec>::iterator i = m_mods.begin();