-- LUALOCALS < --------------------------------------------------------- local minetest, pairs, string, table, type = minetest, pairs, string, table, type local string_gmatch, string_match, string_sub, table_concat = string.gmatch, string.match, string.sub, table.concat -- LUALOCALS > --------------------------------------------------------- local modname = minetest.get_current_modname() local keepinteract = minetest.settings:get_bool(modname .. "_keep_interact") local keepshout = minetest.settings:get_bool(modname .. "_keep_shout") local function hasprivs(player, ...) if type(player) == "string" then player = minetest.get_player_by_name(player) end if not (player and player.is_player and player:is_player()) then return end return minetest.check_player_privs(player, ...) end local function isstealth(player) return hasprivs(player, "stealth") end local saved = {} local function nonzero(t) for _, v in pairs(t) do if v ~= 0 then return true end end end local function updatevisible(player) local props = player:get_properties() if not props then return end local pname = player:get_player_name() local atts = player:get_nametag_attributes() local isvis = nonzero(props.visual_size) or props.pointable or atts.color.a > 0 or props.makes_footstep_sound if isvis and not saved[pname] then saved[pname] = {p = props, a = atts} end local needvis = not isstealth(player) if isvis == needvis then return end player:set_properties({ visual_size = needvis and saved[pname].p.visual_size or {x = 0, y = 0}, pointable = needvis and saved[pname].p.pointable or false, makes_footstep_sound = needvis and saved[pname].p.makes_footstep_sound or false }) player:set_nametag_attributes({ color = needvis and saved[pname].a.color or {r = 0, g = 0, b = 0, a = 0} }) minetest.after(0, function() return updatevisible(player) end) end local function grantrevoke(pname) return minetest.after(0, function() local player = minetest.get_player_by_name(pname) if player then return updatevisible(player) end end) end minetest.register_privilege("stealth", { description = "Invisibility", give_to_singleplayer = false, give_to_admin = false, on_grant = grantrevoke, on_revoke = grantrevoke }) local function auto_remove_priv(player, priv) if hasprivs(player, {stealth = true, [priv] = true, privs = true}) then local cc = minetest.registered_chatcommands.revoke if cc and cc.func then local pname = player:get_player_name() minetest.after(0, function() cc.func(pname, pname .. " " .. priv) end) end end end minetest.register_on_joinplayer(function(player) if not keepinteract then auto_remove_priv(player, "interact") end if not keepshout then auto_remove_priv(player, "shout") end return updatevisible(player) end) local timer = 0 minetest.register_globalstep(function(dtime) timer = timer - dtime if timer <= 0 then for _, player in pairs(minetest.get_connected_players()) do if isstealth(player) then updatevisible(player) end end timer = 1 end end) minetest.after(0, function() local oldjoin = minetest.send_join_message function minetest.send_join_message(pname, ...) if not minetest.check_player_privs(pname, "stealth") then return oldjoin(pname, ...) end end local oldleave = minetest.send_leave_message function minetest.send_leave_message(pname, ...) if not minetest.check_player_privs(pname, "stealth") then return oldleave(pname, ...) end end end) local function stripfirstline(msg) local pref, clients, suff = string_match(msg, "^(.*clients[^%w_]+)([^}]*)(.*)$") if not clients then return msg end local clist = {} for pname in string_gmatch(clients, "%S+") do if string_sub(pname, -1) == "," then pname = string_sub(pname, 1, #pname - 1) end if not isstealth(pname) then clist[#clist + 1] = pname end end msg = pref .. table_concat(clist, ", ") .. suff return msg end local function stripstatus(rawmsg, ...) local lines = rawmsg:split("\n") lines[1] = stripfirstline(lines[1]) return table_concat(lines, "\n"), ... end local oldstatus = minetest.get_server_status function minetest.get_server_status(...) return stripstatus(oldstatus(...)) end