Add the multi-factions mode to support multi factions

This commit is contained in:
Supergoat666 2020-07-28 22:47:50 +02:00
parent 95a35e2671
commit 15ec2ad3e1
2 changed files with 327 additions and 148 deletions

@ -3,6 +3,13 @@
A simple mod which allows player created factions. Not very useful on its own, it becomes very powerful when combined with other mods. A simple mod which allows player created factions. Not very useful on its own, it becomes very powerful when combined with other mods.
## Usage ## Usage
We can choose a mode : single or multi factions.
By default the mod is single faction, if we want to change, all it takes is to add a line `mode_unique_faction = false` into the mod.conf file
Below, parameters with [] are useful only with the multi-factions mode.
There is an admin privs to enable every functions for every faction : playerfactions_admin
These commands can be used by anyone: These commands can be used by anyone:
@ -10,14 +17,18 @@ These commands can be used by anyone:
- `/factions list`: List available factions - `/factions list`: List available factions
- `/factions info <faction>`: See information on a faction - `/factions info <faction>`: See information on a faction
- `/factions join <faction> <password>`: Join an existing faction - `/factions join <faction> <password>`: Join an existing faction
- `/factions leave`: Leave your faction - `/factions leave [faction]`: Leave your faction
These extra commands can only be used by faction owners: These extra commands can only be used by faction owners and someone with the playerfactions_admin priv:
- `/factions kick [faction] <player>`: Kick someone from your faction
- `/factions disband [faction]`: Disband your faction
- `/factions passwd [faction] <password>`: Change your faction's password
- `/factions chown [faction] <player>`: Transfer ownership of your faction
This commands can only be used by someone with the playerfactions_admin priv:
- `/factions invite <player> <faction>`: Add player to a faction
- `/factions kick <player>`: Kick someone from your faction
- `/factions disband`: Disband your faction
- `/factions passwd`: Change your faction's password
- `/factions chown`: Transfer ownership of your faction
## Translations ## Translations
@ -38,8 +49,17 @@ Additionally, `playerfactions` can optionally depend on the following mods:
I strongly recommend reading through the `init.lua` file; the functions at the top give you a pretty good idea of how to use it, but just in case you're short on time I'll list the most important functions below. I strongly recommend reading through the `init.lua` file; the functions at the top give you a pretty good idea of how to use it, but just in case you're short on time I'll list the most important functions below.
- `get_player_faction(player)`: Get the faction a player belongs to, `nil` if they haven't joined a faction - `get_facts()`: Get the table with all data. The structure is :
```{["name_of_faction1"]={
["owner"]=name_of_the_owner,
["members"]={["name_of_a_member1"]=true, ["name_of_a_member2"]=true}
}}
```
- `get_player_faction(player)`: Get a string with the faction a player belongs to, `nil` if they haven't joined a faction. In multi-faction mode, it will return the oldest faction which player is into. (It checks the facts variable from the top)
- `get_player_factions(player)`: Get a table with the faction(s) a player belongs to, `nil` if they haven't joined a faction : {name_of_faction1, name_of_faction2}
- `get_owner(faction)`: Get the owner of a faction - `get_owner(faction)`: Get the owner of a faction
- `chown(fname, owner)`: Change the owner of a faction
- `register_faction(faction, player, password)`: Create a new faction - `register_faction(faction, player, password)`: Create a new faction
- `disband_faction(faction)`: Disband a faction - `disband_faction(faction)`: Disband a faction
- `get_password(faction)`: Gets a faction's password - `get_password(faction)`: Gets a faction's password
@ -47,7 +67,7 @@ I strongly recommend reading through the `init.lua` file; the functions at the t
- `join_faction(faction, player)`: Sets the given player as belonging to this faction - `join_faction(faction, player)`: Sets the given player as belonging to this faction
- `leave_faction(player)`: Clears a player's faction - `leave_faction(player)`: Clears a player's faction
Note that none of these functions have any sanity checks (e.g. making sure factions exist), so I strongly recommend you read `init.lua` to determine how they are used. Otherwise, you could end up getting some pretty strange errors. Note that all of these functions have sanity checks : if faction or player does not exists, it return false. If operation succeed, it return true or the needed value.
## Acknowledgements ## Acknowledgements

375
init.lua

@ -1,5 +1,4 @@
minetest.register_privilege("playerfactions_admin", {description = "Authorize to use every /factions commands",give_to_singleplayer = false}) minetest.register_privilege("playerfactions_admin", {description = "Authorize to use every /factions commands",give_to_singleplayer = false})
-- Load support for intllib. -- Load support for intllib.
local MP = minetest.get_modpath(minetest.get_current_modname()) local MP = minetest.get_modpath(minetest.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua") local S, NS = dofile(MP.."/intllib.lua")
@ -8,11 +7,9 @@ local S, NS = dofile(MP.."/intllib.lua")
factions = {} factions = {}
local facts = {} local facts = {}
local storage = minetest.get_mod_storage() local storage = minetest.get_mod_storage()
if storage:get_string("facts") ~= "" then if storage:get_string("facts") ~= "" then
facts = minetest.deserialize(storage:get_string("facts")) facts = minetest.deserialize(storage:get_string("facts"))
end end
-- Fix factions -- Fix factions
for fname, fact in pairs(facts) do for fname, fact in pairs(facts) do
if fact.members == nil then if fact.members == nil then
@ -20,92 +17,131 @@ for fname, fact in pairs(facts) do
end end
end end
local modConf = io.open(minetest.get_modpath("playerfactions").."/mod.conf")
local content = modConf:read("*all")
local found, _ , mod_u = content:find("mode_unique_faction%s-=%s-(%S+)")
if found == nil then
factions.mode_unique_faction = true
elseif mod_u == "false" then
factions.mode_unique_faction = false
else
factions.mode_unique_faction = true
end
local function save_factions() local function save_factions()
storage:set_string("facts", minetest.serialize(facts)) storage:set_string("facts", minetest.serialize(facts))
end end
-- Data manipulation -- Data manipulation
function factions.get_player_faction(name) function factions.get_player_faction(name)
local player = minetest.get_player_by_name(name) if not minetest.player_exists(name) then
return false
-- Look in faction objects if we can't do the shortcut end
if player == nil then
for fname, fact in pairs(facts) do for fname, fact in pairs(facts) do
if fact.members[name] then if fact.members[name] then
return fname return fname
end end
end end
return nil return nil
end
-- Player is online, we can take a shortcut using the player object function factions.get_player_factions(name)
-- In the future, we can always do this; see https://github.com/minetest/minetest/issues/6193 if not minetest.player_exists(name) then
else return false
local faction = player:get_meta():get_string("faction") end
if faction == "" then local player_factions = {}
return nil for fname, fact in pairs(facts) do
else if fact.members[name] then
-- Check that faction still exists table.insert(player_factions, fname)
if facts[faction] == nil then end
factions.leave_faction(name) end
return nil return player_factions
else end
local found = false
if facts[faction].members[name] == nil then function factions.get_owned_factions(name)
facts[faction].members[name] = true local own_factions = {}
for fname, fact in pairs(facts) do
if minetest.get_player_privs(name).playerfactions_admin or fact.owner == name then
table.insert(own_factions, fname)
end
end
return own_factions
end
function factions.get_owner(fname)
if facts[fname] == nil then
return false
end
return facts[fname].owner
end
function factions.chown(fname, owner)
if facts[fname] == nil then
return false
end
facts[fname].owner = owner
save_factions() save_factions()
end return true
return faction
end
end
end
end
function factions.get_owner(name)
if facts[name] == nil then
return nil
else
return facts[name].owner
end
end
function factions.chown(name, owner)
facts[name].owner = owner
end end
function factions.register_faction(fname, founder, pw) function factions.register_faction(fname, founder, pw)
if facts[fname] ~= nil then
return false
end
facts[fname] = { facts[fname] = {
name = fname, name = fname,
owner = founder, owner = founder,
password = pw, password = pw,
members = {} members = {[founder] = true}
} }
save_factions() save_factions()
factions.join_faction(fname, founder) return true
end end
function factions.disband_faction(name) function factions.disband_faction(fname)
facts[name] = nil if facts[fname] == nil then
save_factions() return false
end
function factions.get_password(name)
return facts[name].password
end
function factions.set_password(name, password)
facts[name].password = password
end
function factions.join_faction(name, player)
minetest.get_player_by_name(player):get_meta():set_string("faction", name)
facts[name].members[player] = true
end
function factions.leave_faction(name)
minetest.get_player_by_name(name):get_meta():set_string("faction", "")
if facts[name] ~= nil then
facts[name].members[player] = nil
end end
facts[fname] = nil
save_factions()
return true
end
function factions.get_password(fname)
if facts[fname] == nil then
return false
end
return facts[fname].password
end
function factions.set_password(fname, password)
if facts[fname] == nil then
return false
end
facts[fname].password = password
save_factions()
return true
end
function factions.join_faction(fname, player)
if facts[fname] == nil or not minetest.player_exists(player) then
return false
end
facts[fname].members[player] = 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
return false
end
facts[fname].members[player_name] = nil
save_factions()
return true
end end
-- Chat commands -- Chat commands
@ -121,11 +157,13 @@ local function handle_command(name, param)
if action == "create" then if action == "create" then
local faction_name = params[2] local faction_name = params[2]
local password = params[3] local password = params[3]
if faction_name == nil then if factions.mode_unique_faction and factions.get_player_faction(name) ~= nil then
minetest.chat_send_player(name, S("You are already in a faction you can't create one"))
elseif faction_name == nil then
minetest.chat_send_player(name, S("Missing faction name")) minetest.chat_send_player(name, S("Missing faction name"))
elseif password == nil then elseif password == nil then
minetest.chat_send_player(name, S("Missing password")) minetest.chat_send_player(name, S("Missing password"))
elseif factions.get_owner(faction_name) ~= nil then elseif facts[faction_name] ~= nil then
minetest.chat_send_player(name, S("That faction already exists")) minetest.chat_send_player(name, S("That faction already exists"))
else else
factions.register_faction(faction_name, name, password) factions.register_faction(faction_name, name, password)
@ -133,19 +171,38 @@ local function handle_command(name, param)
return true return true
end end
elseif action == "disband" then elseif action == "disband" then
local faction_name = factions.get_player_faction(name) local password = nil
local password = params[2] local faction_name = nil
if faction_name == nil then local own_factions = factions.get_owned_factions(name)
minetest.chat_send_player(name, S("You are not in a faction")) local number_factions = #own_factions
if number_factions == 0 then
password = "No importance, player will not be abble because player is not the owner of no faction"
elseif #params == 1 then
faction_name = "No importance, player will not be abble because no password is given"
elseif #params == 2 and number_factions == 1 then
password = params[2]
faction_name = own_factions[1]
elseif #params >= 3 then
faction_name = params[2]
password = params[3]
end
if password == nil then
minetest.chat_send_player(name, S("Password is needed"))
elseif faction_name == nil then
if number_factions == 0 then
minetest.chat_send_player(name, S("You are the owner of no faction, you can't use this command"))
else
minetest.chat_send_player(name, S("You are the owner of many factions, you have to choose one of them : @1", table.concat(own_factions, ", ")))
end
elseif not facts[faction_name] then
minetest.chat_send_player(name, S("This faction doesn't exists"))
elseif name ~= factions.get_owner(faction_name) and not minetest.get_player_privs(name).playerfactions_admin then elseif name ~= factions.get_owner(faction_name) and not minetest.get_player_privs(name).playerfactions_admin then
minetest.chat_send_player(name, S("Permission denied")) minetest.chat_send_player(name, S("Permission denied : you are not the owner of this faction and don't have the privs playerfactions_admin."))
elseif password == nil then
minetest.chat_send_player(name, S("WARNING! This cannot be reversed! Run again with the password if you're absolutely certain"))
elseif password ~= factions.get_password(faction_name) then elseif password ~= factions.get_password(faction_name) then
print("wrong password") print("wrong password")
minetest.chat_send_player(name, S("Permission denied")) minetest.chat_send_player(name, S("Permission denied: wrong password"))
else else
factions.disband_faction(faction_name, name, name) factions.disband_faction(faction_name)
minetest.chat_send_player(name, S("Disbanded @1", faction_name)) minetest.chat_send_player(name, S("Disbanded @1", faction_name))
return true return true
end end
@ -155,7 +212,7 @@ local function handle_command(name, param)
table.insert(faction_list, k) table.insert(faction_list, k)
end end
if #faction_list ~= 0 then if #faction_list ~= 0 then
minetest.chat_send_player(name, "Factions("..#faction_list.."): "..table.concat(faction_list, ",")) minetest.chat_send_player(name, "Factions("..#faction_list.."): "..table.concat(faction_list, ", "))
else else
minetest.chat_send_player(name, S("There are no factions yet")) minetest.chat_send_player(name, S("There are no factions yet"))
end end
@ -187,78 +244,179 @@ local function handle_command(name, param)
elseif action == "join" then elseif action == "join" then
local faction_name = params[2] local faction_name = params[2]
local password = params[3] local password = params[3]
if factions.get_player_faction(name) ~= nil then if factions.get_player_faction(name) ~= nil and factions.mode_unique_faction then
minetest.chat_send_player(name, S("You are already in a faction")) minetest.chat_send_player(name, S("You are already in a faction"))
elseif factions.get_owner(faction_name) == nil then elseif facts[faction_name] == nil then
minetest.chat_send_player(name, S("The faction @1 doesn't exist", faction_name)) minetest.chat_send_player(name, S("The faction @1 doesn't exist", faction_name))
elseif factions.get_password(faction_name) ~= password then elseif factions.get_password(faction_name) ~= password then
minetest.chat_send_player(name, S("Permission denied")) minetest.chat_send_player(name, S("Permission denied : wrong password."))
else else
factions.join_faction(faction_name, name) if factions.join_faction(faction_name, name) then
minetest.chat_send_player(name, S("Joined @1", faction_name)) minetest.chat_send_player(name, S("Joined @1", faction_name))
return true return true
else
minetest.chat_send_player(name, S("Error on joining, please try again"))
return false
end
end end
elseif action == "leave" then elseif action == "leave" then
local faction_name = factions.get_player_faction(name) local player_factions = factions.get_player_factions(name)
local number_factions = table.getn(player_factions)
local faction_name = nil
if number_factions == 0 then
faction_name = nil
elseif #params == 1 and number_factions == 1 then
faction_name = player_factions[1]
elseif #params >= 1 and facts[params[2]] ~= nil then
faction_name = params[2]
end
if faction_name == nil then if faction_name == nil then
if number_factions == 0 then
minetest.chat_send_player(name, S("You are not in a faction")) minetest.chat_send_player(name, S("You are not in a faction"))
elseif factions.get_owner(faction_name) == name then elseif facts[params[2]] then
minetest.chat_send_player(name, S("You cannot leave your own faction")) minetest.chat_send_player(name, S("You are in many factions (@1), you must precise which one you want to leave", table.concat(player_factions, ", ")))
else else
factions.leave_faction(name) minetest.chat_send_player(name, "The given faction doesn't exists")
end
elseif factions.get_owner(faction_name) == name then
minetest.chat_send_player(name, S("You cannot leave your own faction, change owner or disband it."))
else
if factions.leave_faction(faction_name, name) then
minetest.chat_send_player(name, S("Left @1", faction_name)) minetest.chat_send_player(name, S("Left @1", faction_name))
return true return true
else
minetest.chat_send_player(name, S("Error on leaving the faction, please try again"))
return false
end
end end
elseif action == "kick" then elseif action == "kick" then
local faction_name = factions.get_player_faction(name) local target = nil
local target = params[2] local faction_name = nil
local own_factions = factions.get_owned_factions(name)
local number_factions = table.getn(own_factions)
if number_factions == 0 then
target = "No importance, the permission is denied"
elseif #params == 2 and number_factions == 1 then
target = params[2]
faction_name = own_factions[1]
elseif #params >= 3 then
faction_name = params[2]
target = params[3]
elseif facts[params[2]] ~= nil then
faction_name = "No importance, no target is given"
end
if faction_name == nil then if faction_name == nil then
minetest.chat_send_player(name, S("You are not in a faction")) if number_factions == 0 then
minetest.chat_send_player(name, S("You are the owner of no faction, you can't use this command"))
else
minetest.chat_send_player(name, S("You are the owner of many factions, you have to choose one of them : @1", table.concat(own_factions, ", ")))
end
elseif target == nil then elseif target == nil then
minetest.chat_send_player(name, S("Missing player name")) minetest.chat_send_player(name, S("Missing player name"))
elseif factions.get_owner(faction_name) ~= name and not minetest.get_player_privs(name).playerfactions_admin or factions.get_player_faction(target) ~= faction_name then elseif factions.get_owner(faction_name) ~= name and not minetest.get_player_privs(name).playerfactions_admin then
minetest.chat_send_player(name, S("Permission denied")) minetest.chat_send_player(name, S("Permission denied"))
elseif not facts[faction_name].members[target] then
minetest.chat_send_player(name, S("This player is not in the faction"))
elseif target == name then elseif target == name then
minetest.chat_send_player(name, S("You cannot kick yourself")) minetest.chat_send_player(name, S("You cannot kick yourself"))
else else
factions.leave_faction(name) if factions.leave_faction(faction_name, target) then
minetest.chat_send_player(name, S("Kicked @1 from faction", target)) minetest.chat_send_player(name, S("Kicked @1 from faction", target))
if target == factions.get_owner(faction_name) then
if factions.chown(faction_name, name) then
minetest.chat_send_player(name, S("@1 was the owner of the faction, the ownership of the faction belongs to you",target))
end
end
return true return true
else
minetest.chat_send_player(name, S("Error on kicking @1 from faction, please try again", target))
return false
end
end end
elseif action == "passwd" then elseif action == "passwd" then
local faction_name = factions.get_player_faction(name) local password = nil
local password = params[2] local faction_name = nil
local own_factions = factions.get_owned_factions(name)
local number_factions = table.getn(own_factions)
if #params == 1 then
faction_name = "No importance, there is no password"
elseif number_factions == 0 then
password = "No importance, player is the owner of no faction"
elseif #params == 2 and number_factions == 1 then
password = params[2]
faction_name = own_factions[1]
elseif #params >= 3 then
faction_name = params[2]
password = params[3]
elseif facts[params[2]] ~= nil then
faction_name = params[2]
end
if faction_name == nil then if faction_name == nil then
minetest.chat_send_player(name, S("You are not in a faction")) if number_factions == 0 then
minetest.chat_send_player(name, S("You are the owner of no faction, you can't use this command"))
else
minetest.chat_send_player(name, S("You are the owner of many factions, you have to choose one of them : @1", table.concat(own_factions, ", ")))
end
elseif password == nil then elseif password == nil then
minetest.chat_send_player(name, S("Missing password")) minetest.chat_send_player(name, S("Missing password"))
elseif factions.get_owner(faction_name) ~= name and not minetest.get_player_privs(name).playerfactions_admin then elseif factions.get_owner(faction_name) ~= name and not minetest.get_player_privs(name).playerfactions_admin then
minetest.chat_send_player(name, S("Permission denied")) minetest.chat_send_player(name, S("Permission denied"))
else else
factions.set_password(faction_name, password) if factions.set_password(faction_name, password) then
minetest.chat_send_player(name, S("Password has been updated")) minetest.chat_send_player(name, S("Password has been updated"))
return true return true
else
minetest.chat_send_player(name, S("Error, password didn't change, please try again"))
return false
end
end end
elseif action == "chown" then elseif action == "chown" then
local faction_name = factions.get_player_faction(name) local own_factions = factions.get_owned_factions(name)
local target = params[2] local number_factions = table.getn(own_factions)
local password = params[3] local faction_name = nil
local target = nil
local password = nil
if #params < 3 then
faction_name = "No importance, there is no target or no password"
if minetest.player_exists(params[2]) then
target = "No importance, there is no password"
end
elseif number_factions == 0 then
target = "No importance, player is owner of no faction"
password = "No importance, player is owner of no faction"
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[2]
target = params[3]
password = params[4]
end
if faction_name == nil then if faction_name == nil then
minetest.chat_send_player(name, S("You are not in a faction")) if number_factions == 0 then
minetest.chat_send_player(name, S("You are the owner of no faction, you can't use this command"))
else
minetest.chat_send_player(name, S("You are the owner of many factions, you have to choose one of them : @1", table.concat(own_factions, ", ")))
end
elseif target == nil then elseif target == nil then
minetest.chat_send_player(name, S("Missing player name")) minetest.chat_send_player(name, S("Missing player name"))
elseif factions.get_player_faction(target) ~= faction_name then
minetest.chat_send_player(name, S("@1 isn't in your faction", target))
elseif factions.get_owner(faction_name) ~= name and not minetest.get_player_privs(name).playerfactions_admin then
minetest.chat_send_player(name, S("Permission denied"))
elseif password == nil then elseif password == nil then
minetest.chat_send_player(name, S("WARNING! This cannot be reversed! Run again with the password if you're absolutely certain")) minetest.chat_send_player(name, S("Missing password"))
elseif name ~= factions.get_owner(faction_name) and not minetest.get_player_privs(name).playerfactions_admin then
minetest.chat_send_player(name, S("Permission denied: you're not the owner of this faction"))
elseif not facts[faction_name].members[target] then
minetest.chat_send_player(name, S("@1 isn't in your faction", target))
elseif password ~= factions.get_password(faction_name) then elseif password ~= factions.get_password(faction_name) then
minetest.chat_send_player(name, S("Permission denied")) minetest.chat_send_player(name, S("Permission denied: wrong password"))
else else
factions.chown(faction_name, target) if factions.chown(faction_name, target) then
minetest.chat_send_player(name, S("Ownership has been transferred to @1", target)) minetest.chat_send_player(name, S("Ownership has been transferred to @1", target))
return true return true
else
minetest.chat_send_player(name, S("Error, the owner didn't change, please verify parameters and try again"))
return false
end
end end
elseif action == "invite" then elseif action == "invite" then
if not minetest.get_player_privs(name).playerfactions_admin then if not minetest.get_player_privs(name).playerfactions_admin then
@ -270,7 +428,7 @@ local function handle_command(name, param)
minetest.chat_send_player(name, "The faction doesn't exist") minetest.chat_send_player(name, "The faction doesn't exist")
elseif not minetest.player_exists(target) then elseif not minetest.player_exists(target) then
minetest.chat_send_player(name, "The player doesn't exist") minetest.chat_send_player(name, "The player doesn't exist")
elseif factions.get_player_faction(target) ~= nil then elseif factions.mode_unique_faction and factions.get_player_faction(target) ~= nil then
minetest.chat_send_player(name, S("The player is already in the faction \"@1\"",factions.get_player_faction(target))) minetest.chat_send_player(name, S("The player is already in the faction \"@1\"",factions.get_player_faction(target)))
else else
if factions.join_faction(faction_name, target) then if factions.join_faction(faction_name, target) then
@ -294,10 +452,11 @@ minetest.register_chatcommand("factions", {
.."info <faction>: "..S("See information on a faction").."\n" .."info <faction>: "..S("See information on a faction").."\n"
.."join <faction> <password>: "..S("Join an existing faction").."\n" .."join <faction> <password>: "..S("Join an existing faction").."\n"
.."leave: "..S("Leave your faction").."\n" .."leave: "..S("Leave your faction").."\n"
.."kick <player>: "..S("Kick someone from your faction").."\n" .."kick [faction] <player>: "..S("Kick someone from your faction or from the given faction").."\n"
.."disband: "..S("Disband your faction").."\n" .."disband [faction] <password>: "..S("Disband your faction or the given faction").."\n"
.."passwd <password>: "..S("Change your faction's password").."\n" .."passwd [faction] <password>: "..S("Change your faction's password or the password of the given faction").."\n"
.."chown <player>:"..S("Transfer ownership of your faction").."\n", .."chown [faction] <player>:"..S("Transfer ownership of your faction").."\n"
.."invite <player> <faction>:"..S("Add player to a faction, you need factionsplayer_admin privs").."\n",
description = "", description = "",
privs = {}, privs = {},