diff options
Diffstat (limited to 'builtin/game')
-rw-r--r-- | builtin/game/auth.lua | 16 | ||||
-rw-r--r-- | builtin/game/chatcommands.lua | 124 | ||||
-rw-r--r-- | builtin/game/constants.lua | 10 | ||||
-rw-r--r-- | builtin/game/detached_inventory.lua | 4 | ||||
-rw-r--r-- | builtin/game/falling.lua | 110 | ||||
-rw-r--r-- | builtin/game/forceloading.lua | 41 | ||||
-rw-r--r-- | builtin/game/init.lua | 14 | ||||
-rw-r--r-- | builtin/game/item.lua | 11 | ||||
-rw-r--r-- | builtin/game/item_entity.lua | 4 | ||||
-rw-r--r-- | builtin/game/misc.lua | 64 | ||||
-rw-r--r-- | builtin/game/mod_profiling.lua | 356 | ||||
-rw-r--r-- | builtin/game/privileges.lua | 64 | ||||
-rw-r--r-- | builtin/game/register.lua | 43 | ||||
-rw-r--r-- | builtin/game/voxelarea.lua | 49 |
14 files changed, 404 insertions, 506 deletions
diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua index deb811b14..46fe3d342 100644 --- a/builtin/game/auth.lua +++ b/builtin/game/auth.lua @@ -199,3 +199,19 @@ core.register_on_joinplayer(function(player) record_login(player:get_player_name()) 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("\nCannot create new player called '%s'. ".. + "Another account called '%s' is already registered. ".. + "Please check the spelling if it's your account ".. + "or use a different nickname.", name, k) + end + end +end) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 3350140ee..2bd93855b 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -14,19 +14,6 @@ function core.register_chatcommand(cmd, def) core.chatcommands[cmd] = def end -if core.setting_getbool("mod_profiling") then - local tracefct = profiling_print_log - profiling_print_log = nil - core.register_chatcommand("save_mod_profile", - { - params = "", - description = "save mod profiling data to logfile " .. - "(depends on default loglevel)", - func = tracefct, - privs = { server=true } - }) -end - core.register_on_chat_message(function(name, message) local cmd, param = string.match(message, "^/([^ ]+) *(.*)") if not param then @@ -51,6 +38,12 @@ core.register_on_chat_message(function(name, message) return true -- Handled chat message end) +if core.setting_getbool("profiler.load") then + -- Run after register_chatcommand and its register_on_chat_message + -- Before any chattcommands that should be profiled + profiler.init_chatcommand() +end + -- Parses a "range" string in the format of "here (number)" or -- "(x1, y1, z1) (x2, y2, z2)", returning two position vectors local function parse_range_str(player_name, str) @@ -102,7 +95,7 @@ core.register_chatcommand("help", { description = "Get help for commands or list privileges", func = function(name, param) local function format_help_line(cmd, def) - local msg = "/"..cmd + local msg = core.colorize("#00ffff", "/"..cmd) if def.params and def.params ~= "" then msg = msg .. " " .. def.params end @@ -154,60 +147,79 @@ core.register_chatcommand("help", { core.register_chatcommand("privs", { params = "<name>", description = "print out privileges of player", - func = function(name, param) - param = (param ~= "" and param or name) - return true, "Privileges of " .. param .. ": " + func = function(caller, param) + param = param:trim() + local name = (param ~= "" and param or caller) + return true, "Privileges of " .. name .. ": " .. core.privs_to_string( - core.get_player_privs(param), ' ') + core.get_player_privs(name), ' ') end, }) + +local function handle_grant_command(caller, grantname, grantprivstr) + local caller_privs = minetest.get_player_privs(caller) + if not (caller_privs.privs or caller_privs.basic_privs) then + return false, "Your privileges are insufficient." + end + + if not core.auth_table[grantname] then + return false, "Player " .. grantname .. " does not exist." + end + local grantprivs = core.string_to_privs(grantprivstr) + if grantprivstr == "all" then + grantprivs = core.registered_privileges + end + local privs = core.get_player_privs(grantname) + local privs_unknown = "" + local basic_privs = + core.string_to_privs(core.setting_get("basic_privs") or "interact,shout") + for priv, _ in pairs(grantprivs) do + if not basic_privs[priv] and not caller_privs.privs then + return false, "Your privileges are insufficient." + end + if not core.registered_privileges[priv] then + privs_unknown = privs_unknown .. "Unknown privilege: " .. priv .. "\n" + end + privs[priv] = true + end + if privs_unknown ~= "" then + return false, privs_unknown + end + core.set_player_privs(grantname, privs) + core.log("action", caller..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname) + if grantname ~= caller then + core.chat_send_player(grantname, caller + .. " granted you privileges: " + .. core.privs_to_string(grantprivs, ' ')) + end + return true, "Privileges of " .. grantname .. ": " + .. core.privs_to_string( + core.get_player_privs(grantname), ' ') +end + core.register_chatcommand("grant", { params = "<name> <privilege>|all", description = "Give privilege to player", func = function(name, param) - if not core.check_player_privs(name, {privs=true}) and - not core.check_player_privs(name, {basic_privs=true}) then - return false, "Your privileges are insufficient." - end local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)") if not grantname or not grantprivstr then return false, "Invalid parameters (see /help grant)" - elseif not core.auth_table[grantname] then - return false, "Player " .. grantname .. " does not exist." - end - local grantprivs = core.string_to_privs(grantprivstr) - if grantprivstr == "all" then - grantprivs = core.registered_privileges - end - local privs = core.get_player_privs(grantname) - local privs_unknown = "" - local basic_privs = - core.string_to_privs(core.setting_get("basic_privs") or "interact,shout") - for priv, _ in pairs(grantprivs) do - if not basic_privs[priv] and - not core.check_player_privs(name, {privs=true}) then - return false, "Your privileges are insufficient." - end - if not core.registered_privileges[priv] then - privs_unknown = privs_unknown .. "Unknown privilege: " .. priv .. "\n" - end - privs[priv] = true - end - if privs_unknown ~= "" then - return false, privs_unknown - end - core.set_player_privs(grantname, privs) - core.log("action", name..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname) - if grantname ~= name then - core.chat_send_player(grantname, name - .. " granted you privileges: " - .. core.privs_to_string(grantprivs, ' ')) - end - return true, "Privileges of " .. grantname .. ": " - .. core.privs_to_string( - core.get_player_privs(grantname), ' ') + end + return handle_grant_command(name, grantname, grantprivstr) + end, +}) + +core.register_chatcommand("grantme", { + params = "<privilege>|all", + description = "Grant privileges to yourself", + func = function(name, param) + if param == "" then + return false, "Invalid parameters (see /help grantme)" + end + return handle_grant_command(name, name, param) end, }) + core.register_chatcommand("revoke", { params = "<name> <privilege>|all", description = "Remove privilege from player", diff --git a/builtin/game/constants.lua b/builtin/game/constants.lua index d0b7c753c..50c515b24 100644 --- a/builtin/game/constants.lua +++ b/builtin/game/constants.lua @@ -4,14 +4,24 @@ -- Constants values for use with the Lua API -- +-- mapnode.h -- Built-in Content IDs (for use with VoxelManip API) core.CONTENT_UNKNOWN = 125 core.CONTENT_AIR = 126 core.CONTENT_IGNORE = 127 +-- emerge.h -- Block emerge status constants (for use with core.emerge_area) core.EMERGE_CANCELLED = 0 core.EMERGE_ERRORED = 1 core.EMERGE_FROM_MEMORY = 2 core.EMERGE_FROM_DISK = 3 core.EMERGE_GENERATED = 4 + +-- constants.h +-- Size of mapblocks in nodes +core.MAP_BLOCKSIZE = 16 + +-- light.h +-- Maximum value for node 'light_source' parameter +core.LIGHT_MAX = 14 diff --git a/builtin/game/detached_inventory.lua b/builtin/game/detached_inventory.lua index b5d106b04..420e89ff2 100644 --- a/builtin/game/detached_inventory.lua +++ b/builtin/game/detached_inventory.lua @@ -2,7 +2,7 @@ core.detached_inventories = {} -function core.create_detached_inventory(name, callbacks) +function core.create_detached_inventory(name, callbacks, player_name) local stuff = {} stuff.name = name if callbacks then @@ -15,6 +15,6 @@ function core.create_detached_inventory(name, callbacks) end stuff.mod_origin = core.get_current_modname() or "??" core.detached_inventories[name] = stuff - return core.create_detached_inventory_raw(name) + return core.create_detached_inventory_raw(name, player_name) end diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index 57bb98cfd..4696ce481 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -1,5 +1,7 @@ -- Minetest: builtin/item.lua +local builtin_shared = ... + -- -- Falling stuff -- @@ -41,19 +43,20 @@ core.register_entity(":__builtin:falling_node", { end, on_step = function(self, dtime) - -- Set gravity + -- Set gravity local acceleration = self.object:getacceleration() if not vector.equals(acceleration, {x = 0, y = -10, z = 0}) then self.object:setacceleration({x = 0, y = -10, z = 0}) end - -- Turn to actual sand when collides to ground or just move + -- Turn to actual node when colliding with ground, or continue to 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 = core.get_node(bcp) - local bcd = core.registered_nodes[bcn.name] - -- Note: walkable is in the node definition, not in item groups - if not bcd or - (bcd.walkable or + -- Position of bottom center point + local bcp = {x = pos.x, y = pos.y - 0.7, z = pos.z} + -- Avoid bugs caused by an unloaded node below + local bcn = core.get_node_or_nil(bcp) + local bcd = bcn and core.registered_nodes[bcn.name] + if bcn and + (not bcd or bcd.walkable or (core.get_item_group(self.node.name, "float") ~= 0 and bcd.liquidtype ~= "none")) then if bcd and bcd.leveled and @@ -75,20 +78,20 @@ core.register_entity(":__builtin:falling_node", { local np = {x = bcp.x, y = bcp.y + 1, z = bcp.z} -- Check what's here local n2 = core.get_node(np) + local nd = core.registered_nodes[n2.name] -- If it's not air or liquid, remove node and replace it with -- it's drops - if n2.name ~= "air" and (not core.registered_nodes[n2.name] or - core.registered_nodes[n2.name].liquidtype == "none") then + if n2.name ~= "air" and (not nd or nd.liquidtype == "none") then core.remove_node(np) - if core.registered_nodes[n2.name].buildable_to == false then + if nd.buildable_to == false then -- Add dropped items local drops = core.get_node_drops(n2.name, "") - for _, dropped_item in ipairs(drops) do + for _, dropped_item in pairs(drops) do core.add_item(np, dropped_item) end end -- Run script hook - for _, callback in ipairs(core.registered_on_dignodes) do + for _, callback in pairs(core.registered_on_dignodes) do callback(np, n2) end end @@ -97,7 +100,7 @@ core.register_entity(":__builtin:falling_node", { core.add_node(np, self.node) end self.object:remove() - nodeupdate(np) + core.check_for_falling(np) return end local vel = self.object:getvelocity() @@ -108,15 +111,17 @@ core.register_entity(":__builtin:falling_node", { end }) -function spawn_falling_node(p, node) +local function spawn_falling_node(p, node) local obj = core.add_entity(p, "__builtin:falling_node") - obj:get_luaentity():set_node(node) + if obj then + obj:get_luaentity():set_node(node) + end end -function drop_attached_node(p) +local function drop_attached_node(p) local nn = core.get_node(p).name core.remove_node(p) - for _, item in ipairs(core.get_node_drops(nn, "")) do + for _, item in pairs(core.get_node_drops(nn, "")) do local pos = { x = p.x + math.random()/2 - 0.25, y = p.y + math.random()/2 - 0.25, @@ -126,11 +131,15 @@ function drop_attached_node(p) end end -function check_attached_node(p, n) +function builtin_shared.check_attached_node(p, n) local def = core.registered_nodes[n.name] local d = {x = 0, y = 0, z = 0} if def.paramtype2 == "wallmounted" then - d = core.wallmounted_to_dir(n.param2) + -- The fallback vector here is in case 'wallmounted to dir' is nil due + -- to voxelmanip placing a wallmounted node without resetting a + -- pre-existing param2 value that is out-of-range for wallmounted. + -- The fallback vector corresponds to param2 = 0. + d = core.wallmounted_to_dir(n.param2) or {x = 0, y = 1, z = 0} else d.y = -1 end @@ -147,19 +156,23 @@ end -- Some common functions -- -function nodeupdate_single(p) +function core.check_single_for_falling(p) local n = core.get_node(p) if core.get_item_group(n.name, "falling_node") ~= 0 then local p_bottom = {x = p.x, y = p.y - 1, z = p.z} - local n_bottom = core.get_node(p_bottom) - -- Note: walkable is in the node definition, not in item groups - if core.registered_nodes[n_bottom.name] and + -- Only spawn falling node if node below is loaded + local n_bottom = core.get_node_or_nil(p_bottom) + local d_bottom = n_bottom and core.registered_nodes[n_bottom.name] + if d_bottom and + (core.get_item_group(n.name, "float") == 0 or - core.registered_nodes[n_bottom.name].liquidtype == "none") and - (n.name ~= n_bottom.name or (core.registered_nodes[n_bottom.name].leveled and - core.get_node_level(p_bottom) < core.get_node_max_level(p_bottom))) and - (not core.registered_nodes[n_bottom.name].walkable or - core.registered_nodes[n_bottom.name].buildable_to) then + d_bottom.liquidtype == "none") and + + (n.name ~= n_bottom.name or (d_bottom.leveled and + core.get_node_level(p_bottom) < + core.get_node_max_level(p_bottom))) and + + (not d_bottom.walkable or d_bottom.buildable_to) then n.level = core.get_node_level(p) core.remove_node(p) spawn_falling_node(p, n) @@ -168,7 +181,7 @@ function nodeupdate_single(p) end if core.get_item_group(n.name, "attached_node") ~= 0 then - if not check_attached_node(p, n) then + if not builtin_shared.check_attached_node(p, n) then drop_attached_node(p) return true end @@ -181,7 +194,7 @@ end -- We don't walk diagonals, only our direct neighbors, and self. -- Down first as likely case, but always before self. The same with sides. -- Up must come last, so that things above self will also fall all at once. -local nodeupdate_neighbors = { +local check_for_falling_neighbors = { {x = -1, y = -1, z = 0}, {x = 1, y = -1, z = 0}, {x = 0, y = -1, z = -1}, @@ -195,7 +208,7 @@ local nodeupdate_neighbors = { {x = 0, y = 1, z = 0}, } -function nodeupdate(p) +function core.check_for_falling(p) -- Round p to prevent falling entities to get stuck. p = vector.round(p) @@ -214,10 +227,10 @@ function nodeupdate(p) n = n + 1 s[n] = {p = p, v = v} -- Select next node from neighbor list. - p = vector.add(p, nodeupdate_neighbors[v]) + p = vector.add(p, check_for_falling_neighbors[v]) -- Now we check out the node. If it is in need of an update, -- it will let us know in the return value (true = updated). - if not nodeupdate_single(p) then + if not core.check_single_for_falling(p) then -- If we don't need to "recurse" (walk) to it then pop -- our previous pos off the stack and continue from there, -- with the v value we were at when we last were at that @@ -249,12 +262,33 @@ end -- Global callbacks -- -function on_placenode(p, node) - nodeupdate(p) +local function on_placenode(p, node) + core.check_for_falling(p) end core.register_on_placenode(on_placenode) -function on_dignode(p, node) - nodeupdate(p) +local function on_dignode(p, node) + core.check_for_falling(p) end core.register_on_dignode(on_dignode) + +local function on_punchnode(p, node) + core.check_for_falling(p) +end +core.register_on_punchnode(on_punchnode) + +-- +-- Globally exported functions +-- + +-- TODO remove this function after the 0.4.15 release +function nodeupdate(p) + core.log("deprecated", "nodeupdate: deprecated, please use core.check_for_falling instead") + core.check_for_falling(p) +end + +-- TODO remove this function after the 0.4.15 release +function nodeupdate_single(p) + core.log("deprecated", "nodeupdate_single: deprecated, please use core.check_single_for_falling instead") + core.check_single_for_falling(p) +end diff --git a/builtin/game/forceloading.lua b/builtin/game/forceloading.lua index 8c9fbf512..8a05de36c 100644 --- a/builtin/game/forceloading.lua +++ b/builtin/game/forceloading.lua @@ -5,9 +5,10 @@ core.forceload_block = nil core.forceload_free_block = nil local blocks_forceloaded +local blocks_temploaded = {} local total_forceloaded = 0 -local BLOCKSIZE = 16 +local BLOCKSIZE = core.MAP_BLOCKSIZE local function get_blockpos(pos) return { x = math.floor(pos.x/BLOCKSIZE), @@ -15,32 +16,52 @@ local function get_blockpos(pos) z = math.floor(pos.z/BLOCKSIZE)} end -function core.forceload_block(pos) +-- When we create/free a forceload, it's either transient or persistent. We want +-- to add to/remove from the table that corresponds to the type of forceload, but +-- we also need the other table because whether we forceload a block depends on +-- both tables. +-- This function returns the "primary" table we are adding to/removing from, and +-- the other table. +local function get_relevant_tables(transient) + if transient then + return blocks_temploaded, blocks_forceloaded + else + return blocks_forceloaded, blocks_temploaded + end +end + +function core.forceload_block(pos, transient) local blockpos = get_blockpos(pos) local hash = core.hash_node_position(blockpos) - if blocks_forceloaded[hash] ~= nil then - blocks_forceloaded[hash] = blocks_forceloaded[hash] + 1 + local relevant_table, other_table = get_relevant_tables(transient) + if relevant_table[hash] ~= nil then + relevant_table[hash] = relevant_table[hash] + 1 return true + elseif other_table[hash] ~= nil then + relevant_table[hash] = 1 else if total_forceloaded >= (tonumber(core.setting_get("max_forceloaded_blocks")) or 16) then return false end total_forceloaded = total_forceloaded+1 - blocks_forceloaded[hash] = 1 + relevant_table[hash] = 1 forceload_block(blockpos) return true end end -function core.forceload_free_block(pos) +function core.forceload_free_block(pos, transient) local blockpos = get_blockpos(pos) local hash = core.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 + local relevant_table, other_table = get_relevant_tables(transient) + if relevant_table[hash] == nil then return end + if relevant_table[hash] > 1 then + relevant_table[hash] = relevant_table[hash] - 1 + elseif other_table[hash] ~= nil then + relevant_table[hash] = nil else total_forceloaded = total_forceloaded-1 - blocks_forceloaded[hash] = nil + relevant_table[hash] = nil forceload_free_block(blockpos) end end diff --git a/builtin/game/init.lua b/builtin/game/init.lua index a6cfa3bf8..b5e2f7cca 100644 --- a/builtin/game/init.lua +++ b/builtin/game/init.lua @@ -3,14 +3,18 @@ local scriptpath = core.get_builtin_path()..DIR_DELIM local commonpath = scriptpath.."common"..DIR_DELIM local gamepath = scriptpath.."game"..DIR_DELIM +-- Shared between builtin files, but +-- not exposed to outer context +local builtin_shared = {} + dofile(commonpath.."vector.lua") dofile(gamepath.."constants.lua") -dofile(gamepath.."item.lua") +assert(loadfile(gamepath.."item.lua"))(builtin_shared) dofile(gamepath.."register.lua") -if core.setting_getbool("mod_profiling") then - dofile(gamepath.."mod_profiling.lua") +if core.setting_getbool("profiler.load") then + profiler = dofile(scriptpath.."profiler"..DIR_DELIM.."init.lua") end dofile(gamepath.."item_entity.lua") @@ -21,8 +25,10 @@ dofile(gamepath.."auth.lua") dofile(gamepath.."chatcommands.lua") dofile(gamepath.."static_spawn.lua") dofile(gamepath.."detached_inventory.lua") -dofile(gamepath.."falling.lua") +assert(loadfile(gamepath.."falling.lua"))(builtin_shared) dofile(gamepath.."features.lua") dofile(gamepath.."voxelarea.lua") dofile(gamepath.."forceloading.lua") dofile(gamepath.."statbars.lua") + +profiler = nil diff --git a/builtin/game/item.lua b/builtin/game/item.lua index 36c2c1a68..bf456a4e0 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -1,5 +1,7 @@ -- Minetest: builtin/item.lua +local builtin_shared = ... + local function copy_pointed_thing(pointed_thing) return { type = pointed_thing.type, @@ -250,7 +252,9 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) 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 + if def.place_param2 ~= nil then + newnode.param2 = def.place_param2 + elseif def.paramtype2 == 'wallmounted' and not param2 then local dir = { x = under.x - above.x, y = under.y - above.y, @@ -273,7 +277,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) -- Check if the node is attached and if it can be placed there if core.get_item_group(def.name, "attached_node") ~= 0 and - not check_attached_node(place_to, newnode) then + not builtin_shared.check_attached_node(place_to, newnode) then core.log("action", "attached node " .. def.name .. " can not be placed at " .. core.pos_to_string(place_to)) return itemstack, false @@ -470,6 +474,9 @@ function core.node_dig(pos, node, digger) -- Wear out tool if not core.setting_getbool("creative_mode") then wielded:add_wear(dp.wear) + if wielded:get_count() == 0 and wdef.sound and wdef.sound.breaks then + core.sound_play(wdef.sound.breaks, {pos = pos, gain = 0.5}) + end end end digger:set_wielded_item(wielded) diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index a66bf33d0..be158c119 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -31,7 +31,6 @@ core.register_entity(":__builtin:item", { spritediv = {x = 1, y = 1}, initial_sprite_basepos = {x = 0, y = 0}, is_visible = false, - infotext = "", }, itemstring = '', @@ -51,7 +50,6 @@ core.register_entity(":__builtin:item", { local c = s local itemtable = stack:to_table() local itemname = nil - local description = "" if itemtable then itemname = stack:to_table().name end @@ -60,7 +58,6 @@ core.register_entity(":__builtin:item", { if core.registered_items[itemname] then item_texture = core.registered_items[itemname].inventory_image item_type = core.registered_items[itemname].type - description = core.registered_items[itemname].description end local prop = { is_visible = true, @@ -69,7 +66,6 @@ core.register_entity(":__builtin:item", { visual_size = {x = s, y = s}, collisionbox = {-c, -c, -c, c, c, c}, automatic_rotate = math.pi * 0.5, - infotext = description, } self.object:set_properties(prop) end, diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index de41cfc91..7caa9e7ba 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -38,7 +38,7 @@ core.register_globalstep(function(dtime) end) function core.after(after, func, ...) - assert(tonumber(time) and type(func) == "function", + assert(tonumber(after) and type(func) == "function", "Invalid core.after invocation") jobs[#jobs + 1] = { func = func, @@ -48,11 +48,13 @@ function core.after(after, func, ...) } end -function core.check_player_privs(player_or_name, ...) - local name = player_or_name - -- Check if we have been provided with a Player object. - if type(name) ~= "string" then +function core.check_player_privs(name, ...) + local arg_type = type(name) + if (arg_type == "userdata" or arg_type == "table") and + name.get_player_name then -- If it quacks like a Player... name = name:get_player_name() + elseif arg_type ~= "string" then + error("Invalid core.check_player_privs argument type: " .. arg_type, 2) end local requested_privs = {...} @@ -85,11 +87,21 @@ end local player_list = {} core.register_on_joinplayer(function(player) - player_list[player:get_player_name()] = player + local player_name = player:get_player_name() + player_list[player_name] = player + if not minetest.is_singleplayer() then + core.chat_send_all("*** " .. player_name .. " joined the game.") + end end) -core.register_on_leaveplayer(function(player) - player_list[player:get_player_name()] = nil +core.register_on_leaveplayer(function(player, timed_out) + local player_name = player:get_player_name() + player_list[player_name] = nil + local announcement = "*** " .. player_name .. " left the game." + if timed_out then + announcement = announcement .. " (timed out)" + end + core.chat_send_all(announcement) end) function core.get_connected_players() @@ -197,3 +209,39 @@ function core.http_add_fetch(httpenv) return httpenv end + +if minetest.setting_getbool("disable_escape_sequences") then + + function core.get_color_escape_sequence(color) + return "" + end + + function core.get_background_escape_sequence(color) + return "" + end + + function core.colorize(color, message) + return message + end + +else + + local ESCAPE_CHAR = string.char(0x1b) + function core.get_color_escape_sequence(color) + return ESCAPE_CHAR .. "(c@" .. color .. ")" + end + + function core.get_background_escape_sequence(color) + return ESCAPE_CHAR .. "(b@" .. color .. ")" + end + + function core.colorize(color, message) + return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("#ffffff") + end + +end + +function core.close_formspec(player_name, formname) + return minetest.show_formspec(player_name, formname, "") +end + diff --git a/builtin/game/mod_profiling.lua b/builtin/game/mod_profiling.lua deleted file mode 100644 index df2d10221..000000000 --- a/builtin/game/mod_profiling.lua +++ /dev/null @@ -1,356 +0,0 @@ --- Minetest: builtin/game/mod_profiling.lua - -local mod_statistics = {} -mod_statistics.step_total = 0 -mod_statistics.data = {} -mod_statistics.stats = {} -mod_statistics.stats["total"] = { - min_us = math.huge, - max_us = 0, - avg_us = 0, - min_per = 100, - max_per = 100, - avg_per = 100 -} - -local replacement_table = { - "register_globalstep", - "register_on_placenode", - "register_on_dignode", - "register_on_punchnode", - "register_on_generated", - "register_on_newplayer", - "register_on_dieplayer", - "register_on_respawnplayer", - "register_on_prejoinplayer", - "register_on_joinplayer", - "register_on_leaveplayer", - "register_on_cheat", - "register_on_chat_message", - "register_on_player_receive_fields", - "register_on_mapgen_init", - "register_on_craft", - "register_craft_predict", - "register_on_item_eat" -} - --------------------------------------------------------------------------------- -function mod_statistics.log_time(type, modname, time_in_us) - - if modname == nil then - modname = "builtin" - end - - if mod_statistics.data[modname] == nil then - mod_statistics.data[modname] = {} - end - - if mod_statistics.data[modname][type] == nil then - mod_statistics.data[modname][type] = 0 - end - - mod_statistics.data[modname][type] = - mod_statistics.data[modname][type] + time_in_us - mod_statistics.step_total = mod_statistics.step_total + time_in_us -end - --------------------------------------------------------------------------------- -function mod_statistics.update_statistics(dtime) - for modname,types in pairs(mod_statistics.data) do - - if mod_statistics.stats[modname] == nil then - mod_statistics.stats[modname] = { - min_us = math.huge, - max_us = 0, - avg_us = 0, - min_per = 100, - max_per = 0, - avg_per = 0 - } - end - - local modtime = 0 - for type,time in pairs(types) do - modtime = modtime + time - if mod_statistics.stats[modname].types == nil then - mod_statistics.stats[modname].types = {} - end - if mod_statistics.stats[modname].types[type] == nil then - mod_statistics.stats[modname].types[type] = { - min_us = math.huge, - max_us = 0, - avg_us = 0, - min_per = 100, - max_per = 0, - avg_per = 0 - } - end - - local toupdate = mod_statistics.stats[modname].types[type] - - --update us values - toupdate.min_us = math.min(time, toupdate.min_us) - toupdate.max_us = math.max(time, toupdate.max_us) - --TODO find better algorithm - toupdate.avg_us = toupdate.avg_us * 0.99 + modtime * 0.01 - - --update percentage values - local cur_per = (time/mod_statistics.step_total) * 100 - toupdate.min_per = math.min(toupdate.min_per, cur_per) - - toupdate.max_per = math.max(toupdate.max_per, cur_per) - - --TODO find better algorithm - toupdate.avg_per = toupdate.avg_per * 0.99 + cur_per * 0.01 - - mod_statistics.data[modname][type] = 0 - end - - --per mod statistics - --update us values - mod_statistics.stats[modname].min_us = - math.min(modtime, mod_statistics.stats[modname].min_us) - mod_statistics.stats[modname].max_us = - math.max(modtime, mod_statistics.stats[modname].max_us) - --TODO find better algorithm - mod_statistics.stats[modname].avg_us = - mod_statistics.stats[modname].avg_us * 0.99 + modtime * 0.01 - - --update percentage values - local cur_per = (modtime/mod_statistics.step_total) * 100 - mod_statistics.stats[modname].min_per = - math.min(mod_statistics.stats[modname].min_per, cur_per) - - mod_statistics.stats[modname].max_per = - math.max(mod_statistics.stats[modname].max_per, cur_per) - - --TODO find better algorithm - mod_statistics.stats[modname].avg_per = - mod_statistics.stats[modname].avg_per * 0.99 + cur_per * 0.01 - end - - --update "total" - mod_statistics.stats["total"].min_us = - math.min(mod_statistics.step_total, mod_statistics.stats["total"].min_us) - mod_statistics.stats["total"].max_us = - math.max(mod_statistics.step_total, mod_statistics.stats["total"].max_us) - --TODO find better algorithm - mod_statistics.stats["total"].avg_us = - mod_statistics.stats["total"].avg_us * 0.99 + - mod_statistics.step_total * 0.01 - - mod_statistics.step_total = 0 -end - --------------------------------------------------------------------------------- -local function build_callback(log_id, fct) - return function( toregister ) - local modname = core.get_current_modname() - - fct(function(...) - local starttime = core.get_us_time() - -- note maximum 10 return values are supported unless someone finds - -- a way to store a variable lenght return value list - local r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 = toregister(...) - local delta = core.get_us_time() - starttime - mod_statistics.log_time(log_id, modname, delta) - return r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 - end - ) - end -end - --------------------------------------------------------------------------------- -function profiling_print_log(cmd, filter) - - print("Filter:" .. dump(filter)) - - core.log("action", "Values below show times/percentages per server step.") - core.log("action", "Following suffixes are used for entities:") - core.log("action", "\t#oa := on_activate, #os := on_step, #op := on_punch, #or := on_rightclick, #gs := get_staticdata") - core.log("action", - string.format("%16s | %25s | %10s | %10s | %10s | %9s | %9s | %9s", - "modname", "type" , "min µs", "max µs", "avg µs", "min %", "max %", "avg %") - ) - core.log("action", - "-----------------+---------------------------+-----------+" .. - "-----------+-----------+-----------+-----------+-----------") - for modname,statistics in pairs(mod_statistics.stats) do - if modname ~= "total" then - - if (filter == "") or (modname == filter) then - if modname:len() > 16 then - modname = "..." .. modname:sub(-13) - end - - core.log("action", - string.format("%16s | %25s | %9d | %9d | %9d | %9d | %9d | %9d", - modname, - " ", - statistics.min_us, - statistics.max_us, - statistics.avg_us, - statistics.min_per, - statistics.max_per, - statistics.avg_per) - ) - if core.setting_getbool("detailed_profiling") then - if statistics.types ~= nil then - for type,typestats in pairs(statistics.types) do - - if type:len() > 25 then - type = "..." .. type:sub(-22) - end - - core.log("action", - string.format( - "%16s | %25s | %9d | %9d | %9d | %9d | %9d | %9d", - " ", - type, - typestats.min_us, - typestats.max_us, - typestats.avg_us, - typestats.min_per, - typestats.max_per, - typestats.avg_per) - ) - end - end - end - end - end - end - core.log("action", - "-----------------+---------------------------+-----------+" .. - "-----------+-----------+-----------+-----------+-----------") - - if filter == "" then - core.log("action", - string.format("%16s | %25s | %9d | %9d | %9d | %9d | %9d | %9d", - "total", - " ", - mod_statistics.stats["total"].min_us, - mod_statistics.stats["total"].max_us, - mod_statistics.stats["total"].avg_us, - mod_statistics.stats["total"].min_per, - mod_statistics.stats["total"].max_per, - mod_statistics.stats["total"].avg_per) - ) - end - core.log("action", " ") - - return true -end - --------------------------------------------------------------------------------- -local function initialize_profiling() - core.log("action", "Initialize tracing") - - mod_statistics.entity_callbacks = {} - - -- first register our own globalstep handler - core.register_globalstep(mod_statistics.update_statistics) - - local rp_register_entity = core.register_entity - core.register_entity = function(name, prototype) - local modname = core.get_current_modname() - local new_on_activate = nil - local new_on_step = nil - local new_on_punch = nil - local new_rightclick = nil - local new_get_staticdata = nil - - if prototype.on_activate ~= nil then - local cbid = name .. "#oa" - mod_statistics.entity_callbacks[cbid] = prototype.on_activate - new_on_activate = function(self, staticdata, dtime_s) - local starttime = core.get_us_time() - mod_statistics.entity_callbacks[cbid](self, staticdata, dtime_s) - local delta = core.get_us_time() -starttime - mod_statistics.log_time(cbid, modname, delta) - end - end - - if prototype.on_step ~= nil then - local cbid = name .. "#os" - mod_statistics.entity_callbacks[cbid] = prototype.on_step - new_on_step = function(self, dtime) - local starttime = core.get_us_time() - mod_statistics.entity_callbacks[cbid](self, dtime) - local delta = core.get_us_time() -starttime - mod_statistics.log_time(cbid, modname, delta) - end - end - - if prototype.on_punch ~= nil then - local cbid = name .. "#op" - mod_statistics.entity_callbacks[cbid] = prototype.on_punch - new_on_punch = function(self, hitter) - local starttime = core.get_us_time() - mod_statistics.entity_callbacks[cbid](self, hitter) - local delta = core.get_us_time() -starttime - mod_statistics.log_time(cbid, modname, delta) - end - end - - if prototype.rightclick ~= nil then - local cbid = name .. "#rc" - mod_statistics.entity_callbacks[cbid] = prototype.rightclick - new_rightclick = function(self, clicker) - local starttime = core.get_us_time() - mod_statistics.entity_callbacks[cbid](self, clicker) - local delta = core.get_us_time() -starttime - mod_statistics.log_time(cbid, modname, delta) - end - end - - if prototype.get_staticdata ~= nil then - local cbid = name .. "#gs" - mod_statistics.entity_callbacks[cbid] = prototype.get_staticdata - new_get_staticdata = function(self) - local starttime = core.get_us_time() - local retval = mod_statistics.entity_callbacks[cbid](self) - local delta = core.get_us_time() -starttime - mod_statistics.log_time(cbid, modname, delta) - return retval - end - end - - prototype.on_activate = new_on_activate - prototype.on_step = new_on_step - prototype.on_punch = new_on_punch - prototype.rightclick = new_rightclick - prototype.get_staticdata = new_get_staticdata - - rp_register_entity(name,prototype) - end - - for i,v in ipairs(replacement_table) do - local to_replace = core[v] - core[v] = build_callback(v, to_replace) - end - - local rp_register_abm = core.register_abm - core.register_abm = function(spec) - - local modname = core.get_current_modname() - - local replacement_spec = { - nodenames = spec.nodenames, - neighbors = spec.neighbors, - interval = spec.interval, - chance = spec.chance, - action = function(pos, node, active_object_count, active_object_count_wider) - local starttime = core.get_us_time() - spec.action(pos, node, active_object_count, active_object_count_wider) - local delta = core.get_us_time() - starttime - mod_statistics.log_time("abm", modname, delta) - end - } - rp_register_abm(replacement_spec) - end - - core.log("action", "Mod profiling initialized") -end - -initialize_profiling() diff --git a/builtin/game/privileges.lua b/builtin/game/privileges.lua index bd5ead624..56e090f4c 100644 --- a/builtin/game/privileges.lua +++ b/builtin/game/privileges.lua @@ -26,18 +26,46 @@ function core.register_privilege(name, param) end core.register_privilege("interact", "Can interact with things and modify the world") -core.register_privilege("teleport", "Can use /teleport command") -core.register_privilege("bring", "Can teleport other players") -core.register_privilege("settime", "Can use /time") -core.register_privilege("privs", "Can modify privileges") -core.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges") -core.register_privilege("server", "Can do server maintenance stuff") -core.register_privilege("protection_bypass", "Can bypass node protection in the world") core.register_privilege("shout", "Can speak in chat") -core.register_privilege("ban", "Can ban and unban players") -core.register_privilege("kick", "Can kick players") -core.register_privilege("give", "Can use /give and /giveme") -core.register_privilege("password", "Can use /setpassword and /clearpassword") +core.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges") +core.register_privilege("privs", "Can modify privileges") + +core.register_privilege("teleport", { + description = "Can use /teleport command", + give_to_singleplayer = false, +}) +core.register_privilege("bring", { + description = "Can teleport other players", + give_to_singleplayer = false, +}) +core.register_privilege("settime", { + description = "Can use /time", + give_to_singleplayer = false, +}) +core.register_privilege("server", { + description = "Can do server maintenance stuff", + give_to_singleplayer = false, +}) +core.register_privilege("protection_bypass", { + description = "Can bypass node protection in the world", + give_to_singleplayer = false, +}) +core.register_privilege("ban", { + description = "Can ban and unban players", + give_to_singleplayer = false, +}) +core.register_privilege("kick", { + description = "Can kick players", + give_to_singleplayer = false, +}) +core.register_privilege("give", { + description = "Can use /give and /giveme", + give_to_singleplayer = false, +}) +core.register_privilege("password", { + description = "Can use /setpassword and /clearpassword", + give_to_singleplayer = false, +}) core.register_privilege("fly", { description = "Can fly using the free_move mode", give_to_singleplayer = false, @@ -50,5 +78,15 @@ core.register_privilege("noclip", { description = "Can fly through walls", give_to_singleplayer = false, }) -core.register_privilege("rollback", "Can use the rollback functionality") - +core.register_privilege("rollback", { + description = "Can use the rollback functionality", + give_to_singleplayer = false, +}) +core.register_privilege("zoom", { + description = "Can zoom the camera", + give_to_singleplayer = false, +}) +core.register_privilege("debug", { + description = "Allows enabling various debug options that may affect gameplay", + give_to_singleplayer = false, +}) diff --git a/builtin/game/register.lua b/builtin/game/register.lua index f330491a2..90f095e9f 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -7,6 +7,9 @@ local register_item_raw = core.register_item_raw core.register_item_raw = nil +local unregister_item_raw = core.unregister_item_raw +core.unregister_item_raw = nil + local register_alias_raw = core.register_alias_raw core.register_alias_raw = nil @@ -124,6 +127,11 @@ function core.register_item(name, itemdef) fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8}, } end + if itemdef.light_source and itemdef.light_source > core.LIGHT_MAX then + itemdef.light_source = core.LIGHT_MAX + core.log("warning", "Node 'light_source' value exceeds maximum," .. + " limiting to maximum: " ..name) + end setmetatable(itemdef, {__index = core.nodedef_default}) core.registered_nodes[itemdef.name] = itemdef elseif itemdef.type == "craft" then @@ -172,6 +180,27 @@ function core.register_item(name, itemdef) register_item_raw(itemdef) end +function core.unregister_item(name) + if not core.registered_items[name] then + core.log("warning", "Not unregistering item " ..name.. + " because it doesn't exist.") + return + end + -- Erase from registered_* table + local type = core.registered_items[name].type + if type == "node" then + core.registered_nodes[name] = nil + elseif type == "craft" then + core.registered_craftitems[name] = nil + elseif type == "tool" then + core.registered_tools[name] = nil + end + core.registered_items[name] = nil + + + unregister_item_raw(name) +end + function core.register_node(name, nodedef) nodedef.type = "node" core.register_item(name, nodedef) @@ -242,6 +271,20 @@ function core.register_alias(name, convert_to) end end +function core.register_alias_force(name, convert_to) + if forbidden_item_names[name] then + error("Unable to register alias: Name is forbidden: " .. name) + end + if core.registered_items[name] ~= nil then + core.unregister_item(name) + core.log("info", "Removed item " ..name.. + " while attempting to force add an alias") + end + --core.log("Registering alias: " .. name .. " -> " .. convert_to) + core.registered_aliases[name] = convert_to + register_alias_raw(name, convert_to) +end + function core.on_craft(itemstack, player, old_craft_list, craft_inv) for _, func in ipairs(core.registered_on_crafts) do itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack diff --git a/builtin/game/voxelarea.lua b/builtin/game/voxelarea.lua index 6d926c940..724761414 100644 --- a/builtin/game/voxelarea.lua +++ b/builtin/game/voxelarea.lua @@ -50,7 +50,7 @@ end function VoxelArea:position(i) local p = {} local MinEdge = self.MinEdge - + i = i - 1 p.z = math.floor(i / self.zstride) + MinEdge.z @@ -84,23 +84,46 @@ 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 + local xrange = maxx - minx + 1 + local nextaction = i + 1 + xrange + + local y = 0 + local yrange = maxy - miny + 1 + local yreqstride = self.ystride - xrange + + local z = 0 + local zrange = maxz - minz + 1 + local multistride = self.zstride - ((yrange - 1) * self.ystride + xrange) + return function() + -- continue i until it needs to jump i = i + 1 - if i % zstride == zoff then - i = i + zstridediff - elseif i % ystride == yoff then - i = i + ystridediff + if i ~= nextaction then + return i end - if i <= last then + + -- continue y until maxy is exceeded + y = y + 1 + if y ~= yrange then + -- set i to index(minx, miny + y, minz + z) - 1 + i = i + yreqstride + nextaction = i + xrange return i end + + -- continue z until maxz is exceeded + z = z + 1 + if z == zrange then + -- cuboid finished, return nil + return + end + + -- set i to index(minx, miny, minz + z) - 1 + i = i + multistride + + y = 0 + nextaction = i + xrange + return i end end |