aboutsummaryrefslogtreecommitdiff
path: root/builtin/common
diff options
context:
space:
mode:
Diffstat (limited to 'builtin/common')
-rw-r--r--builtin/common/after.lua20
-rw-r--r--builtin/common/filterlist.lua1
-rw-r--r--builtin/common/information_formspecs.lua152
-rw-r--r--builtin/common/misc_helpers.lua31
-rw-r--r--builtin/common/serialize.lua1
-rw-r--r--builtin/common/vector.lua19
6 files changed, 208 insertions, 16 deletions
diff --git a/builtin/common/after.lua b/builtin/common/after.lua
index cdfaaab86..b314711c9 100644
--- a/builtin/common/after.lua
+++ b/builtin/common/after.lua
@@ -1,33 +1,41 @@
local jobs = {}
local time = 0.0
+local time_next = math.huge
core.register_globalstep(function(dtime)
time = time + dtime
- if #jobs < 1 then
+ if time < time_next then
return
end
+ time_next = math.huge
+
-- Iterate backwards so that we miss any new timers added by
- -- a timer callback, and so that we don't skip the next timer
- -- in the list if we remove one.
+ -- a timer callback.
for i = #jobs, 1, -1 do
local job = jobs[i]
if time >= job.expire then
core.set_last_run_mod(job.mod_origin)
job.func(unpack(job.arg))
- table.remove(jobs, i)
+ local jobs_l = #jobs
+ jobs[i] = jobs[jobs_l]
+ jobs[jobs_l] = nil
+ elseif job.expire < time_next then
+ time_next = job.expire
end
end
end)
function core.after(after, func, ...)
assert(tonumber(after) and type(func) == "function",
- "Invalid core.after invocation")
+ "Invalid minetest.after invocation")
+ local expire = time + after
jobs[#jobs + 1] = {
func = func,
- expire = time + after,
+ expire = expire,
arg = {...},
mod_origin = core.get_last_run_mod()
}
+ time_next = math.min(time_next, expire)
end
diff --git a/builtin/common/filterlist.lua b/builtin/common/filterlist.lua
index 1ba1d8741..e30379f2f 100644
--- a/builtin/common/filterlist.lua
+++ b/builtin/common/filterlist.lua
@@ -250,7 +250,6 @@ end
--------------------------------------------------------------------------------
function compare_worlds(world1,world2)
-
if world1.path ~= world2.path then
return false
end
diff --git a/builtin/common/information_formspecs.lua b/builtin/common/information_formspecs.lua
new file mode 100644
index 000000000..b977e2656
--- /dev/null
+++ b/builtin/common/information_formspecs.lua
@@ -0,0 +1,152 @@
+local COLOR_BLUE = "#7AF"
+local COLOR_GREEN = "#7F7"
+local COLOR_GRAY = "#BBB"
+
+local LIST_FORMSPEC = [[
+ size[13,6.5]
+ label[0,-0.1;%s]
+ tablecolumns[color;tree;text;text]
+ table[0,0.5;12.8,5.5;list;%s;0]
+ button_exit[5,6;3,1;quit;%s]
+ ]]
+
+local LIST_FORMSPEC_DESCRIPTION = [[
+ size[13,7.5]
+ label[0,-0.1;%s]
+ tablecolumns[color;tree;text;text]
+ table[0,0.5;12.8,4.8;list;%s;%i]
+ box[0,5.5;12.8,1.5;#000]
+ textarea[0.3,5.5;13.05,1.9;;;%s]
+ button_exit[5,7;3,1;quit;%s]
+ ]]
+
+local formspec_escape = core.formspec_escape
+local check_player_privs = core.check_player_privs
+
+
+-- CHAT COMMANDS FORMSPEC
+
+local mod_cmds = {}
+
+local function load_mod_command_tree()
+ mod_cmds = {}
+
+ for name, def in pairs(core.registered_chatcommands) do
+ mod_cmds[def.mod_origin] = mod_cmds[def.mod_origin] or {}
+ local cmds = mod_cmds[def.mod_origin]
+
+ -- Could be simplified, but avoid the priv checks whenever possible
+ cmds[#cmds + 1] = { name, def }
+ end
+ local sorted_mod_cmds = {}
+ for modname, cmds in pairs(mod_cmds) do
+ table.sort(cmds, function(a, b) return a[1] < b[1] end)
+ sorted_mod_cmds[#sorted_mod_cmds + 1] = { modname, cmds }
+ end
+ table.sort(sorted_mod_cmds, function(a, b) return a[1] < b[1] end)
+ mod_cmds = sorted_mod_cmds
+end
+
+core.after(0, load_mod_command_tree)
+
+local function build_chatcommands_formspec(name, sel, copy)
+ local rows = {}
+ rows[1] = "#FFF,0,Command,Parameters"
+
+ local description = "For more information, click on any entry in the list.\n" ..
+ "Double-click to copy the entry to the chat history."
+
+ for i, data in ipairs(mod_cmds) do
+ rows[#rows + 1] = COLOR_BLUE .. ",0," .. formspec_escape(data[1]) .. ","
+ for j, cmds in ipairs(data[2]) do
+ local has_priv = check_player_privs(name, cmds[2].privs)
+ rows[#rows + 1] = ("%s,1,%s,%s"):format(
+ has_priv and COLOR_GREEN or COLOR_GRAY,
+ cmds[1], formspec_escape(cmds[2].params))
+ if sel == #rows then
+ description = cmds[2].description
+ if copy then
+ core.chat_send_player(name, ("Command: %s %s"):format(
+ core.colorize("#0FF", "/" .. cmds[1]), cmds[2].params))
+ end
+ end
+ end
+ end
+
+ return LIST_FORMSPEC_DESCRIPTION:format(
+ "Available commands: (see also: /help <cmd>)",
+ table.concat(rows, ","), sel or 0,
+ description, "Close"
+ )
+end
+
+
+-- PRIVILEGES FORMSPEC
+
+local function build_privs_formspec(name)
+ local privs = {}
+ for priv_name, def in pairs(core.registered_privileges) do
+ privs[#privs + 1] = { priv_name, def }
+ end
+ table.sort(privs, function(a, b) return a[1] < b[1] end)
+
+ local rows = {}
+ rows[1] = "#FFF,0,Privilege,Description"
+
+ local player_privs = core.get_player_privs(name)
+ for i, data in ipairs(privs) do
+ rows[#rows + 1] = ("%s,0,%s,%s"):format(
+ player_privs[data[1]] and COLOR_GREEN or COLOR_GRAY,
+ data[1], formspec_escape(data[2].description))
+ end
+
+ return LIST_FORMSPEC:format(
+ "Available privileges:",
+ table.concat(rows, ","),
+ "Close"
+ )
+end
+
+
+-- DETAILED CHAT COMMAND INFORMATION
+
+core.register_on_player_receive_fields(function(player, formname, fields)
+ if formname ~= "__builtin:help_cmds" or fields.quit then
+ return
+ end
+
+ local event = minetest.explode_table_event(fields.list)
+ if event.type ~= "INV" then
+ local name = player:get_player_name()
+ core.show_formspec(name, "__builtin:help_cmds",
+ build_chatcommands_formspec(name, event.row, event.type == "DCL"))
+ end
+end)
+
+
+local help_command = core.registered_chatcommands["help"]
+local old_help_func = help_command.func
+
+help_command.func = function(name, param)
+ local admin = core.settings:get("name")
+
+ -- If the admin ran help, put the output in the chat buffer as well to
+ -- work with the server terminal
+ if param == "privs" then
+ core.show_formspec(name, "__builtin:help_privs",
+ build_privs_formspec(name))
+ if name ~= admin then
+ return
+ end
+ end
+ if param == "" or param == "all" then
+ core.show_formspec(name, "__builtin:help_cmds",
+ build_chatcommands_formspec(name))
+ if name ~= admin then
+ return
+ end
+ end
+
+ return old_help_func(name, param)
+end
+
diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua
index e250b0ed1..d6673a691 100644
--- a/builtin/common/misc_helpers.lua
+++ b/builtin/common/misc_helpers.lua
@@ -128,6 +128,7 @@ function dump(o, indent, nested, level)
if t ~= "table" then
return basic_dump(o)
end
+
-- Contains table -> true/nil of currently nested tables
nested = nested or {}
if nested[o] then
@@ -136,10 +137,11 @@ function dump(o, indent, nested, level)
nested[o] = true
indent = indent or "\t"
level = level or 1
- local t = {}
+
+ local ret = {}
local dumped_indexes = {}
for i, v in ipairs(o) do
- t[#t + 1] = dump(v, indent, nested, level + 1)
+ ret[#ret + 1] = dump(v, indent, nested, level + 1)
dumped_indexes[i] = true
end
for k, v in pairs(o) do
@@ -148,7 +150,7 @@ function dump(o, indent, nested, level)
k = "["..dump(k, indent, nested, level + 1).."]"
end
v = dump(v, indent, nested, level + 1)
- t[#t + 1] = k.." = "..v
+ ret[#ret + 1] = k.." = "..v
end
end
nested[o] = nil
@@ -157,10 +159,10 @@ function dump(o, indent, nested, level)
local end_indent_str = "\n"..string.rep(indent, level - 1)
return string.format("{%s%s%s}",
indent_str,
- table.concat(t, ","..indent_str),
+ table.concat(ret, ","..indent_str),
end_indent_str)
end
- return "{"..table.concat(t, ", ").."}"
+ return "{"..table.concat(ret, ", ").."}"
end
--------------------------------------------------------------------------------
@@ -244,6 +246,20 @@ function math.sign(x, tolerance)
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)
local parts = text:split(DIR_DELIM)
@@ -393,9 +409,8 @@ if INIT == "game" then
end
local old_itemstack = ItemStack(itemstack)
- local new_itemstack, removed = core.item_place_node(
- itemstack, placer, pointed_thing, param2, prevent_after_place
- )
+ local new_itemstack = core.item_place_node(itemstack, placer,
+ pointed_thing, param2, prevent_after_place)
return infinitestacks and old_itemstack or new_itemstack
end
diff --git a/builtin/common/serialize.lua b/builtin/common/serialize.lua
index 692ddd5f0..c91d2d5ce 100644
--- a/builtin/common/serialize.lua
+++ b/builtin/common/serialize.lua
@@ -218,4 +218,3 @@ test_in = {escape_chars="\n\r\t\v\\\"\'", non_european="θשׁ٩∂"}
test_out = core.deserialize(core.serialize(test_in))
assert(test_in.escape_chars == test_out.escape_chars)
assert(test_in.non_european == test_out.non_european)
-
diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua
index c3d380ed3..ca6541eb4 100644
--- a/builtin/common/vector.lua
+++ b/builtin/common/vector.lua
@@ -70,6 +70,25 @@ function vector.direction(pos1, pos2)
})
end
+function vector.angle(a, b)
+ local dotp = vector.dot(a, b)
+ local cp = vector.cross(a, b)
+ local crossplen = vector.length(cp)
+ return math.atan2(crossplen, dotp)
+end
+
+function vector.dot(a, b)
+ return a.x * b.x + a.y * b.y + a.z * b.z
+end
+
+function vector.cross(a, b)
+ return {
+ x = a.y * b.z - a.z * b.y,
+ y = a.z * b.x - a.x * b.z,
+ z = a.x * b.y - a.y * b.x
+ }
+end
+
function vector.add(a, b)
if type(b) == "table" then
return {x = a.x + b.x,