From: ShadowNinja Date: Sun, 27 Apr 2014 21:55:49 +0000 (-0400) Subject: Organize builtin into subdirectories X-Git-Url: http://81.2.79.47:8989/gitweb/?a=commitdiff_plain;h=1cd512913e4d4ad1fb43d4b6e3d7971bb6c67528;p=zefram%2Fminetest%2Fminetest_engine.git Organize builtin into subdirectories --- diff --git a/builtin/async/init.lua b/builtin/async/init.lua new file mode 100644 index 00000000..79b1a0a6 --- /dev/null +++ b/builtin/async/init.lua @@ -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 index cdcb82ee..00000000 --- a/builtin/async_env.lua +++ /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 index 2c3fb8fa..00000000 --- a/builtin/async_event.lua +++ /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 index b6cca609..00000000 --- a/builtin/auth.lua +++ /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 index 1babe006..00000000 --- a/builtin/builtin.lua +++ /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 index f8df83d8..00000000 --- a/builtin/chatcommands.lua +++ /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 = "", - 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/", - 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 ' 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 = "", - 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 = " |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 = " |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 = " ", - 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 = "", - 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 = ",, | | ,, | ", - 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] | ", - 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 ' 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 = "" - 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 = " ", - 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 = "", - 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 = "", - 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 = "[] [] [limit]", - description = "check who has last touched a node or near it, ".. - "max. 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 = " [] | : []", - description = "revert actions of a player; default for 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 = "", - 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 = "", - 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 = " [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 = " ", - 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 index 00000000..ef4bf435 --- /dev/null +++ b/builtin/common/async_event.lua @@ -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 index 00000000..9c734972 --- /dev/null +++ b/builtin/common/misc_helpers.lua @@ -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 "" + elseif type(o) == "userdata" then + return "" + 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 "" + 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 "" + elseif type(o) == "userdata" then + return "" + 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 index 00000000..93fffe80 --- /dev/null +++ b/builtin/common/serialize.lua @@ -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 +-- 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 index 00000000..77944b61 --- /dev/null +++ b/builtin/common/vector.lua @@ -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 index d8b578d4..00000000 --- a/builtin/deprecated.lua +++ /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 index 3757f138..00000000 --- a/builtin/detached_inventory.lua +++ /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 index 7ac348bd..00000000 --- a/builtin/falling.lua +++ /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 index f3de3ba2..00000000 --- a/builtin/features.lua +++ /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 index 379a5cea..00000000 --- a/builtin/filterlist.lua +++ /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 index 84895792..00000000 --- a/builtin/forceloading.lua +++ /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 index 00000000..b6cca609 --- /dev/null +++ b/builtin/game/auth.lua @@ -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 index 00000000..f8df83d8 --- /dev/null +++ b/builtin/game/chatcommands.lua @@ -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 = "", + 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/", + 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 ' 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 = "", + 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 = " |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 = " |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 = " ", + 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 = "", + 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 = ",, | | ,, | ", + 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] | ", + 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 ' 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 = "" + 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 = " ", + 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 = "", + 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 = "", + 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 = "[] [] [limit]", + description = "check who has last touched a node or near it, ".. + "max. 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 = " [] | : []", + description = "revert actions of a player; default for 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 = "", + 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 = "", + 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 = " [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 = " ", + 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 index 00000000..d8b578d4 --- /dev/null +++ b/builtin/game/deprecated.lua @@ -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 index 00000000..3757f138 --- /dev/null +++ b/builtin/game/detached_inventory.lua @@ -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 index 00000000..93d17221 --- /dev/null +++ b/builtin/game/falling.lua @@ -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 index 00000000..f3de3ba2 --- /dev/null +++ b/builtin/game/features.lua @@ -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 index 00000000..84895792 --- /dev/null +++ b/builtin/game/forceloading.lua @@ -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 index 00000000..b878a266 --- /dev/null +++ b/builtin/game/init.lua @@ -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 index 00000000..002c14f5 --- /dev/null +++ b/builtin/game/item.lua @@ -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 index 00000000..8150e6da --- /dev/null +++ b/builtin/game/item_entity.lua @@ -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 index 00000000..82cc527c --- /dev/null +++ b/builtin/game/misc.lua @@ -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 index 00000000..244aa453 --- /dev/null +++ b/builtin/game/privileges.lua @@ -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 index 00000000..99c5115c --- /dev/null +++ b/builtin/game/register.lua @@ -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 index 00000000..ca656a97 --- /dev/null +++ b/builtin/game/statbars.lua @@ -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 index 00000000..e8c107d8 --- /dev/null +++ b/builtin/game/static_spawn.lua @@ -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 index 00000000..93bbf73a --- /dev/null +++ b/builtin/game/voxelarea.lua @@ -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 index c99c2de2..00000000 --- a/builtin/gamemgr.lua +++ /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 index 00000000..9969111b --- /dev/null +++ b/builtin/init.lua @@ -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 index 002c14f5..00000000 --- a/builtin/item.lua +++ /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 index 0dcc2dc2..00000000 --- a/builtin/item_entity.lua +++ /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 index 67e0113b..00000000 --- a/builtin/mainmenu.lua +++ /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) ,".. - "Ryan Kwolek (kwolekr) ,".. - "PilzAdam ," .. - "Ilya Zhuravlev (xyz) ,".. - "Lisa Milne (darkrose) ,".. - "Maciej Kasatkin (RealBadAngel) ,".. - "proller ,".. - "sfan5 ,".. - "kahrl ,".. - "sapier,".. - "ShadowNinja ,".. - "Nathanael Courant (Nore/Novatux) ,".. - "BlockMen,".. - ",".. - "#FFFF00" .. fgettext("Active Contributors") .. "," .. - "Vanessa Ezekowitz (VanessaE) ,".. - "Jurgen Doser (doserj) ,".. - "Jeija ,".. - "MirceaKitsune ,".. - "dannydark ,".. - "0gb.us <0gb.us@0gb.us>,".. - "," .. - "#FFFF00" .. fgettext("Previous Contributors") .. "," .. - "Guiseppe Bilotta (Oblomov) ,".. - "Jonathan Neuschafer ,".. - "Nils Dagsson Moskopp (erlehmann) ,".. - "Constantin Wenger (SpeedProg) ,".. - "matttpt ,".. - "JacobF ,".. - ";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 index 00000000..379a5cea --- /dev/null +++ b/builtin/mainmenu/filterlist.lua @@ -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 index 00000000..c99c2de2 --- /dev/null +++ b/builtin/mainmenu/gamemgr.lua @@ -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 index 00000000..d78a668c --- /dev/null +++ b/builtin/mainmenu/init.lua @@ -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) ,".. + "Ryan Kwolek (kwolekr) ,".. + "PilzAdam ," .. + "Ilya Zhuravlev (xyz) ,".. + "Lisa Milne (darkrose) ,".. + "Maciej Kasatkin (RealBadAngel) ,".. + "proller ,".. + "sfan5 ,".. + "kahrl ,".. + "sapier,".. + "ShadowNinja ,".. + "Nathanael Courant (Nore/Novatux) ,".. + "BlockMen,".. + ",".. + "#FFFF00" .. fgettext("Active Contributors") .. "," .. + "Vanessa Ezekowitz (VanessaE) ,".. + "Jurgen Doser (doserj) ,".. + "Jeija ,".. + "MirceaKitsune ,".. + "dannydark ,".. + "0gb.us <0gb.us@0gb.us>,".. + "," .. + "#FFFF00" .. fgettext("Previous Contributors") .. "," .. + "Guiseppe Bilotta (Oblomov) ,".. + "Jonathan Neuschafer ,".. + "Nils Dagsson Moskopp (erlehmann) ,".. + "Constantin Wenger (SpeedProg) ,".. + "matttpt ,".. + "JacobF ,".. + ";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 index 00000000..2e4d5f8b --- /dev/null +++ b/builtin/mainmenu/menubar.lua @@ -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 index 00000000..eeb65add --- /dev/null +++ b/builtin/mainmenu/modmgr.lua @@ -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 index 00000000..ef7fd016 --- /dev/null +++ b/builtin/mainmenu/modstore.lua @@ -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 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 index 00000000..998fc219 --- /dev/null +++ b/builtin/mainmenu/textures.lua @@ -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 index 82cc527c..00000000 --- a/builtin/misc.lua +++ /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 index 9c734972..00000000 --- a/builtin/misc_helpers.lua +++ /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 "" - elseif type(o) == "userdata" then - return "" - 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 "" - 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 "" - elseif type(o) == "userdata" then - return "" - 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 index 99c5115c..00000000 --- a/builtin/misc_register.lua +++ /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 index 2e4d5f8b..00000000 --- a/builtin/mm_menubar.lua +++ /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 index 998fc219..00000000 --- a/builtin/mm_textures.lua +++ /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 index eeb65add..00000000 --- a/builtin/modmgr.lua +++ /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 index ef7fd016..00000000 --- a/builtin/modstore.lua +++ /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 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 index 244aa453..00000000 --- a/builtin/privileges.lua +++ /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 index 93fffe80..00000000 --- a/builtin/serialize.lua +++ /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 --- 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 index ca656a97..00000000 --- a/builtin/statbars.lua +++ /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 index e8c107d8..00000000 --- a/builtin/static_spawn.lua +++ /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 index 77944b61..00000000 --- a/builtin/vector.lua +++ /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 index 93bbf73a..00000000 --- a/builtin/voxelarea.lua +++ /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 diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 3c17e650..945804ba 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -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/*/"); diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index e32d629d..ef018021 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -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; diff --git a/src/script/cpp_api/s_async.cpp b/src/script/cpp_api/s_async.cpp index 4feed3e5..64260fb3 100644 --- a/src/script/cpp_api/s_async.cpp +++ b/src/script/cpp_api/s_async.cpp @@ -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 diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 774b3f51..1a172fd3 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -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 \""<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; } diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index f26a88a9..b30bab29 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -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); } + diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index 13357587..d72978dc 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -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); diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp index b2c2150c..fccd1072 100644 --- a/src/script/scripting_game.cpp +++ b/src/script/scripting_game.cpp @@ -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; } diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp index dbf1fc45..9afddd15 100644 --- a/src/script/scripting_mainmenu.cpp +++ b/src/script/scripting_mainmenu.cpp @@ -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; } diff --git a/src/server.cpp b/src/server.cpp index ed99a721..288f254e 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -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 [\"" - <loadMod(builtinpath, "__builtin"); - if(!success){ - errorstream<<"Server: Failed to load and run " - <loadScript(scriptpath)) { + throw ModError("Failed to load and run " + scriptpath); } + + // Print 'em infostream<<"Server: Loading mods: "; for(std::vector::iterator i = m_mods.begin();