diff --git a/mods/cloaking/LICENSE.md b/mods/cloaking/LICENSE.md index 1ba4a63..9491e04 100644 --- a/mods/cloaking/LICENSE.md +++ b/mods/cloaking/LICENSE.md @@ -1,6 +1,6 @@ # The MIT License (MIT) -*Copyright © 2018 by luk3yx* +Copyright © 2019 by luk3yx Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/mods/cloaking/README.md b/mods/cloaking/README.md index aa9a3c5..afb0ce0 100644 --- a/mods/cloaking/README.md +++ b/mods/cloaking/README.md @@ -1,6 +1,6 @@ # Minetest cloaking mod -Allows players to cloak and uncloak. +Allows players to cloak and uncloak, inspired by Star Trek's [cloaking device]. ## What is cloaking? @@ -27,6 +27,9 @@ privileges. - `/cloak [victim]`: Cloaks yourself, alternatively an unsuspecting `victim`. - `/uncloak [victim]`: Uncloaks yourself, or a `victim`. +- `/cloak_chat `: Sends `` to all online players with the + `cloaking` privilege. You can disable cloaked chat by setting + `cloaking.enable_cloaked_chat` to `false` in `minetest.conf`. ## How do I download cloaking? @@ -44,10 +47,16 @@ Cloaking adds the following functions: - `cloaking.cloak_player(player)`: Cloaks a player. - `cloaking.uncloak_player(player)`: Uncloaks a player. - `cloaking.get_cloaked_players()`: Gets a list of cloaked player names. +- `cloaking.get_connected_names()`: Gets a list of cloaked and uncloaked player + names. - `cloaking.is_cloaked(player)`: Checks if a player is cloaked. - `cloaking.on_chat_message(player, message)`: Returns `true` and warns `player` if they are cloaked and trying to send a chat message, otherwise returns `nil`. +- `cloaking.chat`: Cloaked chat API, this is `nil` if cloaked chat is disabled. + - `cloaking.chat.send(message)`: Sends a message to cloaked chat. + - `cloaking.chat.prefix`: The text (`-Cloaked-`) that is prepended to cloaked + chat messages before they are sent to players. It also adds the following functions that ignore cloaked players and can interact with them: @@ -60,3 +69,5 @@ don't want your chatcommand working with cloaked players, you can add `_disallow_while_cloaked = true` to the definition. These modifications do not require that you add `cloaking` to `depends.txt`, as when cloaking is not loaded this parameter is simply ignored. + +[cloaking device]: https://memory-alpha.fandom.com/wiki/Cloaking_device diff --git a/mods/cloaking/chat3.lua b/mods/cloaking/chat3.lua index 569f0f8..248f50c 100644 --- a/mods/cloaking/chat3.lua +++ b/mods/cloaking/chat3.lua @@ -1,14 +1,14 @@ -- -- Minetest cloaking mod: chat3 fixes -- --- © 2018 by luk3yx +-- © 2019 by luk3yx -- -- Override minetest.get_connected_players() so it lists cloaked players for -- chat3. local get_uncloaked_players = minetest.get_connected_players -minetest.get_connected_players = function() +function minetest.get_connected_players() local d = debug.getinfo(2) if d.func == chat3.send or d.func == minetest.chatcommands['me'].func then return cloaking.get_connected_players() @@ -20,7 +20,7 @@ end -- Override get_player_by_name() to allow chat3 to access cloaked players. local get_uncloaked_player_by_name = minetest.get_player_by_name -minetest.get_player_by_name = function(player) +function minetest.get_player_by_name(player) local d = debug.getinfo(2) if d.func == chat3.send or d.func == minetest.chatcommands["me"].func then return cloaking.get_player_by_name(player) diff --git a/mods/cloaking/chatcommands.lua b/mods/cloaking/chatcommands.lua index cda96ad..221d499 100644 --- a/mods/cloaking/chatcommands.lua +++ b/mods/cloaking/chatcommands.lua @@ -1,7 +1,7 @@ -- -- Minetest cloaking mod: chatcommands -- --- © 2018 by luk3yx +-- © 2019 by luk3yx -- minetest.register_privilege('cloaking', @@ -26,6 +26,7 @@ minetest.register_chatcommand("cloak", { return false, victim .. " is already cloaked!" end + minetest.log('action', player .. ' cloaks ' .. victim .. '.') cloaking.cloak(p) return true, "Cloaked!" end @@ -42,14 +43,16 @@ minetest.register_chatcommand("uncloak", { return false, "You don't have permission to uncloak someone else." end + if victim == '*' then + minetest.log('action', player .. ' uncloaks everyone.') for _, player in ipairs(cloaking.get_cloaked_players()) do cloaking.uncloak(player) end return true, "Uncloaked everyone!" end - p = cloaking.get_player_by_name(victim) + local p = cloaking.get_player_by_name(victim) if not p then return false, "Could not find a player with the name '" .. victim .. "'!" end @@ -58,7 +61,27 @@ minetest.register_chatcommand("uncloak", { return false, victim .. " is not cloaked!" end + minetest.log('action', player .. ' uncloaks ' .. victim .. '.') cloaking.uncloak(p) return true, "Uncloaked!" end }) + +-- Allow /teleport to be used on cloaked players if you have the "cloaking" +-- privilege. +local tp = minetest.registered_chatcommands['teleport'].func +minetest.override_chatcommand('teleport', { + func = function(name, param) + if minetest.check_player_privs(name, 'cloaking') then + local g = minetest.get_player_by_name + minetest.get_player_by_name = cloaking.get_player_by_name + + local err, msg = tp(name, param) + + minetest.get_player_by_name = g + return err, msg + else + return tp(name, param) + end + end +}) diff --git a/mods/cloaking/cloaked-chat.lua b/mods/cloaking/cloaked-chat.lua new file mode 100644 index 0000000..55ffc49 --- /dev/null +++ b/mods/cloaking/cloaked-chat.lua @@ -0,0 +1,53 @@ +-- +-- Minetest player cloaking mod: "Cloaked chat" +-- +-- © 2019 by luk3yx +-- + +cloaking.chat = {prefix = '-Cloaked-'} + +-- Send a message to all players with the "cloaking" privilege. +function cloaking.chat.send(msg) + -- Add the "cloaked chat" prefix and remove newlines. + msg = msg:gsub('[\r\n]', ' ') + + -- Log the chat message + minetest.log('action', 'CLOAKED CHAT: ' .. msg) + + -- Send the message to everyone with the "cloaking" priv. + msg = cloaking.chat.prefix .. ' ' .. msg + for _, name in ipairs(cloaking.get_connected_names()) do + if minetest.check_player_privs(name, 'cloaking') then + minetest.chat_send_player(name, msg) + end + end +end + +-- Create a '/cloak_chat' command +minetest.register_chatcommand('cloak_chat', { + params = '', + description = 'Send a chat message to cloaked players.', + privs = {cloaking = true}, + _allow_while_cloaked = true, + + func = function(name, param) + cloaking.chat.send('<' .. name .. '> ' .. param) + end +}) +minetest.registered_chatcommands['cloak-chat'] = + minetest.registered_chatcommands['cloak_chat'] + +-- Override cloaking.on_chat_message +function cloaking.on_chat_message(name, message) + if message:sub(1, 1) ~= "/" and cloaking.is_cloaked(name) then + if minetest.check_player_privs(name, 'cloaking') then + cloaking.chat.send('<' .. name .. '> ' .. message) + else + minetest.chat_send_player(name, "You cannot use chat while" .. + " cloaked. Please use /uncloak if you want to use chat.") + end + return true + end +end + +minetest.registered_on_chat_messages[1] = cloaking.on_chat_message diff --git a/mods/cloaking/core.lua b/mods/cloaking/core.lua index 3bd17ec..64819dc 100644 --- a/mods/cloaking/core.lua +++ b/mods/cloaking/core.lua @@ -1,7 +1,7 @@ -- -- Minetest player cloaking mod: Core functions -- --- © 2018 by luk3yx +-- © 2019 by luk3yx -- cloaking = {} @@ -16,7 +16,7 @@ local cloaked_players = {} local chatcommands_modified = false -- Override built-in functions -minetest.get_player_by_name = function(player) +function minetest.get_player_by_name(player) if cloaked_players[player] then return nil else @@ -24,7 +24,7 @@ minetest.get_player_by_name = function(player) end end -minetest.get_objects_inside_radius = function(pos, radius) +function minetest.get_objects_inside_radius(pos, radius) local objs = {} for _, obj in ipairs(cloaking.get_objects_inside_radius(pos, radius)) do if not cloaked_players[obj:get_player_name()] then @@ -34,7 +34,7 @@ minetest.get_objects_inside_radius = function(pos, radius) return objs end -minetest.get_server_status = function() +function minetest.get_server_status() local status = cloaking.get_server_status() local motd = status:sub(status:find('}', 1, true) + 0) status = status:sub(1, status:find('{', 1, true)) @@ -98,7 +98,8 @@ local override_chatcommands = function() end end -cloaking.on_chat_message = function(name, message) +-- Handle chat messages +function cloaking.on_chat_message(name, message) if message:sub(1, 1) ~= "/" and cloaked_players[name] then minetest.chat_send_player(name, "You cannot use chat while cloaked." .. " Please use /uncloak if you want to use chat.") @@ -106,7 +107,11 @@ cloaking.on_chat_message = function(name, message) end end -minetest.register_on_chat_message(cloaking.on_chat_message) +table.insert(minetest.registered_on_chat_messages, 1, cloaking.on_chat_message) +minetest.callback_origins[cloaking.on_chat_message] = { + mod = 'cloaking', + name = 'on_chat_message' +} -- Disallow some built-in commands. for _, cmd in ipairs({'me', 'msg'}) do @@ -118,7 +123,8 @@ for _, cmd in ipairs({'me', 'msg'}) do end -- The cloak and uncloak functions -cloaking.cloak = function(player) +local use_areas = minetest.get_modpath('areas') and areas and areas.hud +function cloaking.cloak(player) if not chatcommands_modified then override_chatcommands() end if type(player) == "string" then player = cloaking.get_player_by_name(player) @@ -133,7 +139,7 @@ cloaking.cloak = function(player) player:set_nametag_attributes({text = " "}) local t = nil - if areas and areas.hud and areas.hud[victim] then + if use_areas and areas.hud[victim] then t = areas.hud[victim] end @@ -145,14 +151,24 @@ cloaking.cloak = function(player) cloaked_players[victim] = true + -- TODO: Get the highest ID somehow + local t_id = t and t.areasId + for id = 0, 20 do + if id ~= t_id and player:hud_get(id) then + player:hud_remove(id) + end + end + if t then areas.hud[victim] = t player:hud_change(areas.hud[victim].areasId, "text", "Cloaked") areas.hud[victim].oldAreas = "" end + + minetest.log('verbose', victim .. ' was cloaked.') end -cloaking.uncloak = function(player) +function cloaking.uncloak(player) if type(player) == "string" then player = cloaking.get_player_by_name(player) end @@ -176,10 +192,12 @@ cloaking.uncloak = function(player) for _, f in ipairs(minetest.registered_on_joinplayers) do f(player) end + + minetest.log('verbose', victim .. ' was uncloaked.') end -- API functions -cloaking.auto_uncloak = function(player) +function cloaking.auto_uncloak(player) if type(player) ~= "string" then player = player:get_player_name() end @@ -188,7 +206,7 @@ cloaking.auto_uncloak = function(player) end end -cloaking.delayed_uncloak = function(player) +function cloaking.delayed_uncloak(player) local victim = player:get_player_name() if cloaked_players[victim] then minetest.after(0.5, function() @@ -211,7 +229,7 @@ minetest.callback_origins[cloaking.delayed_uncloak] = { -- The cloaking mod is so good it fools the built-in get_connected_players, so -- overlay that with one that adds cloaked players in. -cloaking.get_connected_players = function() +function cloaking.get_connected_players() local a = minetest.get_connected_players() for player, cloaked in pairs(cloaked_players) do if cloaked then @@ -221,7 +239,7 @@ cloaking.get_connected_players = function() return a end -cloaking.get_cloaked_players = function() +function cloaking.get_cloaked_players() local players = {} for player, cloaked in pairs(cloaked_players) do if cloaked then @@ -231,7 +249,27 @@ cloaking.get_cloaked_players = function() return players end -cloaking.is_cloaked = function(player) +-- Allow mods to get a list of cloaked and uncloaked player names. +function cloaking.get_connected_names() + local a = cloaking.get_cloaked_players() + for _, player in ipairs(minetest.get_connected_players()) do + table.insert(a, player:get_player_name()) + end + return a +end + +function cloaking.is_cloaked(player) if type(player) ~= "string" then player = player:get_player_name() end return cloaked_players[player] and true or false end + +-- Prevent cloaked players dying +minetest.register_on_player_hpchange(function(player, hp_change) + if player and hp_change < 0 then + local name = player:get_player_name() + if cloaked_players[name] then + hp_change = 0 + end + end + return hp_change +end, true) diff --git a/mods/cloaking/depends.txt b/mods/cloaking/depends.txt index 62d0245..eddcf70 100644 --- a/mods/cloaking/depends.txt +++ b/mods/cloaking/depends.txt @@ -1,2 +1,3 @@ +areas? chat3? irc? diff --git a/mods/cloaking/init.lua b/mods/cloaking/init.lua index ebad558..d2d4b35 100644 --- a/mods/cloaking/init.lua +++ b/mods/cloaking/init.lua @@ -1,7 +1,7 @@ -- -- Minetest player cloaking mod -- --- © 2018 by luk3yx +-- © 2019 by luk3yx -- local path = minetest.get_modpath('cloaking') dofile(path .. '/core.lua') @@ -14,3 +14,37 @@ end if minetest.get_modpath('irc') then dofile(path .. '/irc.lua') end + +-- Attempt to support older versions of Minetest +local cloaked_chat = 'cloaking.enable_cloaked_chat' +if minetest.settings and minetest.settings.get_bool then + cloaked_chat = minetest.settings:get_bool(cloaked_chat) +else + cloaked_chat = minetest.setting_getbool(cloaked_chat) +end + +-- Load cloaked chat if enabled +if cloaked_chat or cloaked_chat == nil then + dofile(path .. '/cloaked-chat.lua') +end + +-- Enforce security of logs +table.insert(minetest.registered_on_chat_messages, 1, function(name, msg) + if msg:find('[\r\n]') then + minetest.chat_send_player(name, + 'You cannot use newlines in chat messages.') + return true + end +end) + +local log = minetest.log +function minetest.log(level, text) + level = level:gsub('[\r\n]', ' ') + if text then + text = text:gsub('[\r\n]', ' ') + else + text = level + level = 'none' + end + return log(level, text) +end diff --git a/mods/cloaking/irc.lua b/mods/cloaking/irc.lua index 9f5dd2e..0378fca 100644 --- a/mods/cloaking/irc.lua +++ b/mods/cloaking/irc.lua @@ -1,12 +1,12 @@ -- -- Minetest cloaking mod: IRC fixes -- --- © 2018 by luk3yx +-- © 2019 by luk3yx -- local irc_sendLocal = irc.sendLocal -irc.sendLocal = function(msg) +function irc.sendLocal(msg) for _, player in ipairs(cloaking.get_cloaked_players()) do minetest.chat_send_player(player, msg) end diff --git a/mods/cloaking/mod.conf b/mods/cloaking/mod.conf new file mode 100644 index 0000000..4e73b91 --- /dev/null +++ b/mods/cloaking/mod.conf @@ -0,0 +1,3 @@ +name = cloaking +description = Allows admins to become invisible to both mods and other players. +optional_depends = areas,chat3,irc diff --git a/mods/cloaking/settingtypes.txt b/mods/cloaking/settingtypes.txt new file mode 100644 index 0000000..da92f73 --- /dev/null +++ b/mods/cloaking/settingtypes.txt @@ -0,0 +1,3 @@ +# Cloaked chat - Allow cloaked players with the "cloaking" priv to use a special +# chat channel. +cloaking.enable_cloaked_chat (Cloaked chat) bool true