From 2428dcadcb1ea74b429963b33b04329faa91790a Mon Sep 17 00:00:00 2001 From: Zughy <4279489-marco_a@users.noreply.gitlab.com> Date: Tue, 15 Sep 2020 21:21:00 +0200 Subject: [PATCH] Files first commit --- DOCS.md | 23 ++++ README.md | 21 ++- api.lua | 137 ++++++++++++++++++ chatcmdbuilder.lua | 306 +++++++++++++++++++++++++++++++++++++++++ commands.lua | 26 ++++ init.lua | 8 ++ locale/template.txt | 18 +++ locale/whitelist.it.tr | 18 +++ mod.conf | 2 + player_manager.lua | 11 ++ 10 files changed, 569 insertions(+), 1 deletion(-) create mode 100644 DOCS.md create mode 100644 api.lua create mode 100644 chatcmdbuilder.lua create mode 100644 commands.lua create mode 100644 init.lua create mode 100644 locale/template.txt create mode 100644 locale/whitelist.it.tr create mode 100644 mod.conf create mode 100644 player_manager.lua diff --git a/DOCS.md b/DOCS.md new file mode 100644 index 0000000..ed85acf --- /dev/null +++ b/DOCS.md @@ -0,0 +1,23 @@ +# Whitelist Docs + +## 1. How it works + +Whitelisted players and whitelist status are both saved into the mod storage, and they're updated every time one of the core functions below succeeds. The end (?) + +## 2. Functions +### 2.1 Core + +> The `sender` field in here is optional. If specified, the sender will receive an output message + +* `whitelist.enable()`: enables the whitelist +* `whitelist.disable()`: disables the whitelist +* `whitelist.add_player(p_name, )`: adds `p_name` to the whitelist +* `whitelist.remove_player(p_name, )`: removes `p_name` from the whitelist + +### 2.2 Utils +* `whitelist.print_list(sender)`: prints a message to `sender` containing all the whitelisted players +* `whitelist.is_player_whitelisted(p_name)`: returns whether `p_name` is whitelisted, as a boolean +* `whitelist.is_whitelist_enabled()`: returns whether the whitelist is enabled, as a boolean + +## 3. About the author +I'm Zughy (Marco), a professional Italian pixel artist who fights for FOSS and digital ethics. If this mod spared you some time and you want to support me somehow, please consider donating on [LiberaPay](https://liberapay.com/Zughy/) diff --git a/README.md b/README.md index 65de61c..11c81da 100644 --- a/README.md +++ b/README.md @@ -1 +1,20 @@ -# Whitelist \ No newline at end of file +# Whitelist +Filter who can enter a Minetest server and who can't through a whitelist + +Support my work + +### Commands +`/whitelist on`: enables the whitelist +`/whitelist off`: disables the whitelist +`/whitelist add `: adds to the whitelist +`/whitelist remove `: removes from the whitelist +`/whitelist who`: lists all the whitelisted players + +### API + + +### Want to help? +Feel free to: +* open an [issue](https://gitlab.com/zughy-friends-minetest/whitelist/-/issues) +* submit a merge request. In this case, PLEASE, do follow milestones and my [coding guidelines](https://cryptpad.fr/pad/#/2/pad/view/-l75iHl3x54py20u2Y5OSAX4iruQBdeQXcO7PGTtGew/embed/). I won't merge features for milestones that are different from the upcoming one (if it's declared), nor messy code +* contact me on the [Minetest Forum](https://forum.minetest.net/memberlist.php?mode=viewprofile&u=26472) diff --git a/api.lua b/api.lua new file mode 100644 index 0000000..60ffa12 --- /dev/null +++ b/api.lua @@ -0,0 +1,137 @@ +whitelist = {} + +local S = minetest.get_translator("whitelist") +local storage = minetest.get_mod_storage() + + + + + +---------------------------------------------- +---------------DICHIARAZIONI------------------ +---------------------------------------------- + +local is_whitelist_on = false +local whitelisted_players = {} + +if storage:get_int("ENABLED") == 1 then + is_whitelist_on = true +end + +if storage:get_string("PLAYERS") ~= "" then + whitelisted_players = minetest.deserialize(storage:get_string("PLAYERS")) +end + + + + + +---------------------------------------------- +--------------------CORE---------------------- +---------------------------------------------- + +function whitelist.enable(sender) + + sender = sender or "" + + -- se è già abilitata + if is_whitelist_on then + minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] The whitelist is already enabled!"))) + return end + + whitelist.add_player(sender) + + is_whitelist_on = true + storage:set_int("ENABLED", 1) + minetest.chat_send_player(sender, "[WHITELIST] " .. S("Whitelist successfully enabled")) +end + + + +function whitelist.disable(sender) + + sender = sender or "" + + -- se è già disabilitata + if not is_whitelist_on then + minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] The whitelist is already disabled!"))) + return end + + is_whitelist_on = false + storage:set_int("ENABLED", 0) + minetest.chat_send_player(sender, "[WHITELIST] " .. S("Whitelist successfully disabled")) +end + + + +function whitelist.add_player(p_name, sender) + + sender = sender or "" + + -- se già c'è + if whitelisted_players[p_name] then + minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] @1 is already whitelisted!", p_name))) + return end + + whitelisted_players[p_name] = true + storage:set_string("PLAYERS", minetest.serialize(whitelisted_players)) + minetest.chat_send_player(sender, "[WHITELIST] " .. minetest.colorize("#c8d692", "+ " .. p_name)) +end + + + +function whitelist.remove_player(p_name, sender) + + sender = sender or "" + + -- se già non c'è + if not whitelisted_players[p_name] then + minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] There is no player whitelisted with that name..."))) + return end + + -- se è lo stesso giocatore + if p_name == sender then + minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] You can't remove yourself!"))) + return end + + whitelisted_players[p_name] = nil + + -- se si rimuove l'ultimo giocatore mentre è attiva + if not next(whitelisted_players) and is_whitelist_on then + minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] Whitelist can't be empty when enabled!"))) + whitelisted_players[p_name] = true + return end + + storage:set_string("PLAYERS", minetest.serialize(whitelisted_players)) + minetest.chat_send_player(sender, "[WHITELIST] " .. minetest.colorize("#f16a54", "- " .. p_name)) +end + + + + + +---------------------------------------------- +--------------------UTILS--------------------- +---------------------------------------------- + +function whitelist.print_list(sender) + local msg = "" + + for p_name, _ in pairs(whitelisted_players) do + msg = msg .. p_name .. ", " + end + + minetest.chat_send_player(sender, "[WHITELIST] " .. minetest.colorize("#eea160", S("Whitelisted players: ")) .. minetest.colorize("#cfc6b8", msg:sub(1, -3))) +end + + + +function whitelist.is_player_whitelisted(p_name) + return whitelisted_players[p_name] ~= nil +end + + + +function whitelist.is_whitelist_enabled() + return is_whitelist_on +end diff --git a/chatcmdbuilder.lua b/chatcmdbuilder.lua new file mode 100644 index 0000000..705b965 --- /dev/null +++ b/chatcmdbuilder.lua @@ -0,0 +1,306 @@ +ChatCmdBuilder = {} + +function ChatCmdBuilder.new(name, func, def) + def = def or {} + local cmd = ChatCmdBuilder.build(func) + cmd.def = def + def.func = cmd.run + minetest.register_chatcommand(name, def) + return cmd +end + +local STATE_READY = 1 +local STATE_PARAM = 2 +local STATE_PARAM_TYPE = 3 +local bad_chars = {} +bad_chars["("] = true +bad_chars[")"] = true +bad_chars["."] = true +bad_chars["%"] = true +bad_chars["+"] = true +bad_chars["-"] = true +bad_chars["*"] = true +bad_chars["?"] = true +bad_chars["["] = true +bad_chars["^"] = true +bad_chars["$"] = true +local function escape(char) + if bad_chars[char] then + return "%" .. char + else + return char + end +end + +local dprint = function() end + +ChatCmdBuilder.types = { + pos = "%(? *(%-?[%d.]+) *, *(%-?[%d.]+) *, *(%-?[%d.]+) *%)?", + text = "(.+)", + number = "(%-?[%d.]+)", + int = "(%-?[%d]+)", + word = "([^ ]+)", + alpha = "([A-Za-z]+)", + modname = "([a-z0-9_]+)", + alphascore = "([A-Za-z_]+)", + alphanumeric = "([A-Za-z0-9]+)", + username = "([A-Za-z0-9-_]+)", +} + +function ChatCmdBuilder.build(func) + local cmd = { + _subs = {} + } + function cmd:sub(route, func, def) + dprint("Parsing " .. route) + + def = def or {} + if string.trim then + route = string.trim(route) + end + + local sub = { + pattern = "^", + params = {}, + func = func + } + + -- End of param reached: add it to the pattern + local param = "" + local param_type = "" + local should_be_eos = false + local function finishParam() + if param ~= "" and param_type ~= "" then + dprint(" - Found param " .. param .. " type " .. param_type) + + local pattern = ChatCmdBuilder.types[param_type] + if not pattern then + error("Unrecognised param_type=" .. param_type) + end + + sub.pattern = sub.pattern .. pattern + + table.insert(sub.params, param_type) + + param = "" + param_type = "" + end + end + + -- Iterate through the route to find params + local state = STATE_READY + local catching_space = false + local match_space = " " -- change to "%s" to also catch tabs and newlines + local catch_space = match_space.."+" + for i = 1, #route do + local c = route:sub(i, i) + if should_be_eos then + error("Should be end of string. Nothing is allowed after a param of type text.") + end + + if state == STATE_READY then + if c == ":" then + dprint(" - Found :, entering param") + state = STATE_PARAM + param_type = "word" + catching_space = false + elseif c:match(match_space) then + print(" - Found space") + if not catching_space then + catching_space = true + sub.pattern = sub.pattern .. catch_space + end + else + catching_space = false + sub.pattern = sub.pattern .. escape(c) + end + elseif state == STATE_PARAM then + if c == ":" then + dprint(" - Found :, entering param type") + state = STATE_PARAM_TYPE + param_type = "" + elseif c:match(match_space) then + print(" - Found whitespace, leaving param") + state = STATE_READY + finishParam() + catching_space = true + sub.pattern = sub.pattern .. catch_space + elseif c:match("%W") then + dprint(" - Found nonalphanum, leaving param") + state = STATE_READY + finishParam() + sub.pattern = sub.pattern .. escape(c) + else + param = param .. c + end + elseif state == STATE_PARAM_TYPE then + if c:match(match_space) then + print(" - Found space, leaving param type") + state = STATE_READY + finishParam() + catching_space = true + sub.pattern = sub.pattern .. catch_space + elseif c:match("%W") then + dprint(" - Found nonalphanum, leaving param type") + state = STATE_READY + finishParam() + sub.pattern = sub.pattern .. escape(c) + else + param_type = param_type .. c + end + end + end + dprint(" - End of route") + finishParam() + sub.pattern = sub.pattern .. "$" + dprint("Pattern: " .. sub.pattern) + + table.insert(self._subs, sub) + end + + if func then + func(cmd) + end + + cmd.run = function(name, param) + for i = 1, #cmd._subs do + local sub = cmd._subs[i] + local res = { string.match(param, sub.pattern) } + if #res > 0 then + local pointer = 1 + local params = { name } + for j = 1, #sub.params do + local param = sub.params[j] + if param == "pos" then + local pos = { + x = tonumber(res[pointer]), + y = tonumber(res[pointer + 1]), + z = tonumber(res[pointer + 2]) + } + table.insert(params, pos) + pointer = pointer + 3 + elseif param == "number" or param == "int" then + table.insert(params, tonumber(res[pointer])) + pointer = pointer + 1 + else + table.insert(params, res[pointer]) + pointer = pointer + 1 + end + end + if table.unpack then + -- lua 5.2 or later + return sub.func(table.unpack(params)) + else + -- lua 5.1 or earlier + return sub.func(unpack(params)) + end + end + end + return false, "Invalid command" + end + + return cmd +end + +local function run_tests() + if not (ChatCmdBuilder.build(function(cmd) + cmd:sub("bar :one and :two:word", function(name, one, two) + if name == "singleplayer" and one == "abc" and two == "def" then + return true + end + end) + end)).run("singleplayer", "bar abc and def") then + error("Test 1 failed") + end + + local move = ChatCmdBuilder.build(function(cmd) + cmd:sub("move :target to :pos:pos", function(name, target, pos) + if name == "singleplayer" and target == "player1" and + pos.x == 0 and pos.y == 1 and pos.z == 2 then + return true + end + end) + end).run + if not move("singleplayer", "move player1 to 0,1,2") then + error("Test 2 failed") + end + if not move("singleplayer", "move player1 to (0,1,2)") then + error("Test 3 failed") + end + if not move("singleplayer", "move player1 to 0, 1,2") then + error("Test 4 failed") + end + if not move("singleplayer", "move player1 to 0 ,1, 2") then + error("Test 5 failed") + end + if not move("singleplayer", "move player1 to 0, 1, 2") then + error("Test 6 failed") + end + if not move("singleplayer", "move player1 to 0 ,1 ,2") then + error("Test 7 failed") + end + if not move("singleplayer", "move player1 to ( 0 ,1 ,2)") then + error("Test 8 failed") + end + if move("singleplayer", "move player1 to abc,def,sdosd") then + error("Test 9 failed") + end + if move("singleplayer", "move player1 to abc def sdosd") then + error("Test 10 failed") + end + + if not (ChatCmdBuilder.build(function(cmd) + cmd:sub("does :one:int plus :two:int equal :three:int", function(name, one, two, three) + if name == "singleplayer" and one + two == three then + return true + end + end) + end)).run("singleplayer", "does 1 plus 2 equal 3") then + error("Test 11 failed") + end + + local checknegint = ChatCmdBuilder.build(function(cmd) + cmd:sub("checknegint :x:int", function(name, x) + return x + end) + end).run + if checknegint("checker","checknegint -2") ~= -2 then + error("Test 12 failed") + end + + local checknegnumber = ChatCmdBuilder.build(function(cmd) + cmd:sub("checknegnumber :x:number", function(name, x) + return x + end) + end).run + if checknegnumber("checker","checknegnumber -3.3") ~= -3.3 then + error("Test 13 failed") + end + + local checknegpos = ChatCmdBuilder.build(function(cmd) + cmd:sub("checknegpos :pos:pos", function(name, pos) + return pos + end) + end).run + local negpos = checknegpos("checker","checknegpos (-13.3,-4.6,-1234.5)") + if negpos.x ~= -13.3 or negpos.y ~= -4.6 or negpos.z ~= -1234.5 then + error("Test 14 failed") + end + + local checktypes = ChatCmdBuilder.build(function(cmd) + cmd:sub("checktypes :int:int :number:number :pos:pos :word:word :text:text", function(name, int, number, pos, word, text) + return int, number, pos.x, pos.y, pos.z, word, text + end) + end).run + local int, number, posx, posy, posz, word, text + int, number, posx, posy, posz, word, text = checktypes("checker","checktypes -1 -2.4 (-3,-5.3,6.12) some text to finish off with") + --dprint(int, number, posx, posy, posz, word, text) + if int ~= -1 or number ~= -2.4 or posx ~= -3 or posy ~= -5.3 or posz ~= 6.12 or word ~= "some" or text ~= "text to finish off with" then + error("Test 15 failed") + end + dprint("All tests passed") + +end +if not minetest then + run_tests() +end diff --git a/commands.lua b/commands.lua new file mode 100644 index 0000000..48b64be --- /dev/null +++ b/commands.lua @@ -0,0 +1,26 @@ +ChatCmdBuilder.new("whitelist", function(cmd) + + cmd:sub("off", function(sender) + whitelist.disable(sender) + end) + + cmd:sub("on", function(sender) + whitelist.enable(sender) + end) + + cmd:sub("add :player:text", function(sender, p_name) + whitelist.add_player(p_name, sender) + end) + + cmd:sub("remove :player:text", function(sender, p_name) + whitelist.remove_player(p_name, sender) + end) + + cmd:sub("who", function(sender) + whitelist.print_list(sender) + end) + +end, { + description = "whitelist commands", + privs = { server = true } +}) diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..c7f4f41 --- /dev/null +++ b/init.lua @@ -0,0 +1,8 @@ +local version = "1.0.0" + +dofile(minetest.get_modpath("whitelist") .. "/api.lua") +dofile(minetest.get_modpath("whitelist") .. "/chatcmdbuilder.lua") +dofile(minetest.get_modpath("whitelist") .. "/commands.lua") +dofile(minetest.get_modpath("whitelist") .. "/player_manager.lua") + +minetest.log("action", "[WHITELIST] Mod initialised, running version " .. version) diff --git a/locale/template.txt b/locale/template.txt new file mode 100644 index 0000000..b2b4f51 --- /dev/null +++ b/locale/template.txt @@ -0,0 +1,18 @@ +# version 1.0.0 +# author(s): +# reviewer(s): +# textdomain: whitelist + +# api.lua +[!] The whitelist is already enabled!= +Whitelist successfully enabled= +[!] The whitelist is already disabled!= +Whitelist successfully disabled= +[!] @1 is already whitelisted!= +[!] There is no player whitelisted with that name...= +[!] You can't remove yourself!= +[!] Whitelist can't be empty when enabled!= +Whitelisted players: = + +#player_manager.lua +You're not whitelisted!= diff --git a/locale/whitelist.it.tr b/locale/whitelist.it.tr new file mode 100644 index 0000000..f0588eb --- /dev/null +++ b/locale/whitelist.it.tr @@ -0,0 +1,18 @@ +# version 1.0.0 +# author(s): Zughy +# reviewer(s): +# textdomain: whitelist + +# api.lua +[!] The whitelist is already enabled!=[!] La whitelist è già abilitata! +Whitelist successfully enabled=Whitelist abilitata con successo +[!] The whitelist is already disabled!=[!] La whitelist è già disabilitata! +Whitelist successfully disabled=Whitelist disabilitata con successo +[!] @1 is already whitelisted!=[!] @1 è già nella whitelist! +[!] There is no player whitelisted with that name...=[!] Nessun giocatore con quel nome è presente nella whitelist... +[!] You can't remove yourself!=[!] Non puoi rimuovere te stesso/a! +[!] Whitelist can't be empty when enabled!=[!] La whitelist non può essere vuota quand'è attiva! +Whitelisted players: =Giocatori in whitelist: + +#player_manager.lua +You're not whitelisted!=Non sei in whitelist! diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..dea3616 --- /dev/null +++ b/mod.conf @@ -0,0 +1,2 @@ +name = whitelist +description = manage who can and who can't enter in your server diff --git a/player_manager.lua b/player_manager.lua new file mode 100644 index 0000000..3fd3011 --- /dev/null +++ b/player_manager.lua @@ -0,0 +1,11 @@ +local S = minetest.get_translator("whitelist") + +minetest.register_on_prejoinplayer(function(name, ip) + + if not whitelist.is_whitelist_enabled() then return end + + if not whitelist.is_player_whitelisted(name) then + return S("You're not whitelisted!") -- this doesn't currently work + end + +end)