From c629b298f9d7888ad390360eccf3f065138807ce Mon Sep 17 00:00:00 2001 From: Luke aka SwissalpS <161979+SwissalpS@users.noreply.github.com> Date: Mon, 14 Oct 2024 04:24:58 +0200 Subject: [PATCH] general overhaul for 5.0.0 (#11) * general overhaul for 5.0.0 - overhauled English language - overhauled French language - adds Spanish locale - adds German locale - adds mtt integration testing - adds github workflow for mtt - refactores handle_command - standardizes usage of table.getn() (nothing crucial in this mod) - standardizes variables to have same format and same name for same purpouse - DB-fix also adds missing owners to members - fixes some situations where admins couldn't do things - fixes display of dynamic admin priv and registration - alows uppercase for subcommands - adds valid_password() for cleaner checks - ensures that existing factions don't get overwritten * provide alternative to table.pack() * add disband hooks support * remove local f == factions --- .github/workflows/test.yml | 15 + .luacheckrc | 4 +- init.lua | 854 +++++++++++++++++++----------------- locale/playerfactions.de.tr | 79 ++++ locale/playerfactions.es.tr | 79 ++++ locale/playerfactions.fr.tr | 77 ++-- locale/template.txt | 55 ++- mod.conf | 1 + mtt.lua | 512 +++++++++++++++++++++ 9 files changed, 1207 insertions(+), 469 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 locale/playerfactions.de.tr create mode 100644 locale/playerfactions.es.tr create mode 100644 mtt.lua diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e0f68c4 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,15 @@ +name: test + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: buckaroobanzay/mtt@main + with: + modname: playerfactions + git_dependencies: | + https://github.com/OgelGames/fakelib.git diff --git a/.luacheckrc b/.luacheckrc index 0e33760..4e85a8f 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -4,5 +4,7 @@ globals = { read_globals = { "minetest", - "table" + "dump", "dump2", + "table", + "mtt", } diff --git a/init.lua b/init.lua index 092ef61..31a318c 100644 --- a/init.lua +++ b/init.lua @@ -1,36 +1,48 @@ -- Translation support local S = minetest.get_translator("playerfactions") --- Data +-- Global factions table factions = {} --- This variable "version" can be used by other mods to check the compatibility of this mod +-- This variable "version" can be used by other mods to check +-- the compatibility of this mod factions.version = 2 +-- Settings +factions.mode_unique_faction = minetest.settings:get_bool( + "player_factions.mode_unique_faction", true) +factions.max_members_list = tonumber(minetest.settings:get( + "player_factions.max_members_list")) or 50 +factions.priv = minetest.settings:get( + "player_factions.priv_admin") or "playerfactions_admin" + +-- Privilege registration (if needed) minetest.register_on_mods_loaded(function() if not minetest.registered_privileges[factions.priv] then - minetest.register_privilege("playerfactions_admin", { + minetest.register_privilege(factions.priv, { description = S("Allow the use of all playerfactions commands"), give_to_singleplayer = false }) end end) +-- Hooks +local disband_hooks = {} +-- When a faction is disbanded, these callbacks are called +-- hook(faction_name) +function factions.register_disband_hook(hook) + if 'function' ~= hook then + return false + end + table.insert(disband_hooks, hook) + return true +end + +-- Data local facts = {} local storage = minetest.get_mod_storage() if storage:get_string("facts") ~= "" then facts = minetest.deserialize(storage:get_string("facts")) end --- Fix factions -for _, fact in pairs(facts) do - if fact.members == nil then - fact.members = {} - end -end - -factions.mode_unique_faction = minetest.settings:get_bool("player_factions.mode_unique_faction", true) -factions.max_members_list = tonumber(minetest.settings:get("player_factions.max_members_list")) or 50 -factions.priv = minetest.settings:get("player_factions.priv_admin") or "playerfactions_admin" - local function save_factions() storage:set_string("facts", minetest.serialize(facts)) @@ -39,7 +51,7 @@ end local function table_copy(data) local copy = {} if type(data) == "table" then - for k,v in pairs(data) do + for k, v in pairs(data) do copy[k]=table_copy(v) end return copy @@ -48,498 +60,538 @@ local function table_copy(data) end end - --- Data manipulation +-- Data manipulation (API) function factions.get_facts() return table_copy(facts) end -function factions.player_is_in_faction(fname, player_name) - if not minetest.player_exists(player_name) or facts[fname] == nil then +function factions.player_is_in_faction(faction_name, player_name) + if not (facts[faction_name] and minetest.player_exists(player_name)) then return false end - return facts[fname].members[player_name] + return facts[faction_name].members[player_name] end -function factions.get_player_faction(name) - if not minetest.player_exists(name) then +function factions.get_player_faction(player_name) + minetest.log("warning", "Function factions.get_player_faction() " + .. "is deprecated in favor of factions.get_player_factions(). " + .. "Please check updates of mods depending on playerfactions.") + if not minetest.player_exists(player_name) then return false end - minetest.log("warning", "Function factions.get_player_faction() is deprecated in favor of " .. - "factions.get_player_factions(). Please check updates of mods depending on playerfactions.") - for fname, fact in pairs(facts) do - if fact.members[name] then - return fname + for faction_name, fact in pairs(facts) do + if fact.members[player_name] then + return faction_name end end return nil end -function factions.get_player_factions(name) - if not minetest.player_exists(name) then +function factions.get_player_factions(player_name) + if not minetest.player_exists(player_name) then return false end - local player_factions = nil - for fname, fact in pairs(facts) do - if fact.members[name] then - if not player_factions then - player_factions = {} - end - table.insert(player_factions, fname) + local player_factions = {} + for faction_name, fact in pairs(facts) do + if fact.members[player_name] then + table.insert(player_factions, faction_name) end end - return player_factions + return 0 < table.getn(player_factions) and player_factions or false end -function factions.get_owned_factions(name) - local own_factions = nil - for fname, fact in pairs(facts) do - if fact.owner == name then - if not own_factions then - own_factions = {} - end - table.insert(own_factions, fname) +function factions.get_owned_factions(player_name) + local owned_factions = {} + for faction_name, fact in pairs(facts) do + if fact.owner == player_name then + table.insert(owned_factions, faction_name) end end - return own_factions + return 0 < table.getn(owned_factions) and owned_factions or false end -function factions.get_administered_factions(name) - local adm_factions = nil - for fname, fact in pairs(facts) do - if minetest.get_player_privs(name)[factions.priv] or fact.owner == name then - if not adm_factions then - adm_factions = {} - end - table.insert(adm_factions, fname) +function factions.get_administered_factions(player_name) + local is_admin = minetest.get_player_privs(player_name)[factions.priv] + local adm_factions = {} + for faction_name, fact in pairs(facts) do + if is_admin or fact.owner == player_name then + table.insert(adm_factions, faction_name) end end - return adm_factions + return 0 < table.getn(adm_factions) and adm_factions or false end -function factions.get_owner(fname) - if facts[fname] == nil then +function factions.get_members(faction_name) + if not facts[faction_name] then return false end - return facts[fname].owner + local faction_members = {} + for player_name in pairs(facts[faction_name].members) do + table.insert(faction_members, player_name) + end + return 0 < table.getn(faction_members) and faction_members or false end -function factions.chown(fname, owner) - if facts[fname] == nil then +function factions.get_owner(faction_name) + if not facts[faction_name] then return false end - facts[fname].owner = owner + return facts[faction_name].owner +end + +function factions.chown(faction_name, player_name) + if not facts[faction_name] then + return false + end + facts[faction_name].owner = player_name save_factions() return true end -function factions.register_faction(fname, founder, pw) - if facts[fname] ~= nil then +function factions.register_faction(faction_name, player_name, password) + if not ('string' == type(faction_name) and 'string' == type(player_name) + and 'string' == type(password)) then + return false + end + if facts[faction_name] then return false end - facts[fname] = { - name = fname, - owner = founder, - password = pw, - members = {[founder] = true} + facts[faction_name] = { + name = faction_name, + owner = player_name, + password = password, + members = { [player_name] = true } } save_factions() return true end -function factions.disband_faction(fname) - if facts[fname] == nil then +function factions.disband_faction(faction_name) + if not facts[faction_name] then return false end - facts[fname] = nil + facts[faction_name] = nil save_factions() + for _, hook in ipairs(disband_hooks) do + hook(faction_name) + end return true end -function factions.get_password(fname) - if facts[fname] == nil then +function factions.valid_password(faction_name, password) + if not facts[faction_name] or not password then return false end - return facts[fname].password + return password == facts[faction_name].password end -function factions.set_password(fname, password) - if facts[fname] == nil then +function factions.get_password(faction_name) + if not facts[faction_name] then return false end - facts[fname].password = password + return facts[faction_name].password +end + +function factions.set_password(faction_name, password) + if not (facts[faction_name] and 'string' == type(password)) then + return false + end + facts[faction_name].password = password save_factions() return true end -function factions.join_faction(fname, player) - if facts[fname] == nil or not minetest.player_exists(player) then +function factions.join_faction(faction_name, player_name) + if not (facts[faction_name] and 'string' == type(player_name) + and minetest.player_exists(player_name)) then return false end - facts[fname].members[player] = true + facts[faction_name].members[player_name] = true save_factions() return true end -function factions.leave_faction(fname, player_name) - if facts[fname] == nil or not minetest.player_exists(player_name) then +function factions.leave_faction(faction_name, player_name) + if not (facts[faction_name] and 'string' == type(player_name) + and minetest.player_exists(player_name)) then return false end - facts[fname].members[player_name] = nil + facts[faction_name].members[player_name] = nil save_factions() return true end -- Chat commands -local function handle_command(name, param) - local params = {} - for p in string.gmatch(param, "[^%s]+") do - table.insert(params, p) +local cc = {} + +function cc.create(player_name, params) + local faction_name = params[2] + local password = params[3] + if not faction_name then + return false, S("Missing faction name.") + elseif not password then + return false, S("Missing password.") + elseif factions.mode_unique_faction and factions.get_player_factions(player_name) then + return false, S("You are already in a faction.") + elseif facts[faction_name] then + return false, S("Faction @1 already exists.", faction_name) + else + factions.register_faction(faction_name, player_name, password) + return true, S("Registered @1.", faction_name) end - if params == nil then - return false, S("Unknown subcommand. Run '/help factions' for help.") +end + +function cc.disband(player_name, params, not_admin) + local password = params[2] + if not password then + return false, S("Missing password.") end - local action = params[1] - if action == "create" then - local faction_name = params[2] - local password = params[3] - if factions.mode_unique_faction and factions.get_player_faction(name) ~= nil then - return false, S("You are already in a faction.") - elseif faction_name == nil then - return false, S("Missing faction name.") - elseif password == nil then - return false, S("Missing password.") - elseif facts[faction_name] ~= nil then - return false, S("That faction already exists.") + local faction_name = params[3] + local owned_factions = factions.get_administered_factions(player_name) + local number_factions = owned_factions and table.getn(owned_factions) or 0 + if not_admin and number_factions == 0 then + return false, S("You don't own any factions.") + elseif not faction_name and number_factions == 1 then + faction_name = owned_factions[1] + elseif not faction_name then + return false, S( + "You are the owner of multiple factions, you have to choose one of them: @1.", + table.concat(owned_factions, ", ")) + end + if not facts[faction_name] then + return false, S("Faction @1 doesn't exist.", faction_name) + elseif not_admin and player_name ~= factions.get_owner(faction_name) then + return false, S("Permission denied: You are not the owner of that faction," + .. " and don't have the @1 privilege.", factions.priv) + elseif not_admin and not factions.valid_password(faction_name, password) then + return false, S("Permission denied: Wrong password.") + else + factions.disband_faction(faction_name) + return true, S("Disbanded @1.", faction_name) + end +end + +function cc.list() + local faction_list = {} + for k in pairs(facts) do + table.insert(faction_list, k) + end + if table.getn(faction_list) == 0 then + return true, S("There are no factions yet.") + else + return true, S("Factions (@1): @2.", + table.getn(faction_list), table.concat(faction_list, ", ")) + end +end + +function cc.info(player_name, params, not_admin) + local faction_name = params[2] + if not faction_name then + local player_factions = factions.get_player_factions(player_name) + if not player_factions then + return true, S("No factions found.") + elseif table.getn(player_factions) == 1 then + faction_name = player_factions[1] else - factions.register_faction(faction_name, name, password) - return true, S("Registered @1.", faction_name) - end - elseif action == "disband" then - local password = nil - local faction_name = nil - local own_factions = factions.get_administered_factions(name) - local number_factions = own_factions and #own_factions or 0 - if number_factions == 0 then - return false, S("You are the owner of no faction.") - elseif #params == 1 then - return false, S("Missing password.") - elseif #params == 2 and number_factions == 1 then - password = params[2] - faction_name = own_factions[1] - elseif #params >= 3 then - faction_name = params[3] - password = params[2] - end - if password == nil then - return false, S("Missing password.") - elseif faction_name == nil then return false, S( - "You are the owner of many factions, you have to choose one of them: @1.", - table.concat(own_factions, ", ") - ) - elseif not facts[faction_name] then - return false, S("This faction doesn't exists.") - elseif name ~= factions.get_owner(faction_name) and not minetest.get_player_privs(name)[factions.priv] then - return false, S("Permission denied: You are not the owner of this faction, " .. - "and don't have the playerfactions_admin privilege.") - elseif password ~= factions.get_password(faction_name) then - return false, S("Permission denied: Wrong password.") - else - factions.disband_faction(faction_name) - return true, S("Disbanded @1.", faction_name) + "You are in multiple factions, you have to choose one of them: @1.", + table.concat(player_factions, ", ")) end - elseif action == "list" then - local faction_list = {} - for k in pairs(facts) do - table.insert(faction_list, k) - end - if #faction_list ~= 0 then - return true, S("Factions (@1): @2.", #faction_list, table.concat(faction_list, ", ")) - else - return true, S("There are no factions yet.") + end + if not facts[faction_name] then + return false, S("Faction @1 doesn't exist.", faction_name) + else + local faction_members = factions.get_members(faction_name) + if table.getn(faction_members) > factions.max_members_list then + faction_members = { S("The faction has more than @1 members," + .. " the members list can't be shown.", factions.max_members_list) } end - elseif action == "info" then - local faction_name = params[2] - if faction_name == nil then - local player_factions = factions.get_player_factions(name) - if not player_factions then - return true, S("no faction found") - elseif #player_factions == 1 then - faction_name = player_factions[1] - else - return false, S( - "You are in many factions, you have to choose one of them: @1.", - table.concat(player_factions, ", ") - ) - end + local summary = S("Name: @1\nOwner: @2\nMembers: @3", + faction_name, factions.get_owner(faction_name), + table.concat(faction_members, ", ")) + if not not_admin or factions.get_owner(faction_name) == player_name then + summary = summary .. "\n" + .. S("Password: @1", factions.get_password(faction_name)) end - if facts[faction_name] == nil then - return false, S("This faction doesn't exists.") + return true, summary + end +end + +function cc.player_info(player_name, params) + player_name = params[2] or player_name + if not player_name or "" == player_name then + return false, S("Missing player name.") + end + local player_factions = factions.get_player_factions(player_name) + if not player_factions then + return false, S( + "Player @1 doesn't exist or isn't in any faction.", player_name) + else + local summary = S("@1 is in the following factions: @2.", + player_name, table.concat(player_factions, ", ")) + local owned_factions = factions.get_owned_factions(player_name) + if owned_factions then + summary = summary .. "\n" .. S( + "@1 is the owner of the following factions: @2.", + player_name, table.concat(owned_factions, ", ")) else - local fmembers = "" - if table.getn(facts[faction_name].members) > factions.max_members_list then - fmembers = S("The faction has more than @1 members, the members list can't be shown.", factions.max_members_list) - else - for play,_ in pairs(facts[faction_name].members) do - if fmembers == "" then - fmembers = play - else - fmembers = fmembers..", "..play - end - end - end - local summary = S("Name: @1\nOwner: @2\nMembers: @3", faction_name, factions.get_owner(faction_name), fmembers) - if factions.get_owner(faction_name) == name or minetest.get_player_privs(name)[factions.priv] then - summary = summary .. "\n" .. S("Password: @1", factions.get_password(faction_name)) - end - return true, summary + summary = summary .. "\n" .. S( + "@1 doesn't own any factions.", player_name) end - elseif action == "player_info" then - local player_name = params[2] - if not player_name then - return false, S("The player name is nil or empty.") + if minetest.get_player_privs(player_name)[factions.priv] then + summary = summary .. "\n" .. S( + "@1 has the @2 privilege so they can admin every faction.", + player_name, factions.priv) end - local player_factions = factions.get_player_factions(player_name) - if not player_factions then - return false, S("This player doesn't exists or is in no faction") + return true, summary + end +end + +function cc.join(player_name, params) + local faction_name = params[2] + local password = params[3] + if factions.mode_unique_faction and factions.get_player_factions(player_name) then + return false, S("You are already in a faction.") + elseif not faction_name then + return false, S("Missing faction name.") + elseif not facts[faction_name] then + return false, S("Faction @1 doesn't exist.", faction_name) + elseif facts[faction_name].members[player_name] then + return false, S("You are already in faction @1.", faction_name) + elseif not factions.valid_password(faction_name, password) then + return false, S("Permission denied: Wrong password.") + else + if factions.join_faction(faction_name, player_name) then + return true, S("Joined @1.", faction_name) else - local str_owner = "" - local str_member = "" - for _,v in ipairs(player_factions) do - if str_member == "" then - str_member = str_member..v - else - str_member = str_member..", "..v - end - end - local summary = S("@1 is in the following factions: @2.", player_name, str_member) - local owned_factions = factions.get_owned_factions(player_name) - if not owned_factions then - summary = summary.. "\n" .. S("This player is the owner of no faction.") - else - for _,v in ipairs(owned_factions) do - if str_owner == "" then - str_owner = str_owner..v - else - str_owner = str_owner..", "..v - end - end - summary = summary .. "\n" .. S("This player is the owner of the following factions: @1.", str_owner) - end - if minetest.get_player_privs(player_name)[factions.priv] then - summary = summary .. "\n" .. S( - "@1 has the playerfactions_admin privilege so they can admin every faction.", - player_name - ) - end - return true, summary + return false, S("Error joining faction.") end - elseif action == "join" then - local faction_name = params[2] - local password = params[3] - if factions.get_player_faction(name) ~= nil and factions.mode_unique_faction then - return false, S("You are already in a faction.") - elseif not faction_name then - return false, S("Missing faction name.") - elseif facts[faction_name] == nil then - return false, S("The faction @1 doesn't exist.", faction_name) - elseif factions.get_password(faction_name) ~= password then - return false, S("Permission denied: Wrong password.") + end +end + +function cc.leave(player_name, params) + local player_factions = factions.get_player_factions(player_name) + local number_factions = player_factions and table.getn(player_factions) or 0 + local faction_name = params[2] + if number_factions == 0 then + return false, S("You are not in a faction.") + elseif not faction_name then + if number_factions == 1 then + faction_name = player_factions[1] else - if factions.join_faction(faction_name, name) then - return true, S("Joined @1.", faction_name) - else - return false, S("Error on joining.") - end - end - elseif action == "leave" then - local player_factions = factions.get_player_factions(name) - local number_factions = player_factions and table.getn(player_factions) or 0 - local faction_name = nil - if number_factions == 0 then - return false, S("You are not in a faction.") - elseif #params == 1 then - if number_factions == 1 then - faction_name = player_factions[1] - else - return false, S( - "You are in many factions, you have to choose one of them: @1.", - table.concat(player_factions, ", ") - ) - end - elseif #params >= 1 and facts[params[2]] ~= nil then - faction_name = params[2] + return false, S( + "You are in multiple factions, you have to choose one of them: @1.", + table.concat(player_factions, ", ")) end - if faction_name == nil then - return false, S("The given faction doesn't exists.") - elseif factions.get_owner(faction_name) == name then - return false, S("You cannot leave your own faction, change owner or disband it.") + end + if not facts[faction_name] then + return false, S("Faction @1 doesn't exist.", faction_name) + elseif factions.get_owner(faction_name) == player_name then + return false, S("You cannot leave your own faction, change owner or disband it.") + elseif not facts[faction_name].members[player_name] then + return false, S("You aren't part of faction @1.", faction_name) + else + if factions.leave_faction(faction_name, player_name) then + return true, S("Left @1.", faction_name) else - if factions.leave_faction(faction_name, name) then - return true, S("Left @1.", faction_name) - else - return false, S("Error on leaving faction.") - end - end - elseif action == "kick" then - local target = nil - local faction_name = nil - local own_factions = factions.get_administered_factions(name) - local number_factions = own_factions and table.getn(own_factions) or 0 - if number_factions == 0 then - return false, S("You are the owner of no faction, you can't use this command.") - elseif #params == 2 and number_factions == 1 then - target = params[2] - faction_name = own_factions[1] - elseif #params >= 3 then - faction_name = params[3] - target = params[2] + return false, S("Error leaving faction.") end - if faction_name == nil then - return false, S( - "You are the owner of many factions, you have to choose one of them: @1.", - table.concat(own_factions, ", ") - ) - elseif target == nil then - return false, S("Missing player name.") - elseif factions.get_owner(faction_name) ~= name and not minetest.get_player_privs(name)[factions.priv] then - return false, S("Permission denied: You are not the owner of this faction, " .. - "and don't have the playerfactions_admin privilege.") - elseif not facts[faction_name].members[target] then - return false, S("This player is not in the specified faction.") - elseif target == factions.get_owner(faction_name) then - return false, S("You cannot kick the owner of a faction, " .. - "use '/factions chown [faction]' to change the ownership.") + end +end + +function cc.kick(player_name, params, not_admin) + local target_name = params[2] + if not target_name then + return false, S("Missing player name.") + end + local faction_name = params[3] + local owned_factions = factions.get_administered_factions(player_name) + local number_factions = owned_factions and table.getn(owned_factions) or 0 + if number_factions == 0 then + return false, S("You don't own any factions, you can't use this command.") + elseif not faction_name and number_factions == 1 then + faction_name = owned_factions[1] + elseif not faction_name then + return false, S( + "You are the owner of multiple factions, you have to choose one of them: @1.", + table.concat(owned_factions, ", ")) + end + if not_admin and factions.get_owner(faction_name) ~= player_name then + return false, S("Permission denied: You are not the owner of that faction, " + .. "and don't have the @1 privilege.", factions.priv) + elseif not facts[faction_name].members[target_name] then + return false, S("@1 is not in the specified faction.", target_name) + elseif target_name == factions.get_owner(faction_name) then + return false, S("You cannot kick the owner of a faction, " + .. "use '/factions chown []' " + .. "to change the ownership.") + else + if factions.leave_faction(faction_name, target_name) then + return true, S("Kicked @1 from faction.", target_name) else - if factions.leave_faction(faction_name, target) then - return true, S("Kicked @1 from faction.", target) - else - return false, S("Error kicking @1 from faction.", target) - end + -- SwissalpS is quite positive that this portion is unreachable + return false, S("Error kicking @1 from faction.", target_name) end - elseif action == "passwd" then - local password = nil - local faction_name = nil - local own_factions = factions.get_administered_factions(name) - local number_factions = own_factions and table.getn(own_factions) or 0 - if #params == 1 then - return false, S("Missing password.") - elseif number_factions == 0 then - return false, S("You are the owner of no faction, you can't use this command.") - elseif #params == 2 and number_factions == 1 then - password = params[2] - faction_name = own_factions[1] - elseif #params >= 3 then - faction_name = params[3] - password = params[2] - end - if faction_name == nil then - return false, S( - "You are the owner of many factions, you have to choose one of them: @1.", - table.concat(own_factions, ", ") - ) - elseif password == nil then - return false, S("Missing password.") - elseif factions.get_owner(faction_name) ~= name and not minetest.get_player_privs(name)[factions.priv] then - return false, S("Permission denied: You are not the owner of this faction, " .. - "and don't have the playerfactions_admin privilege.") + end +end + +function cc.passwd(player_name, params, not_admin) + local password = params[2] + if not password then + return false, S("Missing password.") + end + local faction_name = params[3] + local owned_factions = factions.get_administered_factions(player_name) + local number_factions = owned_factions and table.getn(owned_factions) or 0 + if number_factions == 0 then + return false, S("You don't own any factions, you can't use this command.") + elseif not faction_name and number_factions == 1 then + faction_name = owned_factions[1] + elseif not faction_name then + return false, S( + "You are the owner of multiple factions, you have to choose one of them: @1.", + table.concat(owned_factions, ", ")) + end + if not_admin and factions.get_owner(faction_name) ~= player_name then + return false, S("Permission denied: You are not the owner of that faction, " + .. "and don't have the @1 privilege.", factions.priv) + else + if factions.set_password(faction_name, password) then + return true, S("Password has been updated.") else - if factions.set_password(faction_name, password) then - return true, S("Password has been updated.") - else - return false, S("Failed to change password.") - end + return false, S("Failed to change password.") end - elseif action == "chown" then - local own_factions = factions.get_administered_factions(name) - local number_factions = own_factions and table.getn(own_factions) or 0 - local faction_name = nil - local target = nil - local password = nil - if #params < 3 then - if params[2] ~= nil and minetest.player_exists(params[2]) then - return false, S("Missing password.") - else - return false, S("Missing player name.") - end - elseif number_factions == 0 then - return false, S("You are the owner of no faction, you can't use this command.") - elseif number_factions == 1 and #params == 3 then - faction_name = own_factions[1] - target = params[2] - password = params[3] - elseif #params >= 4 then - faction_name = params[4] - target = params[2] - password = params[3] - end - if faction_name == nil then - return false, S( - "You are the owner of many factions, you have to choose one of them: @1.", - table.concat(own_factions, ", ") - ) - elseif target == nil then - return false, S("Missing player name.") - elseif password == nil then - return false, S("Missing password.") - elseif name ~= factions.get_owner(faction_name) and not minetest.get_player_privs(name)[factions.priv] then - return false, S("Permission denied: You are not the owner of this faction, " .. - "and don't have the playerfactions_admin privilege.") - elseif not facts[faction_name].members[target] then - return false, S("@1 isn't in your faction.", target) - elseif password ~= factions.get_password(faction_name) then - return false, S("Permission denied: Wrong password.") + end +end + +function cc.chown(player_name, params, not_admin) + local target_name = params[2] + local password = params[3] + local faction_name = params[4] + if not target_name then + return false, S("Missing player name.") + elseif not password then + return false, S("Missing password.") + end + local owned_factions = factions.get_administered_factions(player_name) + local number_factions = owned_factions and table.getn(owned_factions) or 0 + if number_factions == 0 then + return false, S("You don't own any factions, you can't use this command.") + elseif not faction_name and number_factions == 1 then + faction_name = owned_factions[1] + elseif not faction_name then + return false, S( + "You are the owner of multiple factions, you have to choose one of them: @1.", + table.concat(owned_factions, ", ")) + end + if not_admin and player_name ~= factions.get_owner(faction_name) then + return false, S("Permission denied: You are not the owner of that faction, " + .. "and don't have the @1 privilege.", factions.priv) + elseif not facts[faction_name].members[target_name] then + return false, S("@1 isn't in faction @2.", target_name, faction_name) + elseif not_admin and not factions.valid_password(faction_name, password) then + return false, S("Permission denied: Wrong password.") + else + if factions.chown(faction_name, target_name) then + return true, S("Ownership has been transferred to @1.", target_name) else - if factions.chown(faction_name, target) then - return true, S("Ownership has been transferred to @1.", target) - else - return false, S("Failed to transfer ownership.") - end + return false, S("Failed to transfer ownership.") end - elseif action == "invite" then - if not minetest.get_player_privs(name)[factions.priv] then - return false, S("Permission denied: You can't use this command, playerfactions_admin priv is needed.") + end +end + +function cc.invite(_, params, not_admin) + if not_admin then + return false, S( + "Permission denied: You can't use this command, @1 priv is needed.", + factions.priv) + end + local target_name = params[2] + local faction_name = params[3] + if not target_name then + return false, S("Missing player name.") + elseif not faction_name then + return false, S("Missing faction name.") + elseif not facts[faction_name] then + return false, S("Faction @1 doesn't exist.", faction_name) + elseif not minetest.player_exists(target_name) then + return false, S("Player @1 doesn't exist.", target_name) + end + local player_factions = factions.get_player_factions(target_name) + if facts[faction_name].members[target_name] then + return false, S("Player @1 is already in faction @2.", + target_name, faction_name) + elseif player_factions and factions.mode_unique_faction then + return false, S("Player @1 is already in faction @2.", + target_name, player_factions[1]) + else + if factions.join_faction(faction_name, target_name) then + return true, S("@1 is now a member of faction @2.", target_name, faction_name) else - local target = params[2] - local faction_name = params[3] - if not target then - return false, S("Missing target.") - elseif not faction_name then - return false, S("Missing faction name.") - elseif facts[faction_name] == nil then - return false, S("The faction @1 doesn't exist.", faction_name) - elseif not minetest.player_exists(target) then - return false, S("The player doesn't exist.") - elseif factions.mode_unique_faction and factions.get_player_faction(target) ~= nil then - return false, S("The player is already in the faction \"@1\".",factions.get_player_faction(target)) - else - if factions.join_faction(faction_name, target) then - return true, S("@1 is now a member of the faction @2.", target, faction_name) - else - return false, S("Error on adding @1 into @2.", target, faction_name) - end - end + -- is this portion ever reachable at all? + return false, S("Error adding @1 to @2.", target_name, faction_name) end - else + end +end + +local function handle_command(player_name, param) + local params = {} + for p in string.gmatch(param, "[^%s]+") do + table.insert(params, p) + end + local action = params[1] + if not action or not cc[action:lower()] then return false, S("Unknown subcommand. Run '/help factions' for help.") end + + local not_admin = not minetest.get_player_privs(player_name)[factions.priv] + return cc[action:lower()](player_name, params, not_admin) end minetest.register_chatcommand("factions", { - params = "create : "..S("Create a new faction").."\n" - .."list: "..S("List available factions").."\n" - .."info : "..S("See information on a faction").."\n" - .."player_info : "..S("See information on a player").."\n" - .."join : "..S("Join an existing faction").."\n" - .."leave [faction]: "..S("Leave your faction").."\n" - .."kick [faction]: "..S("Kick someone from your faction or from the given faction").."\n" - .."disband [faction]: "..S("Disband your faction or the given faction").."\n" - .."passwd [faction]: "..S("Change your faction's password or the password of the given faction").."\n" - .."chown [faction]: "..S("Transfer ownership of your faction").."\n" - .."invite : "..S("Add player to a faction, you need playerfactions_admin privs").."\n", + params = "create : " .. S("Create a new faction") .. "\n" + .. "list: " .. S("List available factions") .. "\n" + .. "info []: " .. S("See information about a faction") .. "\n" + .. "player_info []: " .. S("See information about a player") .. "\n" + .. "join : " .. S("Join an existing faction") .. "\n" + .. "leave []: " .. S("Leave your faction") .. "\n" + .. "kick []: " + .. S("Kick someone from your faction or from the given faction") .. "\n" + .. "disband []: " + .. S("Disband your faction or the given faction") .. "\n" + .. "passwd []: " + .. S("Change your faction's password or the password of the given faction") .." \n" + .. "chown []: " + .. S("Transfer ownership of your faction") .. "\n" + .. "invite : " + .. S("Add player to a faction, you need @1 priv", factions.priv) .. "\n", description = "", privs = {}, func = handle_command }) + +-- Fix factions +do + local save_needed = false + for _, fact in pairs(facts) do + if not fact.members then + fact.members = { + [fact.owner] = true + } + save_needed = true + end + end + if save_needed then + save_factions() + end +end + +-- Integration testing +if minetest.get_modpath("mtt") and mtt.enabled then + factions.S = S + factions.handle_command = handle_command + dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/mtt.lua") +end + +print("[playerfactions] loaded") diff --git a/locale/playerfactions.de.tr b/locale/playerfactions.de.tr new file mode 100644 index 0000000..af1ce6e --- /dev/null +++ b/locale/playerfactions.de.tr @@ -0,0 +1,79 @@ +# textdomain: playerfactions + + +### init.lua ### + +@1 has the @2 privilege so they can admin every faction.=@1 hat das @2-Privileg, sodass er jede Fraktion verwalten kann. + +@1 is in the following factions: @2.=@1 ist in den folgenden Fraktionen: @2. +@1 is now a member of faction @2.=@1 ist jetzt Mitglied der Fraktion @2. +@1 isn't in faction @2.=@1 ist nicht in der Fraktion @2. +Add player to a faction, you need @1 privs=Füge einen Spieler zu einer Fraktion hinzu, du benötigst @1-Privilegien +Allow the use of all playerfactions commands=Erlaubt die Verwendung aller Fraktionsbefehle + +Change your faction's password or the password of the given faction=Ändere das Passwort deiner Fraktion oder das Passwort der angegebenen Fraktion + +Create a new faction=Erstelle eine neue Fraktion +Disband your faction or the given faction=Löse deine Fraktion oder die angegebene Fraktion auf +Disbanded @1.=@1 aufgelöst. +Error kicking @1 from faction.=Fehler beim Rauswerfen von @1 aus der Fraktion. +Error adding @1 to @2.=Fehler beim Hinzufügen von @1 zu @2 +Error joining faction.=Fehler beim Beitritt zur Fraktion. +Error leaving faction.=Fehler beim Verlassen der Fraktion. +Factions (@1): @2.=Fraktionen (@1): @2. +Failed to change password.=Passwort konnte nicht geändert werden. +Failed to transfer ownership.=Eigentum konnte nicht übertragen werden. +Join an existing faction=Trete einer bestehenden Fraktion bei +Joined @1.=Bist @1 beigetreten. +Kick someone from your faction or from the given faction=Jemanden aus deiner Fraktion oder der angegebenen Fraktion werfen +Kicked @1 from faction.=@1 aus Fraktion geworfen. +Leave your faction=Deine Fraktion verlassen +Left @1.=Hast @1 verlassen. +List available factions=Verfügbare Fraktionen auflisten +Missing faction name.=Fraktionsname fehlt. +Missing password.=Passwort fehlt. +Missing player name.=Spielername fehlt. +Name: @1@nOwner: @2@nMembers: @3=Name: @1@nBesitzer: @2@nMitglieder: @3 +Ownership has been transferred to @1.=Eigentum wurde auf @1 übertragen. +Password has been updated.=Passwort wurde aktualisiert. +Password: @1=Passwort: @1 +Permission denied: Wrong password.=Berechtigung verweigert: Falsches Passwort. + +Permission denied: You are not the owner of that faction, and don't have the @1 privilege.=Berechtigung verweigert: Du bist nicht der Besitzer dieser Fraktion und hast nicht das @1-Privileg. + +Permission denied: You can't use this command, @1 priv is needed.=Berechtigung verweigert: Du kannst diesen Befehl nicht verwenden, @1-Privileg ist erforderlich. + +Registered @1.=@1 Registriert. +See information about a faction=Informationen zu einer Fraktion anzeigen +See information about a player=Informationen zu einem Spieler anzeigen +Faction @1 already exists.=Fraktion @1 existiert bereits. +Faction @1 doesn't exist.=Fraktion @1 existiert nicht. + +The faction has more than @1 members, the members list can't be shown.=Die Fraktion hat mehr als @1 Mitglieder, die Mitgliederliste kann nicht angezeigt werden. + +Player @1 doesn't exist.=Spieler @1 existiert nicht. +Player @1 is already in the faction @2.=Spieler @1 ist bereits in der Fraktion @2. +There are no factions yet.=Es gibt noch keine Fraktionen. +Player @1 doesn't exist or isn't in any faction.=Spieler @1 existiert nicht oder ist in keiner Fraktion. +@1 is not in the specified faction.=@1 ist nicht in der angegebenen Fraktion. +@1 doesn't own any factions.=@1 besitzt keine Fraktionen. +@1 is the owner of the following factions: @2.=@1 ist der Besitzer der folgenden Fraktionen: @2. +Transfer ownership of your faction=Eigentum deiner Fraktion übertragen +Unknown subcommand. Run '/help factions' for help.=Unbekannter Unterbefehl. Führe „/help factions“ aus, um Hilfe zu erhalten. +You are already in a faction.=Du bist bereits in einer Fraktion. + +You are in multiple factions, you have to choose one of them: @1.=Du bist in mehreren Fraktionen, du musst eine davon auswählen: @1. + +You are not in a faction.=Du bist in keiner Fraktion. + +You are the owner of multiple factions, you have to choose one of them: @1.=Du bist der Besitzer mehrerer Fraktionen, du musst eine davon auswählen: @1. + +You don't own any factions, you can't use this command.=Du besitzst keine Fraktionen, du kannst diesen Befehl nicht verwenden. +You don't own any factions.=Du besitzst keine Fraktionen. + +You cannot kick the owner of a faction, use '/factions chown []' to change the ownership.=Du kannst den Besitzer einer Fraktion nicht rauswerfen, verwende „/factions chown []“, um den Besitzer zu ändern. + +You cannot leave your own faction, change owner or disband it.=Du kannst deine eigene Fraktion nicht verlassen, wechsle den Besitzer oder löse die Fraktion auf. +No factions found.=Keine Fraktionen gefunden. +You aren't part of faction @1.=Du bist nicht Teil der Fraktion @1. +You are already in faction @1.=Du bist bereits in der Fraktion @1. diff --git a/locale/playerfactions.es.tr b/locale/playerfactions.es.tr new file mode 100644 index 0000000..f51ba17 --- /dev/null +++ b/locale/playerfactions.es.tr @@ -0,0 +1,79 @@ +# textdomain: playerfactions + + +### init.lua ### + +@1 has the @2 privilege so they can admin every faction.=@1 tiene el privilegio @2, por lo que puede administrar todas las facciones. + +@1 is in the following factions: @2.=@1 está en las siguientes facciones: @2. +@1 is now a member of faction @2.=@1 ahora es miembro de la facción @2. +@1 isn't in faction @2.=@1 no está en la facción @2. +Add player to a faction, you need @1 privs=Agrega un jugador a una facción, necesitas los privilegios @1 +Allow the use of all playerfactions commands=Permite el uso de todos los comandos de facciones + +Change your faction's password or the password of the given faction=Cambiar la contraseña de tu facción o la contraseña de la facción indicada + +Create a new faction=Crear una nueva facción +Disband your faction or the given faction=Disolver tu facción o la facción indicada +Disbanded @1.=Disuelto @1. +Error kicking @1 from faction.=Error al expulsar a @1 de la facción. +Error adding @1 to @2.=Error al agregar @1 a @2. +Error joining faction.=Error al unirse a la facción. +Error leaving faction.=Error al salir de la facción. +Factions (@1): @2.=Facciones (@1): @2. +Failed to change password.=Error al cambiar la contraseña. +Failed to transfer ownership.=Error al transferir la propiedad. +Join an existing faction=Unirse a una facción existente +Joined @1.=Te uniste @1. +Kick someone from your faction or from the given faction=Expulsar a alguien de tu facción o de la facción indicada +Kicked @1 from faction.=Expulsaste a @1 de la facción. +Leave your faction=Abandonar tu facción +Left @1.=Abandonaste @1. +List available factions=Lista de facciones disponibles +Missing faction name.=Falta el nombre de la facción. +Missing password.=Falta la contraseña. +Missing player name.=Falta el nombre del jugador. +Name: @1@nOwner: @2@nMembers: @3=Nombre: @1@nPropietario: @2@nMiembros: @3 +Ownership has been transferred to @1.=La propiedad ha sido transferida a @1. +Password has been updated.=La contraseña ha sido actualizada. +Password: @1=Contraseña: @1 +Permission denied: Wrong password.=Permiso denegado: Contraseña incorrecta. + +Permission denied: You are not the owner of that faction, and don't have the @1 privilege.=Permiso denegado: No eres el propietario de esa facción y no tienes el privilegio @1. + +Permission denied: You can't use this command, @1 priv is needed.=Permiso denegado: No puedes usar este comando, necesitas el privilegio @1. + +Registered @1.=@1 registrado. +See information about a faction=Ver información sobre una facción +See information about a player=Ver información sobre un jugador +Faction @1 already exists.=La facción @1 ya existe. +Faction @1 doesn't exist.=La facción @1 no existe. + +The faction has more than @1 members, the members list can't be shown.=La facción tiene más de @1 miembros, la lista de miembros no se puede mostrar. + +Player @1 doesn't exist.=El jugador @1 no existe. +Player @1 is already in the faction @2.=El jugador @1 ya está en la facción @2. +There are no factions yet.=Todavía no hay facciones. +Player @1 doesn't exist or isn't in any faction.=El jugador @1 no existe o no está en ninguna facción. +@1 is not in the specified faction.=@1 no está en la facción especificada. +@1 doesn't own any factions.=@1 no posee ninguna facción. +@1 is the owner of the following factions: @2.=@1 es el propietario de las siguientes facciones: @2. +Transfer ownership of your faction=Transferir la propiedad de tu facción +Unknown subcommand. Run '/help factions' for help.=Subcomando desconocido. Ejecuta '/help factions' para obtener ayuda. +You are already in a faction.=Ya estás en una facción. + +You are in multiple factions, you have to choose one of them: @1.=Estás en varias facciones, debes elegir una de ellas: @1. + +You are not in a faction.=No estás en una facción. + +You are the owner of multiple factions, you have to choose one of them: @1.=Eres el propietario de varias facciones, debes elegir una de ellas: @1. + +You don't own any factions, you can't use this command.=No posees ninguna facción, no puedes usar este comando. +You don't own any factions.=No posees ninguna facción. + +You cannot kick the owner of a faction, use '/factions chown []' to change the ownership.=No puedes expulsar al propietario de una facción, usa '/factions chown []' para cambiar la propiedad. + +You cannot leave your own faction, change owner or disband it.=No puedes abandonar tu propia facción, cambiar de propietario o disolverla. +No factions found.=Ninguna facción encontrado. +You aren't part of faction @1.=No eres parte de la facción @1. +You are already in faction @1.=Ya estás en la facción @1. diff --git a/locale/playerfactions.fr.tr b/locale/playerfactions.fr.tr index a744bdf..4251193 100644 --- a/locale/playerfactions.fr.tr +++ b/locale/playerfactions.fr.tr @@ -3,79 +3,78 @@ ### init.lua ### -@1 has the playerfactions_admin privilege so they can admin every faction.=@1 a le privilège playerfactions_admin et peut donc administrer toutes les factions. +@1 has the @2 privilege so they can admin every faction.=@1 a le privilège @2 et peut donc administrer toutes les factions. @1 is in the following factions: @2.=@1 est dans les factions suivantes : @2. -@1 is now a member of the faction @2.=@1 est maintenant membre de la faction @2. -@1 isn't in your faction.=@1 n’est pas dans votre faction. -Add player to a faction, you need playerfactions_admin privs=Ajoute un joueur à une faction, nécessite le privilège playerfactions_admin +@1 is now a member of faction @2.=@1 est maintenant membre de la faction @2. +@1 isn't in faction @2.=@1 n’est pas dans la faction @2. +Add player to a faction, you need @1 privs=Ajoute un joueur à une faction, nécessite le privilège @1 Allow the use of all playerfactions commands=Autorise l’utilisation de toutes les commandes de playerfactions -Change your faction's password or the password of the given faction=Change le mot de passe de votre faction ou la faction donnée +Change your faction's password or the password of the given faction=Change le mot de passe de ta faction ou la faction donnée Create a new faction=Crée une nouvelle faction -Disband your faction or the given faction=Dissoudre votre faction ou la faction donnée +Disband your faction or the given faction=Dissoudre ta faction ou la faction donnée Disbanded @1.=@1 est dissoute. Error kicking @1 from faction.=Erreur lors de la tentative de virer @1 de la faction. -Error on adding @1 into @2.=Erreur lors de l’ajout de @1 dans @2. -Error on joining.=Erreur lors de la tentative de rejoindre la faction. -Error on leaving faction.=Erreur lors de la tentative de quitter la faction. +Error adding @1 to @2.=Erreur lors de l’ajout de @1 dans @2. +Error joining faction.=Erreur lors de la tentative de rejoindre la faction. +Error leaving faction.=Erreur lors de la tentative de quitter la faction. Factions (@1): @2.=Factions (@1) : @2. Failed to change password.=Echec du changement de mot de passe. Failed to transfer ownership.=Echec du transfert de propriété. Join an existing faction=Rejoindre une faction existante. -Joined @1.=Vous avez rejoint @1. -Kick someone from your faction or from the given faction=Virer quelqu’un de votre faction ou de la faction donnée. +Joined @1.=Tu as rejoint @1. +Kick someone from your faction or from the given faction=Virer quelqu’un de ta faction ou de la faction donnée. Kicked @1 from faction.=@1 a été viré de la faction. -Leave your faction=Quitter votre faction -Left @1.=Vous avez quitté @1. +Leave your faction=Quitter ta faction +Left @1.=Tu as quitté @1. List available factions=Liste les factions disponibles Missing faction name.=Nom de faction manquant. Missing password.=Mot de passe manquant. Missing player name.=Nom de joueur manquant. -Missing target.=Nom du joueur cible manquant. Name: @1@nOwner: @2@nMembers: @3=Nom : @1@nPropriétaire : @2@nMembres : @3 Ownership has been transferred to @1.=La propriété a été transférée à @1. Password has been updated.=Le mot de passe a été mis à jour. Password: @1=Mot de passe : @1 Permission denied: Wrong password.=Permission refusée : mauvais mot de passe. -Permission denied: You are not the owner of this faction, and don't have the playerfactions_admin privilege.=Permission refusée : vous n’êtes pas le propriétaire de cette faction, et n’avez pas le privilège playerfactions_admin. +Permission denied: You are not the owner of that faction, and don't have the @1 privilege.=Permission refusée : tu n'es pas le propriétaire de cette faction, et tu n'as pas le privilège @1. -Permission denied: You can't use this command, playerfactions_admin priv is needed.=Permission refusée : vous ne pouvez pas utiliser cette commande, le privilège playerfactions_admin est nécessaire. +Permission denied: You can't use this command, @1 priv is needed.=Permission refusée : tu ne peux pas utiliser cette commande, le privilège @1 est nécessaire. Registered @1.=@1 enregistrée. -See information on a faction=Voir les informations d’une faction -See information on a player=Voir les informations d’un joueur -That faction already exists.=Cette faction existe déjà. -The faction @1 doesn't exist.=La faction @1 n’existe pas. +See information about a faction=Voir les informations d’une faction +See information about a player=Voir les informations d’un joueur +Faction @1 already exists.=La faction @1 existe déjà. +Faction @1 doesn't exist.=La faction @1 n’existe pas. The faction has more than @1 members, the members list can't be shown.=Cette faction a plus que @1 membres, la liste des membres ne peut pas être affichée. -The given faction doesn't exists.=La faction en question n’existe pas. -The player doesn't exist.=Le joueur n’existe pas. -The player is already in the faction "@1".=Le joueur est déjà dans la faction "@1". +Player @1 doesn't exist.=Le joueur @1 n’existe pas. +Player @1 is already in the faction @2.=Le joueur @1 est déjà dans la faction @2. There are no factions yet.=Il n’y a pas encore de factions. -This faction doesn't exists.=Cette faction n’existe pas. -The player name is nil or empty.=Le nom du joueur est nul ou vide. -This player doesn't exists or is in no faction=Ce joueur n’existe pas ou n’est dans aucune faction. -This player is not in the specified faction.=Le joueur n’est pas dans la faction spécifiée. -This player is the owner of no faction.=Ce joueur n’est propriétaire d’aucune faction. -This player is the owner of the following factions: @1.=Ce joueur n’est le propriétaire d’aucune des factions suivantes : @1. -Transfer ownership of your faction=Transfert la propriété de votre faction -Unknown subcommand. Run '/help factions' for help.=Sous-commande inconnue. Faites '/help factions' pour l’aide. -You are already in a faction.=Vous êtes déjà dans une faction. +Player @1 doesn't exist or isn't in any faction.=Le joueur @1 n’existe pas ou n’est dans aucune faction. +@1 is not in the specified faction.=@1 n’est pas dans la faction spécifiée. +@1 doesn't own any factions.=@1 n’est propriétaire d’aucune faction. +@1 is the owner of the following factions: @2.=@1 est le propriétaire des factions suivantes : @2. +Transfer ownership of your faction=Transfert la propriété de ta faction +Unknown subcommand. Run '/help factions' for help.=Sous-commande inconnue. Fais '/help factions' pour l’aide. +You are already in a faction.=Tu es déjà dans une faction. -You are in many factions, you have to choose one of them: @1.=Vous êtes dans plusieurs factions, vous devez choisir l’une d’entre elles : @1. +You are in multiple factions, you have to choose one of them: @1.=Tu es dans plusieurs factions, tu dois choisir l’une d’entre elles : @1. -You are not in a faction.=Vous n’êtes dans aucune faction. +You are not in a faction.=Tu n'es dans aucune faction. -You are the owner of many factions, you have to choose one of them: @1.=Vous êtes propriétaire de plusieurs factions, vous devez choisir l’une d’entre elles : @1. +You are the owner of multiple factions, you have to choose one of them: @1.=Tu es propriétaire de plusieurs factions, tu dois choisir l’une d’entre elles : @1. -You are the owner of no faction, you can't use this command.=Vous n’êtes propriétaire d’aucune faction, vous ne pouvez pas utiliser cette commande. -You are the owner of no faction.=Vous n’êtes propriétaire d’aucune faction. +You don't own any factions, you can't use this command.=Tu n'es propriétaire d’aucune faction, tu ne peux pas utiliser cette commande. +You don't own any factions.=Tu n'es propriétaire d’aucune faction. -You cannot kick the owner of a faction, use '/factions chown [faction]' to change the ownership.=Vous ne pouvez pas virer le propriétaire de sa faction, utilisez '/factions chown [faction]' pour changer le propriétaire. +You cannot kick the owner of a faction, use '/factions chown []' to change the ownership.=Tu ne peux pas virer le propriétaire de sa faction, utilise '/factions chown []' pour changer le propriétaire. -You cannot leave your own faction, change owner or disband it.=Vous ne pouvez pas quitter votre propre faction, changez le propriétaire ou dissolvez la. +You cannot leave your own faction, change owner or disband it.=Tu ne peux pas quitter ta propre faction, change le propriétaire ou dissout la. +No factions found.=Aucune faction trouvée. +You aren't part of faction @1.=Tu ne fais pas partie de la faction @1. +You are already in faction @1.=Tu es déjà dans la faction @1. diff --git a/locale/template.txt b/locale/template.txt index c309bf4..4386307 100644 --- a/locale/template.txt +++ b/locale/template.txt @@ -3,12 +3,12 @@ ### init.lua ### -@1 has the playerfactions_admin privilege so they can admin every faction.= +@1 has the @2 privilege so they can admin every faction.= @1 is in the following factions: @2.= -@1 is now a member of the faction @2.= -@1 isn't in your faction.= -Add player to a faction, you need playerfactions_admin privs= +@1 is now a member of faction @2.= +@1 isn't in faction @2.= +Add player to a faction, you need @1 privs= Allow the use of all playerfactions commands= Change your faction's password or the password of the given faction= @@ -17,9 +17,9 @@ Create a new faction= Disband your faction or the given faction= Disbanded @1.= Error kicking @1 from faction.= -Error on adding @1 into @2.= -Error on joining.= -Error on leaving faction.= +Error adding @1 to @2.= +Error joining faction.= +Error leaving faction.= Factions (@1): @2.= Failed to change password.= Failed to transfer ownership.= @@ -33,49 +33,48 @@ List available factions= Missing faction name.= Missing password.= Missing player name.= -Missing target.= Name: @1@nOwner: @2@nMembers: @3= Ownership has been transferred to @1.= Password has been updated.= Password: @1= Permission denied: Wrong password.= -Permission denied: You are not the owner of this faction, and don't have the playerfactions_admin privilege.= +Permission denied: You are not the owner of that faction, and don't have the @1 privilege.= -Permission denied: You can't use this command, playerfactions_admin priv is needed.= +Permission denied: You can't use this command, @1 priv is needed.= Registered @1.= -See information on a faction= -See information on a player= -That faction already exists.= -The faction @1 doesn't exist.= +See information about a faction= +See information about a player= +Faction @1 already exists.= +Faction @1 doesn't exist.= The faction has more than @1 members, the members list can't be shown.= -The given faction doesn't exists.= -The player doesn't exist.= -The player is already in the faction "@1".= +Player @1 doesn't exist.= +Player @1 is already in the faction @2.= There are no factions yet.= -This faction doesn't exists.= -The player name is nil or empty.= -This player doesn't exists or is in no faction= -This player is not in the specified faction.= -This player is the owner of no faction.= -This player is the owner of the following factions: @1.= +Player @1 doesn't exist or isn't in any faction.= +@1 is not in the specified faction.= +@1 doesn't own any factions.= +@1 is the owner of the following factions: @2.= Transfer ownership of your faction= Unknown subcommand. Run '/help factions' for help.= You are already in a faction.= -You are in many factions, you have to choose one of them: @1.= +You are in multiple factions, you have to choose one of them: @1.= You are not in a faction.= -You are the owner of many factions, you have to choose one of them: @1.= +You are the owner of multiple factions, you have to choose one of them: @1.= -You are the owner of no faction, you can't use this command.= -You are the owner of no faction.= +You don't own any factions, you can't use this command.= +You don't own any factions.= -You cannot kick the owner of a faction, use '/factions chown [faction]' to change the ownership.= +You cannot kick the owner of a faction, use '/factions chown []' to change the ownership.= You cannot leave your own faction, change owner or disband it.= +No factions found.= +You aren't part of faction @1.= +You are already in faction @1.= diff --git a/mod.conf b/mod.conf index f3e3069..f0a74e8 100644 --- a/mod.conf +++ b/mod.conf @@ -1,2 +1,3 @@ name = playerfactions min_minetest_version = 5.0.0 +optional_depends = mtt diff --git a/mtt.lua b/mtt.lua new file mode 100644 index 0000000..8751559 --- /dev/null +++ b/mtt.lua @@ -0,0 +1,512 @@ +-- requires [fakelib] to work properly +-- https://github.com/OgelGames/fakelib.git + +local pd +if table.pack then + pd = function(...) print(dump(table.pack(...))) end +else + pd = function(...) for _, v in ipairs({ ... }) do print(dump(v)) end end +end +local fcc, S = factions.handle_command, factions.S +factions.mode_unique_faction = false +factions.max_members_list = 11 + +-- factions chat command checker +-- b1: expected return bool +-- s1: expected return string +-- n: name of command executor +-- c: string of command parameters (the "/factions " part cut off) +local function fccc(b1, s1, n, c) + local b2, s2 = fcc(n, c) + return b1 == b2 and s1 == s2 +end + +local function resetDB() + for k in pairs(factions.get_facts()) do + factions.disband_faction(k) + end +end + +local function makeFactions() + return factions.register_faction('Endorian', 'Endor', 'eEe') + and factions.register_faction('Alberian', 'Albert', 'a') + and factions.register_faction('Gandalfian', 'Gandalf', 'GgG♥💩☺') +end + +local function dbChecks(callback) + -- basic db integrity tests + local facts = factions.get_facts() + assert('table' == type(facts)) + assert('table' == type(facts.Alberian)) + assert('Albert' == facts.Alberian.owner) + assert('Alberian' == facts.Alberian.name) + assert('table' == type(facts.Alberian.members)) + -- make sure owners have been added as memebers + assert(true == facts.Alberian.members.Albert) + -- should never fail + assert('eEe' == facts.Endorian.password) + assert('a' == facts.Alberian.password) + assert('GgG♥💩☺' == facts.Gandalfian.password) + + callback() +end + +mtt.register('reset db', function(callback) resetDB() callback() end) + +mtt.register('join & setup players', function(callback) + -- some player + assert('table' == type(mtt.join_player('Endor'))) + -- faction admin + assert('table' == type(mtt.join_player('Albert'))) + -- some other player + assert('table' == type(mtt.join_player('Gandalf'))) + -- player without privs or factions + assert('table' == type(mtt.join_player('HanSolo'))) + + -- make Albert a faction-admin + local player_privs = minetest.get_player_privs('Albert') + player_privs[factions.priv] = true + minetest.set_player_privs('Albert', player_privs) + + -- make sure the others aren't + for _, name in ipairs({ 'Endor', 'Gandalf', 'HanSolo' }) do + player_privs = minetest.get_player_privs(name) + player_privs[factions.priv] = nil + minetest.set_player_privs(name, player_privs) + end + + callback() +end) + +mtt.register('some players leave', function(callback) + -- let's test without the admin online + assert(true == mtt.leave_player('Albert')) + assert(true == mtt.leave_player('Gandalf')) + + callback() +end) + +mtt.register('make factions with backend', function(callback) + assert(makeFactions()) + + callback() +end) + +mtt.register('basic db checks', dbChecks) + +mtt.register('backend functions: player_is_in_faction', function(callback) + assert(false == factions.player_is_in_faction( + 'notExistingFaction', 'notExistingPlayer')) + assert(false == factions.player_is_in_faction( + 'notExistingFaction', 'Gandalf')) + assert(false == factions.player_is_in_faction( + 'Gandalfian', 'notExistingPlayer')) + assert(nil == factions.player_is_in_faction( + 'Gandalfian', 'Albert')) + assert(true == factions.player_is_in_faction( + 'Gandalfian', 'Gandalf')) + + callback() +end) + +mtt.register('backend functions: get_player_faction', function(callback) + -- (depricated) --> check log output for messages + assert(false == factions.get_player_faction('notExistingPlayer')) + assert(nil == factions.get_player_faction('HanSolo')) + assert('Alberian' == factions.get_player_faction('Albert')) + + callback() +end) + +mtt.register('backend functions: get_player_factions', function(callback) + if pcall(factions.get_player_factions, nil) then + callback('did not fail with nil as player argument -> bad') + end + if pcall(factions.get_player_factions, 42) then + callback('did not fail with number as player argument -> bad') + end + assert(false == factions.get_player_factions('notExistingPlayer')) + assert(false == factions.get_player_factions('HanSolo')) + assert('Alberian' == factions.get_player_factions('Albert')[1]) + + callback() +end) + +mtt.register('backend functions: get_owned_factions', function(callback) + assert(false == factions.get_owned_factions(nil)) + assert(false == factions.get_owned_factions(42)) + assert(false == factions.get_owned_factions('notExistingPlayer')) + assert(false == factions.get_owned_factions('HanSolo')) + local t = factions.get_owned_factions('Albert') + assert(1 == #t and 'Alberian' == t[1]) + + callback() +end) + +mtt.register('backend functions: get_administered_factions', function(callback) + if pcall(factions.get_administered_factions) then + callback('calling get_administered_factions with nil did not raise error') + end + -- a bit strange that number as player name 'works' + assert(false == factions.get_administered_factions(42)) + assert(false == factions.get_administered_factions('notExistingPlayer')) + assert(false == factions.get_administered_factions('HanSolo')) + local t = factions.get_administered_factions('Gandalf') + assert(1 == #t and 'Gandalfian' == t[1]) + assert(3 == #factions.get_administered_factions('Albert')) + + callback() +end) + +mtt.register('backend functions: get_owner', function(callback) + assert(false == factions.get_owner('notExistingFaction')) + assert('Gandalf' == factions.get_owner('Gandalfian')) + + callback() +end) + +mtt.register('backend functions: chown', function(callback) + assert(false == factions.chown('notExistingFaction', 'Gandalf')) + assert(true == factions.chown('Endorian', 'Gandalf')) + -- revert the 'illegal' use + factions.chown('Endorian', 'Endor') + + callback() +end) + +mtt.register('backend functions: register_faction', function(callback) + -- (partly tested in setup) + assert(false == factions.register_faction('Endorian', 'Endor', 'rodnE')) + assert(false == factions.register_faction()) + -- empty password + assert(factions.register_faction('foo', 'bar', '')) + + callback() +end) + +mtt.register('backend functions: disband_faction', function(callback) + -- (partly tested in setup) + assert(factions.disband_faction('foo')) + assert(false == factions.disband_faction()) + assert(false == factions.disband_faction('notExistingFaction')) + + callback() +end) + +mtt.register('backend functions: valid_password', function(callback) + assert(false == factions.valid_password()) + assert(false == factions.valid_password('Endorian')) + assert(false == factions.valid_password('Endorian', 'foobar')) + assert(true == factions.valid_password('Endorian', 'eEe')) + + callback() +end) + +mtt.register('backend functions: get_password', function(callback) + assert(false == factions.get_password()) + assert('eEe' == factions.get_password('Endorian')) + + callback() +end) + +mtt.register('backend functions: set_password', function(callback) + assert(false == factions.set_password('notExistingFaction', 'foobar')) + assert(false == factions.set_password('Endorian')) + assert(true == factions.set_password('Endorian', 'EeE')) + assert(factions.valid_password('Endorian', 'EeE')) + -- revert that again + factions.set_password('Endorian', 'eEe') + + callback() +end) + +mtt.register('backend functions: join_faction', function(callback) + assert(false == factions.join_faction()) + assert(false == factions.join_faction('Endorian')) + assert(false == factions.join_faction('Endorian', 'notExistingPlayer')) + assert(true == factions.join_faction('Endorian', 'Gandalf')) + + callback() +end) + +mtt.register('backend functions: leave_faction', function(callback) + assert(false == factions.leave_faction()) + assert(false == factions.leave_faction('Endorian')) + assert(false == factions.leave_faction('Endorian', 'notExistingPlayer')) + assert(true == factions.leave_faction('Endorian', 'Gandalf')) + + callback() +end) + +mtt.register('intermediate db checks', dbChecks) + +mtt.register('frontend functions: no arguments', function(callback) + assert(fccc(false, S("Unknown subcommand. Run '/help factions' for help."), + '', '')) + + callback() +end) + +mtt.register('frontend functions: create', function(callback) + assert(fccc(false, S("Missing faction name."), 'Gandalf', 'create')) + assert(fccc(false, S("Missing password."), 'Gandalf', 'create foobar')) + assert(fccc(false, S("Faction @1 already exists.", 'Gandalfian'), + 'Gandalf', 'create Gandalfian foobar')) + factions.mode_unique_faction = true + assert(fccc(false, S("You are already in a faction."), + 'Gandalf', 'create Gandalfian2 foobar')) + factions.mode_unique_faction = false + -- correct creation (also with capitals in sub-command) + assert(fccc(true, S("Registered @1.", 'Gandalfian2'), + 'Gandalf', 'cREate Gandalfian2 foobar')) + + callback() +end) + +mtt.register('frontend functions: disband', function(callback) + assert(fccc(false, S("Missing password."), 'Gandalf', 'disband')) + -- list order is not predictable, so we try both orders + assert(fccc(false, S( + "You are the owner of multiple factions, you have to choose one of them: @1.", + 'Gandalfian, Gandalfian2'), 'Gandalf', 'disband foobar') + or fccc(false, S( + "You are the owner of multiple factions, you have to choose one of them: @1.", + 'Gandalfian2, Gandalfian'), 'Gandalf', 'disband foobar')) + assert(fccc(false, S("You don't own any factions."), 'HanSolo', + 'disband foobar')) + assert(fccc(false, S("Permission denied: You are not the owner of that faction," + .. " and don't have the @1 privilege.", factions.priv), + 'Endor', 'disband foobar Gandalfian2')) + assert(fccc(false, S("Permission denied: Wrong password."), + 'Endor', 'disband foobar')) + assert(fccc(true, S("Disbanded @1.", 'Endorian'), + 'Endor', 'disband eEe')) + -- admin disbands other player's faction w/o knowing password + assert(fccc(true, S("Disbanded @1.", 'Gandalfian2'), + 'Albert', 'disband foobar Gandalfian2')) + assert(fccc(false, S("Faction @1 doesn't exist.", 'Gandalfian2'), + 'Gandalf', 'disband eEe Gandalfian2')) + + callback() +end) + +mtt.register('frontend functions: list', function(callback) + assert(fccc(true, S("Factions (@1): @2.", 2, 'Gandalfian, Alberian'), + '', 'list') + or fccc(true, S("Factions (@1): @2.", 2, 'Alberian, Gandalfian'), + '', 'list')) + resetDB() + assert(fccc(true, S("There are no factions yet."), '', 'list')) + + callback() +end) + +mtt.register('frontend functions: info', function(callback) + assert(fccc(true, S("No factions found."), 'HanSolo', 'info')) + makeFactions() + assert(fccc(false, S("Faction @1 doesn't exist.", 'foobar'), + 'Endor', 'info foobar')) + factions.join_faction('Endorian', 'Gandalf') + assert(fccc(false, S("You are in multiple factions, you have to choose one of them: @1.", + 'Endorian, Gandalfian'), 'Gandalf', 'info') + or fccc(false, S("You are in multiple factions, you have to choose one of them: @1.", + 'Gandalfian, Endorian'), 'Gandalf', 'info')) + -- SwissalpS can't be bothered to check some of these results in depth, + -- so just dumping result for optical check. + -- owner sees password + pd('Endor executes: /factions info', fcc('Endor', 'info')) + assert(fcc('Endor', 'info')) + factions.max_members_list = 1 + pd('max_members_list == 1 and Endor executes: /factions info', + fcc('Endor', 'info')) + assert(fcc('Endor', 'info')) + factions.max_members_list = 11 + pd('Endor executes: /factions info Gandalfian', fcc('Endor', 'info Gandalfian')) + assert(fcc('Endor', 'info Gandalfian')) + -- admin sees password + pd('Albert executes: /factions info Gandalfian', fcc('Albert', 'info Gandalfian')) + assert(fcc('Albert', 'info Gandalfian')) + + callback() +end) + +mtt.register('frontend functions: player_info', function(callback) + -- should never happen + assert(fccc(false, S("Missing player name."), '', 'player_info')) + assert(fccc(false, S("Player @1 doesn't exist or isn't in any faction.", + 'HanSolo'), 'HanSolo', 'player_info')) + assert(fccc(false, S("Player @1 doesn't exist or isn't in any faction.", + 'notExistingPlayer'), 'Endor', 'player_info notExistingPlayer')) + assert(fccc(true, S("@1 is in the following factions: @2.", + 'Endor', 'Endorian') .. "\n" + .. S("@1 is the owner of the following factions: @2.", + 'Endor', 'Endorian'), 'Endor', 'player_info')) + assert(fccc(true, S("@1 is in the following factions: @2.", + 'Albert', 'Alberian') .. "\n" + .. S("@1 is the owner of the following factions: @2.", + 'Albert', 'Alberian') .. "\n" + .. S("@1 has the @2 privilege so they can admin every faction.", + 'Albert', factions.priv), 'Endor', 'player_info Albert')) + + callback() +end) + +mtt.register('frontend functions: join', function(callback) + factions.mode_unique_faction = true + assert(fccc(false, S("You are already in a faction."), + 'Endor', 'join')) + factions.mode_unique_faction = false + assert(fccc(false, S("Missing faction name."), + 'Endor', 'join')) + assert(fccc(false, S("Faction @1 doesn't exist.", 'notExistingFaction'), + 'Endor', 'join notExistingFaction')) + assert(fccc(false, S("You are already in faction @1.", 'Endorian'), + 'Endor', 'join Endorian')) + assert(fccc(false, S("Permission denied: Wrong password."), + 'Endor', 'join Gandalfian')) + assert(fccc(false, S("Permission denied: Wrong password."), + 'Endor', 'join Gandalfian abc')) + assert(fccc(true, S("Joined @1.", 'Gandalfian'), + 'Endor', 'join Gandalfian GgG♥💩☺')) + + callback() +end) + +mtt.register('frontend functions: leave', function(callback) + assert(fccc(false, S("You are not in a faction."), + 'HanSolo', 'leave')) + assert(fccc(false, S("You are in multiple factions, you have to choose one of them: @1.", + 'Gandalfian, Endorian'), + 'Endor', 'leave') + or fccc(false, S("You are in multiple factions, you have to choose one of them: @1.", + 'Endorian, Gandalfian'), + 'Endor', 'leave')) + assert(fccc(false, S("Faction @1 doesn't exist.", 'notExistingFaction'), + 'Endor', 'leave notExistingFaction')) + assert(fccc(false, S("You cannot leave your own faction, change owner or disband it."), + 'Albert', 'leave')) + assert(fccc(false, S("You aren't part of faction @1.", 'Gandalfian'), + 'Albert', 'leave Gandalfian')) + assert(fccc(true, S("Left @1.", 'Endorian'), + 'Gandalf', 'leave Endorian')) + + callback() +end) + +mtt.register('frontend functions: kick', function(callback) + assert(fccc(false, S("Missing player name."), + 'Gandalf', 'kick')) + assert(fccc(false, S("You don't own any factions, you can't use this command."), + 'HanSolo', 'kick Endor')) + local b, s = fcc('Albert', 'kick Endor') + -- only works if run on English server + assert(false == b and nil ~= s:find('multiple factions,')) + assert(fccc(false, S("Permission denied: You are not the owner of that faction, " + .. "and don't have the @1 privilege.", factions.priv), + 'Endor', 'kick Gandalf Gandalfian')) + assert(fccc(false, S("@1 is not in the specified faction.", 'Gandalf'), + 'Endor', 'kick Gandalf Endorian')) + + assert(fccc(false, S("You cannot kick the owner of a faction, " + .. "use '/factions chown []' " + .. "to change the ownership."), + 'Albert', 'kick Gandalf Gandalfian')) + assert(fccc(true, S("Kicked @1 from faction.", 'Endor'), + 'Gandalf', 'kick Endor Gandalfian')) + + callback() +end) + +mtt.register('frontend functions: passwd', function(callback) + assert(fccc(false, S("Missing password."), + 'HanSolo', 'passwd')) + assert(fccc(false, S("You don't own any factions, you can't use this command."), + 'HanSolo', 'passwd foobar')) + local b, s = fcc('Albert', 'passwd foobar') + -- only works on English locale + assert(false == b and nil ~= s:find('multiple factions')) + assert(fccc(false, S("Permission denied: You are not the owner of that faction, " + .. "and don't have the @1 privilege.", factions.priv), + 'Endor', 'passwd foobar Gandalfian')) + assert(fccc(true, S("Password has been updated."), + 'Endor', 'passwd foobar')) + assert(factions.get_facts().Endorian.password == 'foobar') + assert(fccc(true, S("Password has been updated."), + 'Gandalf', 'passwd foobar Gandalfian')) + assert(factions.get_facts().Gandalfian.password == 'foobar') + assert(fccc(true, S("Password has been updated."), + 'Albert', 'passwd barf Gandalfian')) + assert(factions.get_facts().Gandalfian.password == 'barf') + + callback() +end) + +mtt.register('frontend functions: chown', function(callback) + assert(fccc(false, S("Missing player name."), + 'Gandalf', 'chown')) + assert(fccc(false, S("Missing password."), + 'Gandalf', 'chown notExistingPlayer')) + assert(fccc(false, S("You don't own any factions, you can't use this command."), + 'HanSolo', 'chown notExistingPlayer foobar')) + local b, s = fcc('Albert', 'chown notExistingPlayer foobar') + assert(false == b and nil ~= s:find('multiple factions')) + assert(fccc(false, S("Permission denied: You are not the owner of that faction, " + .. "and don't have the @1 privilege.", factions.priv), + 'Gandalf', 'chown Endor foobar Endorian')) + assert(fccc(false, S("@1 isn't in faction @2.", 'notExistingPlayer', 'Gandalfian'), + 'Gandalf', 'chown notExistingPlayer foobar')) + assert(fccc(false, S("@1 isn't in faction @2.", 'Endor', 'Gandalfian'), + 'Gandalf', 'chown Endor foobar')) + factions.join_faction('Gandalfian', 'Endor') + assert(fccc(false, S("Permission denied: Wrong password."), + 'Gandalf', 'chown Endor foobar')) + assert(fccc(true, S("Ownership has been transferred to @1.", 'Endor'), + 'Gandalf', 'chown Endor barf')) + assert('Endor' == factions.get_owner('Gandalfian')) + assert(fccc(true, S("Ownership has been transferred to @1.", 'Gandalf'), + 'Albert', 'chown Gandalf foobar Gandalfian')) + assert('Gandalf' == factions.get_owner('Gandalfian')) + + callback() +end) + +mtt.register('frontend functions: invite', function(callback) + assert(fccc(false, S("Permission denied: You can't use this command, @1 priv is needed.", + factions.priv), 'notExistingPlayer', 'invite')) + assert(fccc(false, S("Permission denied: You can't use this command, @1 priv is needed.", + factions.priv), 'Endor', 'invite')) + assert(fccc(false, S("Missing player name."), 'Albert', 'invite')) + assert(fccc(false, S("Missing faction name."), 'Albert', 'invite Endor')) + assert(fccc(false, S("Faction @1 doesn't exist.", 'notExistingFaction'), + 'Albert', 'invite Endor notExistingFaction')) + assert(fccc(false, S("Player @1 doesn't exist.", 'notExistingPlayer'), + 'Albert', 'invite notExistingPlayer Gandalfian')) + assert(fccc(false, S("Player @1 is already in faction @2.", 'Endor', 'Gandalfian'), + 'Albert', 'invite Endor Gandalfian')) + factions.mode_unique_faction = true + assert(fccc(false, S("Player @1 is already in faction @2.", 'Gandalf', 'Gandalfian'), + 'Albert', 'invite Gandalf Endorian')) + factions.mode_unique_faction = false + assert(fccc(true, S("@1 is now a member of faction @2.", 'Gandalf', 'Endorian'), + 'Albert', 'invite Gandalf Endorian')) + + callback() +end) + +mtt.register('final db checks', function(callback) + pd(factions.get_facts()) + + callback() +end) + +mtt.register('remaining players leave', function(callback) + assert(true == mtt.leave_player('Endor')) + assert(true == mtt.leave_player('HanSolo')) + + callback() +end) + +mtt.register('final', function(callback) + print('total success') + callback() +end)