Builtin: Backport MT5 Game

This commit is contained in:
MoNTE48 2020-02-04 17:43:19 +01:00
parent b3a34dd850
commit fe5941bdf7
10 changed files with 497 additions and 203 deletions

View File

@ -200,9 +200,6 @@ function table.indexof(list, val)
return -1 return -1
end end
assert(table.indexof({"foo", "bar"}, "foo") == 1)
assert(table.indexof({"foo", "bar"}, "baz") == -1)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
if INIT ~= "client" then if INIT ~= "client" then
function file_exists(filename) function file_exists(filename)
@ -220,8 +217,6 @@ function string:trim()
return (self:gsub("^%s*(.-)%s*$", "%1")) return (self:gsub("^%s*(.-)%s*$", "%1"))
end end
assert(string.trim("\n \t\tfoo bar\t ") == "foo bar")
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function math.hypot(x, y) function math.hypot(x, y)
local t local t
@ -245,6 +240,20 @@ function math.sign(x, tolerance)
return 0 return 0
end end
--------------------------------------------------------------------------------
function math.factorial(x)
assert(x % 1 == 0 and x >= 0, "factorial expects a non-negative integer")
if x >= 171 then
-- 171! is greater than the biggest double, no need to calculate
return math.huge
end
local v = 1
for k = 2, x do
v = v * k
end
return v
end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function get_last_folder(text,count) function get_last_folder(text,count)
local parts = text:split(DIR_DELIM) local parts = text:split(DIR_DELIM)
@ -462,6 +471,12 @@ function core.explode_scrollbar_event(evt)
return retval return retval
end end
--------------------------------------------------------------------------------
function core.rgba(r, g, b, a)
return a and string.format("#%02X%02X%02X%02X", r, g, b, a) or
string.format("#%02X%02X%02X", r, g, b)
end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function core.pos_to_string(pos, decimal_places) function core.pos_to_string(pos, decimal_places)
local x = pos.x local x = pos.x
@ -500,10 +515,6 @@ function core.string_to_pos(value)
return nil return nil
end end
assert(core.string_to_pos("10.0, 5, -2").x == 10)
assert(core.string_to_pos("( 10.0, 5, -2)").z == -2)
assert(core.string_to_pos("asd, 5, -2)") == nil)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function core.string_to_area(value) function core.string_to_area(value)
local p1, p2 = unpack(value:split(") (")) local p1, p2 = unpack(value:split(") ("))
@ -545,6 +556,39 @@ function table.copy(t, seen)
end end
return n return n
end end
function table.insert_all(t, other)
for i=1, #other do
t[#t + 1] = other[i]
end
return t
end
function table.key_value_swap(t)
local ti = {}
for k,v in pairs(t) do
ti[v] = k
end
return ti
end
function table.shuffle(t, from, to, random)
from = from or 1
to = to or #t
random = random or math.random
local n = to - from + 1
while n > 1 do
local r = from + n-1
local l = from + random(0, n-1)
t[l], t[r] = t[r], t[l]
n = n-1
end
end
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- mainmenu only functions -- mainmenu only functions
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -629,6 +673,13 @@ end
-- Returns the exact coordinate of a pointed surface -- Returns the exact coordinate of a pointed surface
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
function core.pointed_thing_to_face_pos(placer, pointed_thing) function core.pointed_thing_to_face_pos(placer, pointed_thing)
-- Avoid crash in some situations when player is inside a node, causing
-- 'above' to equal 'under'.
if vector.equals(pointed_thing.above, pointed_thing.under) then
return pointed_thing.under
end
local eye_height = placer:get_properties().eye_height
local eye_offset_first = placer:get_eye_offset() local eye_offset_first = placer:get_eye_offset()
local node_pos = pointed_thing.under local node_pos = pointed_thing.under
local camera_pos = placer:get_pos() local camera_pos = placer:get_pos()
@ -648,7 +699,7 @@ function core.pointed_thing_to_face_pos(placer, pointed_thing)
end end
local fine_pos = {[nc] = node_pos[nc] + offset} local fine_pos = {[nc] = node_pos[nc] + offset}
camera_pos.y = camera_pos.y + 1.625 + eye_offset_first.y / 10 camera_pos.y = camera_pos.y + eye_height + eye_offset_first.y / 10
local f = (node_pos[nc] + offset - camera_pos[nc]) / look_dir[nc] local f = (node_pos[nc] + offset - camera_pos[nc]) / look_dir[nc]
for i = 1, #oc do for i = 1, #oc do
@ -656,3 +707,25 @@ function core.pointed_thing_to_face_pos(placer, pointed_thing)
end end
return fine_pos return fine_pos
end end
function core.string_to_privs(str, delim)
assert(type(str) == "string")
delim = delim or ','
local privs = {}
for _, priv in pairs(string.split(str, delim)) do
privs[priv:trim()] = true
end
return privs
end
function core.privs_to_string(privs, delim)
assert(type(privs) == "table")
delim = delim or ','
local list = {}
for priv, bool in pairs(privs) do
if bool then
list[#list + 1] = priv
end
end
return table.concat(list, delim)
end

View File

@ -82,18 +82,15 @@ end
core.builtin_auth_handler = { core.builtin_auth_handler = {
get_auth = function(name) get_auth = function(name)
assert(type(name) == "string") assert(type(name) == "string")
-- Figure out what password to use for a new player (singleplayer local auth_entry = core.auth_table[name]
-- always has an empty password, otherwise use default, which is -- If no such auth found, return nil
-- usually empty too) if not auth_entry then
local new_password_hash = ""
-- If not in authentication table, return nil
if not core.auth_table[name] then
return nil return nil
end end
-- Figure out what privileges the player should have. -- Figure out what privileges the player should have.
-- Take a copy of the privilege table -- Take a copy of the privilege table
local privileges = {} local privileges = {}
for priv, _ in pairs(core.auth_table[name].privileges) do for priv, _ in pairs(auth_entry.privileges) do
privileges[priv] = true privileges[priv] = true
end end
-- If singleplayer, give all privileges except those marked as give_to_singleplayer = false -- If singleplayer, give all privileges except those marked as give_to_singleplayer = false
@ -106,15 +103,17 @@ core.builtin_auth_handler = {
-- For the admin, give everything -- For the admin, give everything
elseif name == core.settings:get("name") then elseif name == core.settings:get("name") then
for priv, def in pairs(core.registered_privileges) do for priv, def in pairs(core.registered_privileges) do
privileges[priv] = true if def.give_to_admin then
privileges[priv] = true
end
end end
end end
-- All done -- All done
return { return {
password = core.auth_table[name].password, password = auth_entry.password,
privileges = privileges, privileges = privileges,
-- Is set to nil if unknown -- Is set to nil if unknown
last_login = core.auth_table[name].last_login, last_login = auth_entry.last_login,
} }
end, end,
create_auth = function(name, password) create_auth = function(name, password)
@ -130,12 +129,13 @@ core.builtin_auth_handler = {
set_password = function(name, password) set_password = function(name, password)
assert(type(name) == "string") assert(type(name) == "string")
assert(type(password) == "string") assert(type(password) == "string")
if not core.auth_table[name] then local auth_entry = core.auth_table[name]
if not auth_entry then
core.log("action", "[AUTH] Setting password for new player " .. name) core.log("action", "[AUTH] Setting password for new player " .. name)
core.builtin_auth_handler.create_auth(name, password) core.builtin_auth_handler.create_auth(name, password)
else else
core.log("action", "[AUTH] Setting password for existing player " .. name) core.log("action", "[AUTH] Setting password for existing player " .. name)
core.auth_table[name].password = password auth_entry.password = password
end end
return true return true
end, end,
@ -143,12 +143,28 @@ core.builtin_auth_handler = {
core.log("action", "[AUTH] Setting privileges for player " .. name) core.log("action", "[AUTH] Setting privileges for player " .. name)
assert(type(name) == "string") assert(type(name) == "string")
assert(type(privileges) == "table") assert(type(privileges) == "table")
if not core.auth_table[name] then local auth_entry = core.auth_table[name]
core.builtin_auth_handler.create_auth(name, if not auth_entry then
auth_entry = core.builtin_auth_handler.create_auth(name,
core.get_password_hash(name, core.get_password_hash(name,
core.settings:get("default_password"))) core.settings:get("default_password")))
end end
core.auth_table[name].privileges = privileges
-- Run grant callbacks
for priv, _ in pairs(privileges) do
if not auth_entry.privileges[priv] then
core.run_priv_callbacks(name, priv, nil, "grant")
end
end
-- Run revoke callbacks
for priv, _ in pairs(auth_entry.privileges) do
if not privileges[priv] then
core.run_priv_callbacks(name, priv, nil, "revoke")
end
end
auth_entry.privileges = privileges
core.notify_authentication_modified(name) core.notify_authentication_modified(name)
end, end,
reload = function() reload = function()
@ -163,10 +179,36 @@ core.builtin_auth_handler = {
end, end,
record_login = function(name) record_login = function(name)
assert(type(name) == "string") assert(type(name) == "string")
assert(core.auth_table[name]).last_login = os.time() local auth_entry = core.auth_table[name]
assert(auth_entry)
auth_entry.last_login = os.time()
end, end,
} }
core.register_on_prejoinplayer(function(name, ip)
if core.registered_auth_handler ~= nil then
return -- Don't do anything if custom auth handler registered
end
local auth_entry = core.auth_table
if auth_entry[name] ~= nil then
return
end
local name_lower = name:lower()
for k in pairs(auth_entry) do
if k:lower() == name_lower then
return string.format("\nYou can not register as '%s'! "..
"Another player called '%s' is already registered. "..
"Please check the spelling if it's your account "..
"or use a different name.", name, k)
end
end
end)
--
-- Authentication API
--
function core.register_authentication_handler(handler) function core.register_authentication_handler(handler)
if core.registered_auth_handler then if core.registered_auth_handler then
error("Add-on authentication handler already registered by "..core.registered_auth_handler_modname) error("Add-on authentication handler already registered by "..core.registered_auth_handler_modname)
@ -198,7 +240,6 @@ core.auth_commit = auth_pass("commit")
core.auth_reload() core.auth_reload()
local record_login = auth_pass("record_login") local record_login = auth_pass("record_login")
core.register_on_joinplayer(function(player) core.register_on_joinplayer(function(player)
record_login(player:get_player_name()) record_login(player:get_player_name())
end) end)
@ -207,23 +248,6 @@ core.register_on_shutdown(function()
core.auth_commit() core.auth_commit()
end) end)
core.register_on_prejoinplayer(function(name, ip)
local auth = core.auth_table
if auth[name] ~= nil then
return
end
local name_lower = name:lower()
for k in pairs(auth) do
if k:lower() == name_lower then
return string.format("\nYou can not register as '%s'! "..
"Another player called '%s' is already registered. "..
"Please check the spelling if it's your account "..
"or use a different name.", name, k)
end
end
end)
-- Autosave -- Autosave
if not core.is_singleplayer() then if not core.is_singleplayer() then
local save_interval = 600 local save_interval = 600

View File

@ -1,4 +1,4 @@
-- Minetest: builtin/game/chatcommands.lua -- Minetest: builtin/game/chat.lua
-- --
-- Chat command handler -- Chat command handler
@ -27,9 +27,9 @@ core.register_on_chat_message(function(name, message)
local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs) local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs)
if has_privs then if has_privs then
core.set_last_run_mod(cmd_def.mod_origin) core.set_last_run_mod(cmd_def.mod_origin)
local success, message = cmd_def.func(name, param) local _, result = cmd_def.func(name, param)
if message then if result then
core.chat_send_player(name, message) core.chat_send_player(name, result)
end end
else else
core.chat_send_player(name, "You don't have permission" core.chat_send_player(name, "You don't have permission"
@ -41,7 +41,7 @@ end)
if core.settings:get_bool("profiler.load") then if core.settings:get_bool("profiler.load") then
-- Run after register_chatcommand and its register_on_chat_message -- Run after register_chatcommand and its register_on_chat_message
-- Before any chattcommands that should be profiled -- Before any chatcommands that should be profiled
profiler.init_chatcommand() profiler.init_chatcommand()
end end
@ -80,7 +80,7 @@ core.register_chatcommand_alias = register_chatcommand_alias
-- --
core.register_chatcommand("me", { core.register_chatcommand("me", {
params = "<action>", params = "<action>",
description = "Display chat action (e.g., '/me orders a pizza' displays" description = "Show chat action (e.g., '/me orders a pizza' displays"
.. " '<player name> orders a pizza')", .. " '<player name> orders a pizza')",
privs = {shout = true}, privs = {shout = true},
func = function(name, param) func = function(name, param)
@ -93,7 +93,7 @@ core.register_chatcommand("admin", {
func = function(name) func = function(name)
local admin = core.settings:get("name") local admin = core.settings:get("name")
if admin then if admin then
return true, "The administrator of this server is "..admin.."." return true, "The administrator of this server is " .. admin .. "."
else else
return false, "There's no administrator named in the config file." return false, "There's no administrator named in the config file."
end end
@ -102,16 +102,44 @@ core.register_chatcommand("admin", {
core.register_chatcommand("privs", { core.register_chatcommand("privs", {
params = "[<name>]", params = "[<name>]",
description = "Print privileges of player", description = "Show privileges of yourself or another player",
func = function(caller, param) func = function(caller, param)
param = param:trim() param = param:trim()
local name = (param ~= "" and param or caller) local name = (param ~= "" and param or caller)
if not core.player_exists(name) then
return false, "Player " .. name .. " does not exist."
end
return true, "Privileges of " .. name .. ": " return true, "Privileges of " .. name .. ": "
.. core.privs_to_string( .. core.privs_to_string(
core.get_player_privs(name), ", ") core.get_player_privs(name), ", ")
end end
}) })
core.register_chatcommand("haspriv", {
params = "<privilege>",
description = "Return list of all online players with privilege.",
privs = {basic_privs = true},
func = function(caller, param)
param = param:trim()
if param == "" then
return false, "Invalid parameters (see /help haspriv)"
end
if not core.registered_privileges[param] then
return false, "Unknown privilege!"
end
local privs = core.string_to_privs(param)
local players_with_priv = {}
for _, player in pairs(core.get_connected_players()) do
local player_name = player:get_player_name()
if core.check_player_privs(player_name, privs) then
table.insert(players_with_priv, player_name)
end
end
return true, "Players online with the \"" .. param .. "\" privilege: " ..
table.concat(players_with_priv, ", ")
end
})
local function handle_grant_command(caller, grantname, grantprivstr) local function handle_grant_command(caller, grantname, grantprivstr)
local caller_privs = core.get_player_privs(caller) local caller_privs = core.get_player_privs(caller)
if not (caller_privs.privs or caller_privs.basic_privs) then if not (caller_privs.privs or caller_privs.basic_privs) then
@ -141,6 +169,10 @@ local function handle_grant_command(caller, grantname, grantprivstr)
if privs_unknown ~= "" then if privs_unknown ~= "" then
return false, privs_unknown return false, privs_unknown
end end
for priv, _ in pairs(grantprivs) do
-- call the on_grant callbacks
core.run_priv_callbacks(grantname, priv, caller, "grant")
end
core.set_player_privs(grantname, privs) core.set_player_privs(grantname, privs)
core.log("action", caller..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname) core.log("action", caller..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname)
if grantname ~= caller then if grantname ~= caller then
@ -166,7 +198,7 @@ core.register_chatcommand("grant", {
}) })
core.register_chatcommand("grantme", { core.register_chatcommand("grantme", {
params = "<privilege>|all", params = "<privilege> | all",
description = "Grant privileges to yourself", description = "Grant privileges to yourself",
func = function(name, param) func = function(name, param)
if param == "" then if param == "" then
@ -178,7 +210,7 @@ core.register_chatcommand("grantme", {
core.register_chatcommand("revoke", { core.register_chatcommand("revoke", {
params = "<name> (<privilege> | all)", params = "<name> (<privilege> | all)",
description = "Remove privilege from player", description = "Remove privileges from player",
privs = {}, privs = {},
func = function(name, param) func = function(name, param)
if not core.check_player_privs(name, {privs = true}) and if not core.check_player_privs(name, {privs = true}) and
@ -202,12 +234,19 @@ core.register_chatcommand("revoke", {
end end
end end
if revoke_priv_str == "all" then if revoke_priv_str == "all" then
revoke_privs = privs
privs = {} privs = {}
else else
for priv, _ in pairs(revoke_privs) do for priv, _ in pairs(revoke_privs) do
privs[priv] = nil privs[priv] = nil
end end
end end
for priv, _ in pairs(revoke_privs) do
-- call the on_revoke callbacks
core.run_priv_callbacks(revoke_name, priv, name, "revoke")
end
core.set_player_privs(revoke_name, privs) core.set_player_privs(revoke_name, privs)
core.log("action", name..' revoked (' core.log("action", name..' revoked ('
..core.privs_to_string(revoke_privs, ', ') ..core.privs_to_string(revoke_privs, ', ')
@ -233,11 +272,12 @@ core.register_chatcommand("setpassword", {
toname = param:match("^([^ ]+) *$") toname = param:match("^([^ ]+) *$")
raw_password = nil raw_password = nil
end end
if not toname then if not toname then
return false, "Name field required" return false, "Name field required"
end end
local act_str_past = "?"
local act_str_pres = "?" local act_str_past, act_str_pres
if not raw_password then if not raw_password then
core.set_player_password(toname, "") core.set_player_password(toname, "")
act_str_past = "cleared" act_str_past = "cleared"
@ -249,13 +289,14 @@ core.register_chatcommand("setpassword", {
act_str_past = "set" act_str_past = "set"
act_str_pres = "sets" act_str_pres = "sets"
end end
if toname ~= name then if toname ~= name then
core.chat_send_player(toname, "Your password was " core.chat_send_player(toname, "Your password was "
.. act_str_past .. " by " .. name) .. act_str_past .. " by " .. name)
end end
core.log("action", name .. " " .. act_str_pres core.log("action", name .. " " .. act_str_pres ..
.. " password of " .. toname .. ".") " password of " .. toname .. ".")
return true, "Password of player \"" .. toname .. "\" " .. act_str_past return true, "Password of player \"" .. toname .. "\" " .. act_str_past
end end
@ -263,7 +304,7 @@ core.register_chatcommand("setpassword", {
core.register_chatcommand("clearpassword", { core.register_chatcommand("clearpassword", {
params = "<name>", params = "<name>",
description = "Set empty password", description = "Set empty password for a player",
privs = {password = true}, privs = {password = true},
func = function(name, param) func = function(name, param)
local toname = param local toname = param
@ -289,7 +330,7 @@ core.register_chatcommand("auth_reload", {
core.register_chatcommand("remove_player", { core.register_chatcommand("remove_player", {
params = "<name>", params = "<name>",
description = "Remove player data", description = "Remove a player's data",
privs = {server = true}, privs = {server = true},
func = function(name, param) func = function(name, param)
local toname = param local toname = param
@ -323,7 +364,7 @@ core.register_chatcommand("auth_save", {
core.register_chatcommand("teleport", { core.register_chatcommand("teleport", {
params = "<X>,<Y>,<Z> | <to_name> | (<name> <X>,<Y>,<Z>) | (<name> <to_name>)", params = "<X>,<Y>,<Z> | <to_name> | (<name> <X>,<Y>,<Z>) | (<name> <to_name>)",
description = "Teleport to player or position", description = "Teleport to position or player",
privs = {teleport = true}, privs = {teleport = true},
func = function(name, param) func = function(name, param)
-- Returns (pos, true) if found, otherwise (pos, false) -- Returns (pos, true) if found, otherwise (pos, false)
@ -347,7 +388,6 @@ core.register_chatcommand("teleport", {
return pos, false return pos, false
end end
local teleportee
local p = {} local p = {}
p.x, p.y, p.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") p.x, p.y, p.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
p.x = tonumber(p.x) p.x = tonumber(p.x)
@ -358,37 +398,37 @@ core.register_chatcommand("teleport", {
if p.x < -lm or p.x > lm or p.y < -lm or p.y > lm or p.z < -lm or p.z > lm then if p.x < -lm or p.x > lm or p.y < -lm or p.y > lm or p.z < -lm or p.z > lm then
return false, "Cannot teleport out of map bounds!" return false, "Cannot teleport out of map bounds!"
end end
teleportee = core.get_player_by_name(name) local teleportee = core.get_player_by_name(name)
if teleportee then if teleportee then
teleportee:set_pos(p) teleportee:set_pos(p)
return true, "Teleporting to " .. core.pos_to_string(vector.round(p)) return true, "Teleporting to " .. core.pos_to_string(p, 1)
end end
end end
local teleportee local target_name = param:match("^([^ ]+)$")
local p local teleportee = core.get_player_by_name(name)
local target_name
target_name = param:match("^([^ ]+)$") p = nil
teleportee = core.get_player_by_name(name)
if target_name then if target_name then
local target = core.get_player_by_name(target_name) local target = core.get_player_by_name(target_name)
if target then if target then
p = target:get_pos() p = target:get_pos()
end end
end end
if teleportee and p then if teleportee and p then
p = find_free_position_near(p) p = find_free_position_near(p)
teleportee:set_pos(p) teleportee:set_pos(p)
return true, "Teleporting to " .. target_name return true, "Teleporting to " .. target_name
.. " at " .. core.pos_to_string(vector.round(p)) .. " at " .. core.pos_to_string(p, 1)
end end
if not core.check_player_privs(name, {bring = true}) then if not core.check_player_privs(name, {bring = true}) then
return false, "You don't have permission to teleport other players (missing bring privilege)" return false, "You don't have permission to teleport other players (missing bring privilege)"
end end
local teleportee teleportee = nil
local p = {} p = {}
local teleportee_name local teleportee_name
teleportee_name, p.x, p.y, p.z = param:match( teleportee_name, p.x, p.y, p.z = param:match(
"^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$")
@ -399,13 +439,11 @@ core.register_chatcommand("teleport", {
if teleportee and p.x and p.y and p.z then if teleportee and p.x and p.y and p.z then
teleportee:set_pos(p) teleportee:set_pos(p)
return true, "Teleporting " .. teleportee_name return true, "Teleporting " .. teleportee_name
.. " to " .. core.pos_to_string(vector.round(p)) .. " to " .. core.pos_to_string(p, 1)
end end
local teleportee teleportee = nil
local p p = nil
local teleportee_name
local target_name
teleportee_name, target_name = string.match(param, "^([^ ]+) +([^ ]+)$") teleportee_name, target_name = string.match(param, "^([^ ]+) +([^ ]+)$")
if teleportee_name then if teleportee_name then
teleportee = core.get_player_by_name(teleportee_name) teleportee = core.get_player_by_name(teleportee_name)
@ -421,7 +459,7 @@ core.register_chatcommand("teleport", {
teleportee:set_pos(p) teleportee:set_pos(p)
return true, "Teleporting " .. teleportee_name return true, "Teleporting " .. teleportee_name
.. " to " .. target_name .. " to " .. target_name
.. " at " .. core.pos_to_string(vector.round(p)) .. " at " .. core.pos_to_string(p, 1)
end end
return false, 'Invalid parameters ("' .. param return false, 'Invalid parameters ("' .. param
@ -440,7 +478,8 @@ core.register_chatcommand("set", {
core.settings:set(setname, setvalue) core.settings:set(setname, setvalue)
return true, setname .. " = " .. setvalue return true, setname .. " = " .. setvalue
end end
local setname, setvalue = string.match(param, "([^ ]+) (.+)")
setname, setvalue = string.match(param, "([^ ]+) (.+)")
if setname and setvalue then if setname and setvalue then
if not core.settings:get(setname) then if not core.settings:get(setname) then
return false, "Failed. Use '/set -n <name> <value>' to create a new setting." return false, "Failed. Use '/set -n <name> <value>' to create a new setting."
@ -448,14 +487,16 @@ core.register_chatcommand("set", {
core.settings:set(setname, setvalue) core.settings:set(setname, setvalue)
return true, setname .. " = " .. setvalue return true, setname .. " = " .. setvalue
end end
local setname = string.match(param, "([^ ]+)")
setname = string.match(param, "([^ ]+)")
if setname then if setname then
local setvalue = core.settings:get(setname) setvalue = core.settings:get(setname)
if not setvalue then if not setvalue then
setvalue = "<not set>" setvalue = "<not set>"
end end
return true, setname .. " = " .. setvalue return true, setname .. " = " .. setvalue
end end
return false, "Invalid parameters (see /help set)." return false, "Invalid parameters (see /help set)."
end end
}) })
@ -563,15 +604,12 @@ local function handle_give_command(cmd, giver, receiver, stackstring)
core.log("action", giver .. " invoked " .. cmd core.log("action", giver .. " invoked " .. cmd
.. ', stackstring="' .. stackstring .. '"') .. ', stackstring="' .. stackstring .. '"')
local ritems = core.registered_items local ritems = core.registered_items
local rnodes = core.registered_nodes if not string.match(stackstring, ":") and not ritems[stackstring] then
if not string.match(stackstring, ":") and
not ritems[stackstring] and not rnodes[stackstring] then
local modslist = core.get_modnames() local modslist = core.get_modnames()
local namestring = stackstring:match("(%w+)")
table.insert(modslist, 1, "default") table.insert(modslist, 1, "default")
for _, modname in pairs(modslist) do for _, modname in pairs(modslist) do
local namecheck = modname .. ":" .. namestring local namecheck = modname .. ":" .. stackstring:match("(%w+)")
if ritems[namecheck] or rnodes[namecheck] then if ritems[namecheck] then
stackstring = modname .. ":" .. stackstring stackstring = modname .. ":" .. stackstring
break break
end end
@ -580,8 +618,11 @@ local function handle_give_command(cmd, giver, receiver, stackstring)
local itemstack = ItemStack(stackstring) local itemstack = ItemStack(stackstring)
if itemstack:is_empty() then if itemstack:is_empty() then
return false, "Cannot give an empty item" return false, "Cannot give an empty item"
elseif not itemstack:is_known() then elseif (not itemstack:is_known()) or (itemstack:get_name() == "unknown") then
return false, "Cannot give an unknown item" return false, "Cannot give an unknown item"
-- Forbid giving 'ignore' due to unwanted side effects
elseif itemstack:get_name() == "ignore" then
return false, "Giving 'ignore' is not allowed"
end end
local receiverref = core.get_player_by_name(receiver) local receiverref = core.get_player_by_name(receiver)
if receiverref == nil then if receiverref == nil then
@ -600,18 +641,18 @@ local function handle_give_command(cmd, giver, receiver, stackstring)
-- entered (e.g. big numbers are always interpreted as 2^16-1). -- entered (e.g. big numbers are always interpreted as 2^16-1).
stackstring = itemstack:to_string() stackstring = itemstack:to_string()
if giver == receiver then if giver == receiver then
return true, ("%q %sadded to inventory.") local msg = "%q %sadded to inventory."
:format(stackstring, partiality) return true, msg:format(stackstring, partiality)
else else
core.chat_send_player(receiver, ("%q %sadded to inventory.") core.chat_send_player(receiver, ("%q %sadded to inventory.")
:format(stackstring, partiality)) :format(stackstring, partiality))
return true, ("%q %sadded to %s's inventory.") local msg = "%q %sadded to %s's inventory."
:format(stackstring, partiality, receiver) return true, msg:format(stackstring, partiality, receiver)
end end
end end
core.register_chatcommand("give", { core.register_chatcommand("give", {
params = "<name> <ItemString>", params = "<name> <ItemString> [<count> [<wear>]]",
description = "Give item to player", description = "Give item to player",
privs = {give = true}, privs = {give = true},
func = function(name, param) func = function(name, param)
@ -624,7 +665,7 @@ core.register_chatcommand("give", {
}) })
core.register_chatcommand("giveme", { core.register_chatcommand("giveme", {
params = "<ItemString>", params = "<ItemString> [<count> [<wear>]]",
description = "Give item to yourself", description = "Give item to yourself",
privs = {give = true}, privs = {give = true},
func = function(name, param) func = function(name, param)
@ -652,6 +693,9 @@ core.register_chatcommand("spawnentity", {
core.log("error", "Unable to spawn entity, player is nil") core.log("error", "Unable to spawn entity, player is nil")
return false, "Unable to spawn entity, player is nil" return false, "Unable to spawn entity, player is nil"
end end
if not core.registered_entities[entityname] then
return false, "Cannot spawn an unknown entity"
end
if p == "" then if p == "" then
p = player:get_pos() p = player:get_pos()
else else
@ -686,8 +730,8 @@ core.register_chatcommand("pulverize", {
core.rollback_punch_callbacks = {} core.rollback_punch_callbacks = {}
core.register_on_punchnode(function(pos, node, puncher) core.register_on_punchnode(function(pos, node, puncher)
local name = puncher:get_player_name() local name = puncher and puncher:get_player_name()
if core.rollback_punch_callbacks[name] then if name and core.rollback_punch_callbacks[name] then
core.rollback_punch_callbacks[name](pos, node, puncher) core.rollback_punch_callbacks[name](pos, node, puncher)
core.rollback_punch_callbacks[name] = nil core.rollback_punch_callbacks[name] = nil
end end
@ -785,16 +829,20 @@ core.register_chatcommand("rollback", {
}) })
core.register_chatcommand("status", { core.register_chatcommand("status", {
description = "Print server status", description = "Show server status",
privs = {server = true}, privs = {server = true},
func = function(name, param) func = function(name, param)
return true, core.get_server_status() local status = core.get_server_status(name, false)
if status and status ~= "" then
return true, status
end
return false, "This command was disabled by a mod or game"
end end
}) })
core.register_chatcommand("settime", { core.register_chatcommand("time", {
params = "<0..23>:<0..59> | <0..24000>", params = "[<0..23>:<0..59> | <0..24000>]",
description = "Set time of day", description = "Show or set time of day",
privs = {}, privs = {},
func = function(name, param) func = function(name, param)
if param == "" then if param == "" then
@ -831,10 +879,10 @@ core.register_chatcommand("settime", {
return true, "Time of day changed." return true, "Time of day changed."
end end
}) })
register_chatcommand_alias("time", "settime") register_chatcommand_alias("settime", "time")
core.register_chatcommand("days", { core.register_chatcommand("days", {
description = "Display day count", description = "Show day count since world creation",
func = function(name, param) func = function(name, param)
return true, "Current day is " .. core.get_day_count() return true, "Current day is " .. core.get_day_count()
end end
@ -845,13 +893,15 @@ core.register_chatcommand("shutdown", {
description = "Shutdown server (-1 cancels a delayed shutdown)", description = "Shutdown server (-1 cancels a delayed shutdown)",
privs = {server = true}, privs = {server = true},
func = function(name, param) func = function(name, param)
local delay, reconnect, message = param:match("([^ ][-]?[0-9]+)([^ ]+)(.*)") local delay, reconnect, message
message = message or "" delay, param = param:match("^%s*(%S+)(.*)")
if param then
reconnect, param = param:match("^%s*(%S+)(.*)")
end
message = param and param:match("^%s*(.+)") or ""
delay = tonumber(delay) or 0
if delay ~= "" then if delay == 0 then
delay = tonumber(param) or 0
else
delay = 0
core.log("action", name .. " shuts down server") core.log("action", name .. " shuts down server")
core.chat_send_all("*** Server shutting down (operator request).") core.chat_send_all("*** Server shutting down (operator request).")
end end
@ -860,15 +910,20 @@ core.register_chatcommand("shutdown", {
}) })
core.register_chatcommand("ban", { core.register_chatcommand("ban", {
params = "<name>", params = "[<name>]",
description = "Ban IP of player", description = "Ban the IP of a player or show the ban list",
privs = {ban = true}, privs = {ban = true},
func = function(name, param) func = function(name, param)
if param == "" then if param == "" then
return true, "Ban list: " .. core.get_ban_list() local ban_list = core.get_ban_list()
if ban_list == "" then
return true, "The ban list is empty."
else
return true, "Ban list: " .. ban_list
end
end end
if not core.get_player_by_name(param) then if not core.get_player_by_name(param) then
return false, "No such player." return false, "Player is not online."
end end
if not core.ban_player(param) then if not core.ban_player(param) then
return false, "Failed to ban player." return false, "Failed to ban player."
@ -881,7 +936,7 @@ core.register_chatcommand("ban", {
core.register_chatcommand("unban", { core.register_chatcommand("unban", {
params = "<name> | <IP_address>", params = "<name> | <IP_address>",
description = "Remove IP ban", description = "Remove IP ban belonging to a player/IP",
privs = {ban = true}, privs = {ban = true},
func = function(name, param) func = function(name, param)
if not core.unban_player_or_ip(param) then if not core.unban_player_or_ip(param) then
@ -912,7 +967,7 @@ core.register_chatcommand("kick", {
}) })
core.register_chatcommand("clearobjects", { core.register_chatcommand("clearobjects", {
params = "[full|quick]", params = "[full | quick]",
description = "Clear all objects in world", description = "Clear all objects in world",
privs = {server = true}, privs = {server = true},
func = function(name, param) func = function(name, param)
@ -957,10 +1012,11 @@ core.register_chatcommand("msg", {
end end
}) })
register_chatcommand_alias("m", "msg") register_chatcommand_alias("m", "msg")
register_chatcommand_alias("pm", "msg")
core.register_chatcommand("last-login", { core.register_chatcommand("last-login", {
params = "[<name>]", params = "[<name>]",
description = "Get the last login time of a player", description = "Get the last login time of a player or yourself",
func = function(name, param) func = function(name, param)
if param == "" then if param == "" then
param = name param = name
@ -983,7 +1039,7 @@ core.register_chatcommand("clearinv", {
if param and param ~= "" and param ~= name then if param and param ~= "" and param ~= name then
if not core.check_player_privs(name, {server = true}) then if not core.check_player_privs(name, {server = true}) then
return false, "You don't have permission" return false, "You don't have permission"
.. " to run this command (missing privilege: server)" .. " to clear another player's inventory (missing privilege: server)"
end end
player = core.get_player_by_name(param) player = core.get_player_by_name(param)
core.chat_send_player(param, name.." cleared your inventory.") core.chat_send_player(param, name.." cleared your inventory.")
@ -1059,13 +1115,9 @@ core.register_chatcommand("setspawn", {
if not player then if not player then
return false return false
end end
local pos = vector.round(player:get_pos()) local pos = minetest.pos_to_string(player:get_pos(), 1)
local x = pos.x core.settings:set("static_spawnpoint", pos)
local y = pos.y return true, "The spawn point are set to (" .. pos .. ")"
local z = pos.z
local pos_to_string = x .. "," .. y .. "," .. z
core.settings:set("static_spawnpoint", pos_to_string)
return true, "Setting spawn point to (" .. pos_to_string .. ")"
end end
}) })

View File

@ -21,6 +21,10 @@ core.EMERGE_GENERATED = 4
-- constants.h -- constants.h
-- Size of mapblocks in nodes -- Size of mapblocks in nodes
core.MAP_BLOCKSIZE = 16 core.MAP_BLOCKSIZE = 16
-- Default maximal HP of a player
core.PLAYER_MAX_HP_DEFAULT = 20
-- Default maximal breath of a player
core.PLAYER_MAX_BREATH_DEFAULT = 11
-- light.h -- light.h
-- Maximum value for node 'light_source' parameter -- Maximum value for node 'light_source' parameter

View File

@ -17,3 +17,8 @@ function core.create_detached_inventory(name, callbacks, player_name)
core.detached_inventories[name] = stuff core.detached_inventories[name] = stuff
return core.create_detached_inventory_raw(name, player_name) return core.create_detached_inventory_raw(name, player_name)
end end
function core.remove_detached_inventory(name)
core.detached_inventories[name] = nil
return core.remove_detached_inventory_raw(name)
end

View File

@ -8,6 +8,9 @@ local blocks_forceloaded
local blocks_temploaded = {} local blocks_temploaded = {}
local total_forceloaded = 0 local total_forceloaded = 0
-- true, if the forceloaded blocks got changed (flag for persistence on-disk)
local forceload_blocks_changed = false
local BLOCKSIZE = core.MAP_BLOCKSIZE local BLOCKSIZE = core.MAP_BLOCKSIZE
local function get_blockpos(pos) local function get_blockpos(pos)
return { return {
@ -31,6 +34,9 @@ local function get_relevant_tables(transient)
end end
function core.forceload_block(pos, transient) function core.forceload_block(pos, transient)
-- set changed flag
forceload_blocks_changed = true
local blockpos = get_blockpos(pos) local blockpos = get_blockpos(pos)
local hash = core.hash_node_position(blockpos) local hash = core.hash_node_position(blockpos)
local relevant_table, other_table = get_relevant_tables(transient) local relevant_table, other_table = get_relevant_tables(transient)
@ -51,6 +57,9 @@ function core.forceload_block(pos, transient)
end end
function core.forceload_free_block(pos, transient) function core.forceload_free_block(pos, transient)
-- set changed flag
forceload_blocks_changed = true
local blockpos = get_blockpos(pos) local blockpos = get_blockpos(pos)
local hash = core.hash_node_position(blockpos) local hash = core.hash_node_position(blockpos)
local relevant_table, other_table = get_relevant_tables(transient) local relevant_table, other_table = get_relevant_tables(transient)
@ -95,6 +104,28 @@ core.after(5, function()
end end
end) end)
core.register_on_shutdown(function() -- persists the currently forceloaded blocks to disk
local function persist_forceloaded_blocks()
write_file(wpath.."/force_loaded.txt", blocks_forceloaded) write_file(wpath.."/force_loaded.txt", blocks_forceloaded)
end) end
-- periodical forceload persistence
local function periodically_persist_forceloaded_blocks()
-- only persist if the blocks actually changed
if forceload_blocks_changed then
persist_forceloaded_blocks()
-- reset changed flag
forceload_blocks_changed = false
end
-- recheck after some time
core.after(10, periodically_persist_forceloaded_blocks)
end
-- persist periodically
core.after(5, periodically_persist_forceloaded_blocks)
-- persist on shutdown
core.register_on_shutdown(persist_forceloaded_blocks)

View File

@ -39,26 +39,41 @@ function core.check_player_privs(name, ...)
return true, "" return true, ""
end end
local player_list = {} local player_list = {}
core.register_on_joinplayer(function(player)
local player_name = player:get_player_name()
player_list[player_name] = player
if not core.is_singleplayer() then
core.chat_send_all("=> " .. player_name .. " has joined the server")
end
end)
core.register_on_leaveplayer(function(player, timed_out) function core.send_join_message(player_name)
local player_name = player:get_player_name() core.chat_send_all("=> " .. player_name .. " has joined the server")
player_list[player_name] = nil end
function core.send_leave_message(player_name, timed_out)
local announcement = "<= " .. player_name .. " left the server" local announcement = "<= " .. player_name .. " left the server"
if timed_out then if timed_out then
announcement = announcement .. " (timed out)" announcement = announcement .. " (timed out)"
end end
core.chat_send_all(announcement) core.chat_send_all(announcement)
end
core.register_on_joinplayer(function(player)
local player_name = player:get_player_name()
player_list[player_name] = player
if not core.is_singleplayer() then
core.send_join_message(player_name)
end
end) end)
core.register_on_leaveplayer(function(player, timed_out)
local player_name = player:get_player_name()
player_list[player_name] = nil
core.send_leave_message(player_name, timed_out)
end)
function core.get_connected_players() function core.get_connected_players()
local temp_table = {} local temp_table = {}
for _, value in pairs(player_list) do for _, value in pairs(player_list) do
@ -83,8 +98,10 @@ function core.player_exists(name)
return core.get_auth_handler().get_auth(name) ~= nil return core.get_auth_handler().get_auth(name) ~= nil
end end
-- Returns two position vectors representing a box of `radius` in each -- Returns two position vectors representing a box of `radius` in each
-- direction centered around the player corresponding to `player_name` -- direction centered around the player corresponding to `player_name`
function core.get_player_radius_area(player_name, radius) function core.get_player_radius_area(player_name, radius)
local player = core.get_player_by_name(player_name) local player = core.get_player_by_name(player_name)
if player == nil then if player == nil then
@ -115,19 +132,23 @@ function core.is_valid_pos(pos)
end end
function core.hash_node_position(pos) function core.hash_node_position(pos)
return (pos.z+32768)*65536*65536 + (pos.y+32768)*65536 + pos.x+32768 return (pos.z + 32768) * 65536 * 65536
+ (pos.y + 32768) * 65536
+ pos.x + 32768
end end
function core.get_position_from_hash(hash) function core.get_position_from_hash(hash)
local pos = {} local pos = {}
pos.x = (hash%65536) - 32768 pos.x = (hash % 65536) - 32768
hash = math.floor(hash/65536) hash = math.floor(hash / 65536)
pos.y = (hash%65536) - 32768 pos.y = (hash % 65536) - 32768
hash = math.floor(hash/65536) hash = math.floor(hash / 65536)
pos.z = (hash%65536) - 32768 pos.z = (hash % 65536) - 32768
return pos return pos
end end
function core.get_item_group(name, group) function core.get_item_group(name, group)
if not core.registered_items[name] or not if not core.registered_items[name] or not
core.registered_items[name].groups[group] then core.registered_items[name].groups[group] then
@ -136,11 +157,13 @@ function core.get_item_group(name, group)
return core.registered_items[name].groups[group] return core.registered_items[name].groups[group]
end end
function core.get_node_group(name, group) function core.get_node_group(name, group)
core.log("deprecated", "Deprecated usage of get_node_group, use get_item_group instead") core.log("deprecated", "Deprecated usage of get_node_group, use get_item_group instead")
return core.get_item_group(name, group) return core.get_item_group(name, group)
end end
function core.setting_get_pos(name) function core.setting_get_pos(name)
local value = core.settings:get(name) local value = core.settings:get(name)
if not value then if not value then
@ -150,11 +173,11 @@ function core.setting_get_pos(name)
end end
-- To be overriden by protection mods -- To be overriden by protection mods
function core.is_protected() function core.is_protected()
return false return false
end end
-- To be overriden by protection mods
function core.is_protected_action() function core.is_protected_action()
return false return false
end end
@ -165,8 +188,8 @@ function core.record_protection_violation(pos, name)
end end
end end
-- Checks if specified volume intersects a protected volume -- Checks if specified volume intersects a protected volume
-- Backport from Minetest 5.0
function core.is_area_protected(minp, maxp, player_name, interval) function core.is_area_protected(minp, maxp, player_name, interval)
-- 'interval' is the largest allowed interval for the 3D lattice of checks. -- 'interval' is the largest allowed interval for the 3D lattice of checks.

View File

@ -11,11 +11,14 @@ function core.register_privilege(name, param)
if def.give_to_singleplayer == nil then if def.give_to_singleplayer == nil then
def.give_to_singleplayer = true def.give_to_singleplayer = true
end end
if def.give_to_admin == nil then
def.give_to_admin = def.give_to_singleplayer
end
if def.description == nil then if def.description == nil then
def.description = "(no description)" def.description = "(no description)"
end end
end end
local def = {} local def
if type(param) == "table" then if type(param) == "table" then
def = param def = param
else else
@ -36,7 +39,7 @@ core.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privil
core.register_privilege("privs", "Can modify privileges") core.register_privilege("privs", "Can modify privileges")
core.register_privilege("teleport", { core.register_privilege("teleport", {
description = "Can use /teleport command", description = "Can teleport self",
give_to_singleplayer = creative give_to_singleplayer = creative
}) })
core.register_privilege("bring", { core.register_privilege("bring", {
@ -44,12 +47,13 @@ core.register_privilege("bring", {
give_to_singleplayer = false give_to_singleplayer = false
}) })
core.register_privilege("settime", { core.register_privilege("settime", {
description = "Can use /time", description = "Can set the time of day using /time",
give_to_singleplayer = creative give_to_singleplayer = creative
}) })
core.register_privilege("server", { core.register_privilege("server", {
description = "Can do server maintenance stuff", description = "Can do server maintenance stuff",
give_to_singleplayer = false give_to_singleplayer = false,
give_to_admin = true
}) })
core.register_privilege("protection_bypass", { core.register_privilege("protection_bypass", {
description = "Can bypass node protection in the world", description = "Can bypass node protection in the world",
@ -57,11 +61,13 @@ core.register_privilege("protection_bypass", {
}) })
core.register_privilege("ban", { core.register_privilege("ban", {
description = "Can ban and unban players", description = "Can ban and unban players",
give_to_singleplayer = false give_to_singleplayer = false,
give_to_admin = true
}) })
core.register_privilege("kick", { core.register_privilege("kick", {
description = "Can kick players", description = "Can kick players",
give_to_singleplayer = false give_to_singleplayer = false,
give_to_admin = true
}) })
core.register_privilege("give", { core.register_privilege("give", {
description = "Can use /give and /giveme", description = "Can use /give and /giveme",
@ -72,15 +78,15 @@ core.register_privilege("password", {
give_to_singleplayer = false give_to_singleplayer = false
}) })
core.register_privilege("fly", { core.register_privilege("fly", {
description = "Can fly using the free_move mode", description = "Can use fly mode",
give_to_singleplayer = creative give_to_singleplayer = creative
}) })
core.register_privilege("fast", { core.register_privilege("fast", {
description = "Can walk fast using the fast_move mode", description = "Can use fast mode",
give_to_singleplayer = creative give_to_singleplayer = creative
}) })
core.register_privilege("noclip", { core.register_privilege("noclip", {
description = "Can fly through walls", description = "Can fly through solid nodes using noclip mode",
give_to_singleplayer = false give_to_singleplayer = false
}) })
core.register_privilege("rollback", { core.register_privilege("rollback", {
@ -93,7 +99,8 @@ core.register_privilege("zoom", {
}) })
core.register_privilege("debug", { core.register_privilege("debug", {
description = "Allows enabling various debug options that may affect gameplay", description = "Allows enabling various debug options that may affect gameplay",
give_to_singleplayer = false give_to_singleplayer = false,
give_to_admin = true
}) })
core.register_privilege("weather", { core.register_privilege("weather", {
description = "Allows changing the weather", description = "Allows changing the weather",

View File

@ -79,6 +79,7 @@ end
function core.register_abm(spec) function core.register_abm(spec)
-- Add to core.registered_abms -- Add to core.registered_abms
assert(type(spec.action) == "function", "Required field 'action' of type function")
core.registered_abms[#core.registered_abms + 1] = spec core.registered_abms[#core.registered_abms + 1] = spec
spec.mod_origin = core.get_current_modname() or "??" spec.mod_origin = core.get_current_modname() or "??"
end end
@ -86,6 +87,7 @@ end
function core.register_lbm(spec) function core.register_lbm(spec)
-- Add to core.registered_lbms -- Add to core.registered_lbms
check_modname_prefix(spec.name) check_modname_prefix(spec.name)
assert(type(spec.action) == "function", "Required field 'action' of type function")
core.registered_lbms[#core.registered_lbms + 1] = spec core.registered_lbms[#core.registered_lbms + 1] = spec
spec.mod_origin = core.get_current_modname() or "??" spec.mod_origin = core.get_current_modname() or "??"
end end
@ -106,7 +108,7 @@ function core.register_entity(name, prototype)
end end
-- Intllib -- Intllib
Sl = intllib.make_gettext_pair("locales"); Sl = intllib.make_gettext_pair("locales")
function core.register_item(name, itemdef) function core.register_item(name, itemdef)
-- Check name -- Check name
@ -185,7 +187,7 @@ function core.register_item(name, itemdef)
--core.log("Registering item: " .. itemdef.name) --core.log("Registering item: " .. itemdef.name)
core.registered_items[itemdef.name] = itemdef core.registered_items[itemdef.name] = itemdef
core.registered_aliases[itemdef.name] = nil core.registered_aliases[itemdef.name] = nil
register_item_raw(itemdef) register_item_raw(itemdef)
end end
function core.unregister_item(name) function core.unregister_item(name)
@ -309,18 +311,17 @@ end
-- Alias the forbidden item names to "" so they can't be -- Alias the forbidden item names to "" so they can't be
-- created via itemstrings (e.g. /give) -- created via itemstrings (e.g. /give)
local name
for name in pairs(forbidden_item_names) do for name in pairs(forbidden_item_names) do
core.registered_aliases[name] = "" core.registered_aliases[name] = ""
register_alias_raw(name, "") register_alias_raw(name, "")
end end
-- Deprecated: -- Obsolete:
-- Aliases for core.register_alias (how ironic...) -- Aliases for core.register_alias (how ironic...)
--core.alias_node = core.register_alias -- core.alias_node = core.register_alias
--core.alias_tool = core.register_alias -- core.alias_tool = core.register_alias
--core.alias_craftitem = core.register_alias -- core.alias_craftitem = core.register_alias
-- --
-- Built-in node definitions. Also defined in C. -- Built-in node definitions. Also defined in C.
@ -429,10 +430,6 @@ function core.run_callbacks(callbacks, mode, ...)
local origin = core.callback_origins[callbacks[i]] local origin = core.callback_origins[callbacks[i]]
if origin then if origin then
core.set_last_run_mod(origin.mod) core.set_last_run_mod(origin.mod)
--print("Running " .. tostring(callbacks[i]) ..
-- " (a " .. origin.name .. " callback in " .. origin.mod .. ")")
else
--print("No data associated with callback")
end end
local cb_ret = callbacks[i](...) local cb_ret = callbacks[i](...)
@ -460,6 +457,18 @@ function core.run_callbacks(callbacks, mode, ...)
return ret return ret
end end
function core.run_priv_callbacks(name, priv, caller, method)
local def = core.registered_privileges[priv]
if not def or not def["on_" .. method] or
not def["on_" .. method](name, caller) then
for _, func in ipairs(core["registered_on_priv_" .. method]) do
if not func(name, caller, priv) then
break
end
end
end
end
-- --
-- Callback registration -- Callback registration
-- --
@ -521,11 +530,11 @@ end
core.registered_on_player_hpchanges = { modifiers = { }, loggers = { } } core.registered_on_player_hpchanges = { modifiers = { }, loggers = { } }
function core.registered_on_player_hpchange(player, hp_change) function core.registered_on_player_hpchange(player, hp_change, reason)
local last = false local last
for i = #core.registered_on_player_hpchanges.modifiers, 1, -1 do for i = #core.registered_on_player_hpchanges.modifiers, 1, -1 do
local func = core.registered_on_player_hpchanges.modifiers[i] local func = core.registered_on_player_hpchanges.modifiers[i]
hp_change, last = func(player, hp_change) hp_change, last = func(player, hp_change, reason)
if type(hp_change) ~= "number" then if type(hp_change) ~= "number" then
local debuginfo = debug.getinfo(func) local debuginfo = debug.getinfo(func)
error("The register_on_hp_changes function has to return a number at " .. error("The register_on_hp_changes function has to return a number at " ..
@ -536,7 +545,7 @@ function core.registered_on_player_hpchange(player, hp_change)
end end
end end
for i, func in ipairs(core.registered_on_player_hpchanges.loggers) do for i, func in ipairs(core.registered_on_player_hpchanges.loggers) do
func(player, hp_change) func(player, hp_change, reason)
end end
return hp_change return hp_change
end end
@ -578,11 +587,12 @@ core.registered_craft_predicts, core.register_craft_predict = make_registration(
core.registered_on_protection_violation, core.register_on_protection_violation = make_registration() core.registered_on_protection_violation, core.register_on_protection_violation = make_registration()
core.registered_on_item_eats, core.register_on_item_eat = make_registration() core.registered_on_item_eats, core.register_on_item_eat = make_registration()
core.registered_on_punchplayers, core.register_on_punchplayer = make_registration() core.registered_on_punchplayers, core.register_on_punchplayer = make_registration()
core.registered_on_priv_grant, core.register_on_priv_grant = make_registration()
core.registered_on_priv_revoke, core.register_on_priv_revoke = make_registration()
-- Player step iteration -- Player step iteration
players_per_step = core.settings:get("players_per_globalstep") players_per_step = tonumber(core.settings:get("players_per_globalstep")) or 20
players_per_step = players_per_step and tonumber(players_per_step) or 20
local player_iter local player_iter
local player_iter_forced local player_iter_forced

View File

@ -2234,45 +2234,101 @@ For the following functions `x` can be either a vector or a number:
* Returns a scaled vector or Schur quotient. * Returns a scaled vector or Schur quotient.
Helper functions Helper functions
---------------- -----------------
* `dump2(obj, name="_", dumped={})` * `dump2(obj, name, dumped)`: returns a string which makes `obj`
* Return object serialized as a string, handles reference loops human-readable, handles reference loops.
* `dump(obj, dumped={})` * `obj`: arbitrary variable
* Return object serialized as a string * `name`: string, default: `"_"`
* `dumped`: table, default: `{}`
* `dump(obj, dumped)`: returns a string which makes `obj` human-readable
* `obj`: arbitrary variable
* `dumped`: table, default: `{}`
* `math.hypot(x, y)` * `math.hypot(x, y)`
* Get the hypotenuse of a triangle with legs x and y. * Get the hypotenuse of a triangle with legs x and y.
Useful for distance calculation. Useful for distance calculation.
* `math.sign(x, tolerance)` * `math.sign(x, tolerance)`: returns `-1`, `0` or `1`
* Get the sign of a number. * Get the sign of a number.
Optional: Also returns `0` when the absolute value is within the tolerance (default: `0`) * tolerance: number, default: `0.0`
* `string.split(str, separator=",", include_empty=false, max_splits=-1, sep_is_pattern=false)` * If the absolute value of `x` is within the `tolerance` or `x` is NaN,
* If `max_splits` is negative, do not limit splits. `0` is returned.
* `sep_is_pattern` specifies if separator is a plain string or a pattern (regex). * `math.factorial(x)`: returns the factorial of `x`
* e.g. `string:split("a,b", ",") == {"a","b"}` * `string.split(str, separator, include_empty, max_splits, sep_is_pattern)`
* `string:trim()` * `separator`: string, default: `","`
* e.g. `string.trim("\n \t\tfoo bar\t ") == "foo bar"` * `include_empty`: boolean, default: `false`
* `minetest.wrap_text(str, limit, [as_table])`: returns a string or table * `max_splits`: number, if it's negative, splits aren't limited,
* Adds newlines to the string to keep it within the specified character limit default: `-1`
Note that returned lines may be longer than the limit since it only splits at word borders. * `sep_is_pattern`: boolean, it specifies whether separator is a plain
* limit: Maximal amount of characters in one line string or a pattern (regex), default: `false`
* as_table: optional, if true return table of lines instead of string * e.g. `"a,b":split","` returns `{"a","b"}`
* `minetest.pos_to_string({x=X,y=Y,z=Z}, decimal_places))`: returns string `"(X,Y,Z)"` * `string:trim()`: returns the string without whitespace pre- and suffixes
* Convert position to a printable string * e.g. `"\n \t\tfoo bar\t ":trim()` returns `"foo bar"`
Optional: 'decimal_places' will round the x, y and z of the pos to the given decimal place. * `minetest.wrap_text(str, limit, as_table)`: returns a string or table
* `minetest.string_to_pos(string)`: returns a position * Adds newlines to the string to keep it within the specified character
* Same but in reverse. Returns `nil` if the string can't be parsed to a position. limit
* Note that the returned lines may be longer than the limit since it only
splits at word borders.
* `limit`: number, maximal amount of characters in one line
* `as_table`: boolean, if set to true, a table of lines instead of a string
is returned, default: `false`
* `minetest.pos_to_string(pos, decimal_places)`: returns string `"(X,Y,Z)"`
* `pos`: table {x=X, y=Y, z=Z}
* Converts the position `pos` to a human-readable, printable string
* `decimal_places`: number, if specified, the x, y and z values of
the position are rounded to the given decimal place.
* `minetest.string_to_pos(string)`: returns a position or `nil`
* Same but in reverse.
* If the string can't be parsed to a position, nothing is returned.
* `minetest.string_to_area("(X1, Y1, Z1) (X2, Y2, Z2)")`: returns two positions * `minetest.string_to_area("(X1, Y1, Z1) (X2, Y2, Z2)")`: returns two positions
* Converts a string representing an area box into two positions * Converts a string representing an area box into two positions
* `minetest.formspec_escape(string)`: returns a string * `minetest.formspec_escape(string)`: returns a string
* escapes the characters "[", "]", "\", "," and ";", which can not be used in formspecs * escapes the characters "[", "]", "\", "," and ";", which can not be used
in formspecs.
* `minetest.is_yes(arg)` * `minetest.is_yes(arg)`
* returns true if passed 'y', 'yes', 'true' or a number that isn't zero. * returns true if passed 'y', 'yes', 'true' or a number that isn't zero.
* `minetest.is_nan(arg)`
* returns true when the passed number represents NaN.
* `minetest.get_us_time()` * `minetest.get_us_time()`
* returns time with microsecond precision. May not return wall time. * returns time with microsecond precision. May not return wall time.
* `table.copy(table)`: returns a table * `table.copy(table)`: returns a table
* returns a deep copy of `table` * returns a deep copy of `table`
* `minetest.pointed_thing_to_face_pos(placer, pointed_thing)`: returns a position * `table.indexof(list, val)`: returns the smallest numerical index containing
the value `val` in the table `list`. Non-numerical indices are ignored.
If `val` could not be found, `-1` is returned. `list` must not have
negative indices.
* `table.insert_all(table, other_table)`:
* Appends all values in `other_table` to `table` - uses `#table + 1` to
find new indices.
* `table.key_value_swap(t)`: returns a table with keys and values swapped
* If multiple keys in `t` map to the same value, the result is undefined.
* `table.shuffle(table, [from], [to], [random_func])`:
* Shuffles elements `from` to `to` in `table` in place
* `from` defaults to `1`
* `to` defaults to `#table`
* `random_func` defaults to `math.random`. This function receives two
integers as arguments and should return a random integer inclusively
between them.
* `minetest.pointed_thing_to_face_pos(placer, pointed_thing)`: returns a
position.
* returns the exact position on the surface of a pointed node * returns the exact position on the surface of a pointed node
* `minetest.get_dig_params(groups, tool_capabilities)`: Simulates a tool
that digs a node.
Returns a table with the following fields:
* `diggable`: `true` if node can be dug, `false` otherwise.
* `time`: Time it would take to dig the node.
* `wear`: How much wear would be added to the tool.
`time` and `wear` are meaningless if node's not diggable
Parameters:
* `groups`: Table of the node groups of the node that would be dug
* `tool_capabilities`: Tool capabilities table of the tool
* `minetest.get_hit_params(groups, tool_capabilities [, time_from_last_punch])`:
Simulates an item that punches an object.
Returns a table with the following fields:
* `hp`: How much damage the punch would cause.
* `wear`: How much wear would be added to the tool.
Parameters:
* `groups`: Damage groups of the object
* `tool_capabilities`: Tool capabilities table of the item
* `time_from_last_punch`: time in seconds since last punch action
`minetest` namespace reference `minetest` namespace reference
------------------------------ ------------------------------
@ -2800,12 +2856,17 @@ and `minetest.auth_reload` call the authetification handler.
* `{type="player", name="celeron55"}` * `{type="player", name="celeron55"}`
* `{type="node", pos={x=, y=, z=}}` * `{type="node", pos={x=, y=, z=}}`
* `{type="detached", name="creative"}` * `{type="detached", name="creative"}`
* `minetest.create_detached_inventory(name, callbacks, [player_name])`: returns an `InvRef` * `minetest.create_detached_inventory(name, callbacks, [player_name])`: returns
* callbacks: See "Detached inventory callbacks" an `InvRef`.
* `player_name`: Make detached inventory available to one player exclusively, * `callbacks`: See [Detached inventory callbacks]
by default they will be sent to every player (even if not used). * `player_name`: Make detached inventory available to one player
Note that this parameter is mostly just a workaround and will be removed in future releases. exclusively, by default they will be sent to every player (even if not
used).
Note that this parameter is mostly just a workaround and will be removed
in future releases.
* Creates a detached inventory. If it already exists, it is cleared. * Creates a detached inventory. If it already exists, it is cleared.
* `minetest.remove_detached_inventory(name)`
* Returns a `boolean` indicating whether the removal succeeded.
* `minetest.do_item_eat(hp_change, replace_with_item, poison, itemstack, user, pointed_thing)`: * `minetest.do_item_eat(hp_change, replace_with_item, poison, itemstack, user, pointed_thing)`:
returns left over ItemStack returns left over ItemStack
* See `minetest.item_eat` and `minetest.register_on_item_eat` * See `minetest.item_eat` and `minetest.register_on_item_eat`
@ -2912,8 +2973,8 @@ and `minetest.auth_reload` call the authetification handler.
} }
* `minetest.handle_node_drops(pos, drops, digger)` * `minetest.handle_node_drops(pos, drops, digger)`
* `drops`: list of itemstrings * `drops`: list of itemstrings
* Handles drops from nodes after digging: Default action is to put them into * Handles drops from nodes after digging: Default action is to put them
digger's inventory into digger's inventory.
* Can be overridden to get different functionality (e.g. dropping items on * Can be overridden to get different functionality (e.g. dropping items on
ground) ground)
* `minetest.itemstring_with_palette(item, palette_index)`: returns an item * `minetest.itemstring_with_palette(item, palette_index)`: returns an item
@ -3166,6 +3227,10 @@ These functions return the leftover itemstack.
* See documentation on `minetest.compress()` for supported compression methods. * See documentation on `minetest.compress()` for supported compression methods.
* currently supported. * currently supported.
* `...` indicates method-specific arguments. Currently, no methods use this. * `...` indicates method-specific arguments. Currently, no methods use this.
* `minetest.rgba(red, green, blue[, alpha])`: returns a string
* Each argument is a 8 Bit unsigned integer
* Returns the ColorString from rgb or rgba values
* Example: `minetest.rgba(10, 20, 30, 40)`, returns `"#0A141E28"`
* `minetest.encode_base64(string)`: returns string encoded in base64 * `minetest.encode_base64(string)`: returns string encoded in base64
* Encodes a string in base64. * Encodes a string in base64.
* `minetest.decode_base64(string)`: returns string * `minetest.decode_base64(string)`: returns string