playerfactions/init.lua

601 lines
19 KiB
Lua
Raw Normal View History

-- Translation support
local S = minetest.get_translator("playerfactions")
2024-09-16 15:54:00 +02:00
-- Global factions table
factions = {}
2024-09-16 16:31:27 +02:00
-- 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)
2023-04-14 18:35:31 +02:00
minetest.register_on_mods_loaded(function()
if not minetest.registered_privileges[factions.priv] then
minetest.register_privilege(factions.priv, {
2023-04-14 18:35:31 +02:00
description = S("Allow the use of all playerfactions commands"),
give_to_singleplayer = false
})
end
end)
2024-09-18 13:04:07 +02:00
-- 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
2024-09-16 15:54:00 +02:00
-- Data
2019-09-09 04:11:55 +02:00
local facts = {}
local storage = minetest.get_mod_storage()
2019-09-09 23:59:09 +02:00
if storage:get_string("facts") ~= "" then
2019-09-09 04:11:55 +02:00
facts = minetest.deserialize(storage:get_string("facts"))
end
2019-09-09 04:11:55 +02:00
local function save_factions()
storage:set_string("facts", minetest.serialize(facts))
end
2020-08-22 00:59:29 +02:00
local function table_copy(data)
local copy = {}
if type(data) == "table" then
2024-09-16 16:31:27 +02:00
for k, v in pairs(data) do
2020-08-22 00:59:29 +02:00
copy[k]=table_copy(v)
end
return copy
else
return data
end
end
2024-09-16 16:31:27 +02:00
-- Data manipulation (API)
function factions.get_facts()
2020-08-22 00:59:29 +02:00
return table_copy(facts)
end
function factions.player_is_in_faction(faction_name, player_name)
2024-09-16 16:31:27 +02:00
if not (facts[faction_name] and minetest.player_exists(player_name)) then
return false
2020-08-18 11:25:28 +02:00
end
return facts[faction_name].members[player_name]
end
function factions.get_player_faction(player_name)
2024-09-16 16:31:27 +02:00
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
for faction_name, fact in pairs(facts) do
if fact.members[player_name] then
return faction_name
end
end
return nil
end
2020-07-28 22:29:38 +02:00
function factions.get_player_factions(player_name)
if not minetest.player_exists(player_name) then
return false
end
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 0 < table.getn(player_factions) and player_factions or false
end
2020-07-28 22:29:38 +02:00
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)
2019-09-09 04:11:55 +02:00
end
end
return 0 < table.getn(owned_factions) and owned_factions or false
2019-09-09 04:11:55 +02:00
end
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 0 < table.getn(adm_factions) and adm_factions or false
end
function factions.get_members(faction_name)
if not facts[faction_name] then
return false
end
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.get_owner(faction_name)
if not facts[faction_name] then
return false
2019-09-09 04:11:55 +02:00
end
return facts[faction_name].owner
2019-09-09 04:11:55 +02:00
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(faction_name, player_name, password)
2024-09-18 03:29:21 +02:00
if not ('string' == type(faction_name) and 'string' == type(player_name)
and 'string' == type(password)) then
return false
end
2024-09-16 16:29:02 +02:00
if facts[faction_name] then
return false
end
facts[faction_name] = {
name = faction_name,
owner = player_name,
password256 = factions.hash_password(password),
members = { [player_name] = true }
2019-09-09 04:11:55 +02:00
}
save_factions()
return true
2019-09-09 04:11:55 +02:00
end
function factions.disband_faction(faction_name)
if not facts[faction_name] then
return false
end
facts[faction_name] = nil
2019-09-09 04:11:55 +02:00
save_factions()
2024-09-18 13:04:07 +02:00
for _, hook in ipairs(disband_hooks) do
hook(faction_name)
end
return true
2019-09-09 04:11:55 +02:00
end
function factions.hash_password(password)
return minetest.sha256(password)
end
function factions.valid_password(faction_name, password)
if not facts[faction_name] or not password then
return false
end
return factions.hash_password(password) == facts[faction_name].password256
end
function factions.get_password()
minetest.log("warning", "Deprecated use of factions.get_password(). "
.. "Please update to using factions.valid_password() instead.")
return nil
2019-09-09 04:11:55 +02:00
end
function factions.set_password(faction_name, password)
2024-09-16 16:31:09 +02:00
if not (facts[faction_name] and 'string' == type(password)) then
return false
end
facts[faction_name].password256 = factions.hash_password(password)
save_factions()
return true
2019-09-09 04:11:55 +02:00
end
function factions.join_faction(faction_name, player_name)
2024-09-16 16:31:09 +02:00
if not (facts[faction_name] and 'string' == type(player_name)
and minetest.player_exists(player_name)) then
return false
end
facts[faction_name].members[player_name] = true
save_factions()
return true
2019-09-09 04:11:55 +02:00
end
function factions.leave_faction(faction_name, player_name)
2024-09-16 16:31:09 +02:00
if not (facts[faction_name] and 'string' == type(player_name)
and minetest.player_exists(player_name)) then
return false
end
facts[faction_name].members[player_name] = nil
save_factions()
return true
2019-09-09 04:11:55 +02:00
end
-- Chat commands
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)
2019-09-09 04:11:55 +02:00
end
end
function cc.disband(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 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.",
2024-09-18 03:29:21 +02:00
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)
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]
2019-09-09 04:11:55 +02:00
else
return false, S(
"You are in multiple factions, you have to choose one of them: @1.",
2024-09-18 03:29:21 +02:00
table.concat(player_factions, ", "))
2024-09-15 10:08:26 +02:00
end
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) }
2019-09-09 04:11:55 +02:00
end
local summary = S("Name: @1\nOwner: @2\nMembers: @3",
faction_name, factions.get_owner(faction_name),
table.concat(faction_members, ", "))
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.",
2024-09-16 16:28:13 +02:00
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.",
2024-09-18 03:29:21 +02:00
player_name, table.concat(owned_factions, ", "))
else
summary = summary .. "\n" .. S(
"@1 doesn't own any factions.", player_name)
end
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.",
2024-09-18 03:29:21 +02:00
player_name, factions.priv)
end
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)
2019-09-09 04:11:55 +02:00
else
return false, S("Error joining faction.")
2019-09-09 04:11:55 +02:00
end
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
return false, S(
"You are in multiple factions, you have to choose one of them: @1.",
2024-09-18 03:29:21 +02:00
table.concat(player_factions, ", "))
end
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)
2019-09-09 05:30:31 +02:00
else
return false, S("Error leaving faction.")
2019-09-09 05:30:31 +02:00
end
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.",
2024-09-18 03:29:21 +02:00
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 <player> <password> [<faction>]' "
.. "to change the ownership.")
else
if factions.leave_faction(faction_name, target_name) then
return true, S("Kicked @1 from faction.", target_name)
else
2024-09-17 23:31:37 +02:00
-- SwissalpS is quite positive that this portion is unreachable
return false, S("Error kicking @1 from faction.", target_name)
end
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.",
2024-09-18 03:29:21 +02:00
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
return false, S("Failed to change password.")
end
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.",
2024-09-18 03:29:21 +02:00
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
return false, S("Failed to transfer ownership.")
end
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.",
2024-09-18 03:29:21 +02:00
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)
2024-09-17 23:32:01 +02:00
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])
2019-09-18 22:52:14 +02:00
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
2024-09-17 23:31:37 +02:00
-- is this portion ever reachable at all?
return false, S("Error adding @1 to @2.", target_name, faction_name)
end
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.")
2019-09-09 04:11:55 +02:00
end
local not_admin = not minetest.get_player_privs(player_name)[factions.priv]
return cc[action:lower()](player_name, params, not_admin)
2019-09-09 04:11:55 +02:00
end
2019-09-09 05:30:31 +02:00
minetest.register_chatcommand("factions", {
2024-09-16 16:31:27 +02:00
params = "create <faction> <password>: " .. S("Create a new faction") .. "\n"
.. "list: " .. S("List available factions") .. "\n"
.. "info [<faction>]: " .. S("See information about a faction") .. "\n"
.. "player_info [<player>]: " .. S("See information about a player") .. "\n"
.. "join <faction> <password>: " .. S("Join an existing faction") .. "\n"
.. "leave [<faction>]: " .. S("Leave your faction") .. "\n"
.. "kick <player> [<faction>]: "
.. S("Kick someone from your faction or from the given faction") .. "\n"
.. "disband <password> [<faction>]: "
.. S("Disband your faction or the given faction") .. "\n"
.. "passwd <password> [<faction>]: "
.. S("Change your faction's password or the password of the given faction") .." \n"
.. "chown <player> <password> [<faction>]: "
.. S("Transfer ownership of your faction") .. "\n"
.. "invite <player> <faction>: "
.. S("Add player to a faction, you need @1 priv", factions.priv) .. "\n",
2020-07-28 22:29:38 +02:00
2019-09-09 04:11:55 +02:00
description = "",
privs = {},
func = handle_command
})
-- Fix factions
do
local save_needed = false
for _, fact in pairs(facts) do
if not fact.members then
2024-09-16 01:49:38 +02:00
fact.members = {
[fact.owner] = true
}
end
if fact.password then
fact.password256 = factions.hash_password(fact.password)
fact.password = nil
save_needed = true
end
end
if save_needed then
save_factions()
end
end
2024-09-11 10:51:57 +02:00
2024-09-16 01:38:54 +02:00
-- Integration testing
if minetest.get_modpath("mtt") and mtt.enabled then
2024-09-16 23:22:14 +02:00
factions.S = S
2024-09-16 01:38:54 +02:00
factions.handle_command = handle_command
2024-09-16 01:00:34 +02:00
dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/mtt.lua")
end
2024-09-11 10:51:57 +02:00
print("[playerfactions] loaded")