489 lines
10 KiB
Lua
489 lines
10 KiB
Lua
fren = {}
|
|
frenemies = fren
|
|
|
|
fren.friends = {}
|
|
fren.friend_color = "#00FF00"
|
|
|
|
fren.enemies = {}
|
|
fren.enemy_color = "#FF0000"
|
|
|
|
fren.neutral_color = "#FFFFFF"
|
|
|
|
fren.groups = {}
|
|
|
|
|
|
--[[
|
|
|
|
storage:
|
|
player is {level = n, color = ""}
|
|
group is {name = "", level = n, color = "", members = {"", ""}}
|
|
|
|
available:
|
|
player is {level = n, color = "", groups = {"", ""}}
|
|
group is {name = "", level = n, color = "", members = {"", ""}}
|
|
|
|
qualify(player)
|
|
name_of(qualified)
|
|
|
|
is_enemy(player)
|
|
is_friend(player)
|
|
is_current_player(player)
|
|
is_neutral(player)
|
|
|
|
player_color(player)
|
|
player_groups(player)
|
|
player_level(player)
|
|
|
|
get_online_players()
|
|
get_online_friends()
|
|
get_online_enemies()
|
|
get_online_neutrals()
|
|
get_online_group(group)
|
|
|
|
if a player name contains @ it is a fully qualified name
|
|
|
|
|
|
.friend and .enemy should support level
|
|
|
|
.friend player <color> -- make player friend or recolor them
|
|
.fr player
|
|
.unfriend player
|
|
.unfr player
|
|
|
|
.enemy player <color> -- make player enemy or recolor them
|
|
.en player
|
|
.unenemy player
|
|
.unen player
|
|
|
|
.group name <color> -- create or recolor group
|
|
.rm_group name
|
|
.gadd group player
|
|
.grm group player
|
|
|
|
.lfriends
|
|
.lenemies
|
|
.lgroup group
|
|
|
|
.lfriends_all
|
|
.lenemies_all
|
|
.lgroup_all group
|
|
|
|
maybe groups should be qualified per server?
|
|
--]]
|
|
|
|
|
|
local storage = minetest.get_mod_storage()
|
|
|
|
-- should remove groups
|
|
function fren.serialize()
|
|
return minetest.write_json({
|
|
friends = fren.friends,
|
|
friend_color = fren.friend_color,
|
|
enemies = fren.enemies,
|
|
enemy_color = fren.enemy_color,
|
|
neutral_color = fren.neutral_color,
|
|
groups = fren.groups
|
|
})
|
|
end
|
|
|
|
-- should relate groups
|
|
function fren.deserialize(str)
|
|
local des = minetest.parse_json(str)
|
|
if des then
|
|
fren.friends = des.friends or {}
|
|
fren.friend_color = des.friend_color
|
|
fren.enemies = des.enemies or {}
|
|
fren.enemy_color = des.enemy_color
|
|
fren.neutral_color = des.neutral_color
|
|
fren.groups = des.groups or {}
|
|
end
|
|
end
|
|
|
|
function fren.store()
|
|
storage:set_string("data", fren.serialize())
|
|
end
|
|
|
|
function fren.load()
|
|
local d = storage:get("data")
|
|
if d then
|
|
fren.deserialize(d)
|
|
end
|
|
end
|
|
|
|
fren.load()
|
|
|
|
local server_info = minetest.get_server_info()
|
|
|
|
function fren.qualify(player)
|
|
local name = server_info.ip
|
|
|
|
if server_info.address ~= "" then
|
|
name = server_info.address
|
|
end
|
|
|
|
return player .. "@" .. name .. ":" .. server_info.port
|
|
end
|
|
|
|
function fren.name_of(qualified)
|
|
return qualified:match("(.-)@")
|
|
end
|
|
|
|
function fren.on_server(name)
|
|
local qname=fren.qualify(name)
|
|
for k,v in pairs(fren.friends) do
|
|
if k == qname then return true end
|
|
end
|
|
for k,v in pairs(fren.enemies) do
|
|
if k == qname then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
|
|
-- player required, color/level optional
|
|
function fren.friend(player, color, level)
|
|
local n = fren.qualify(player)
|
|
|
|
fren.friends[n] = {placeholder = true} -- true because the way Minetest serializes Json replaces {} with null
|
|
fren.friends[n].color = color
|
|
fren.friends[n].level = level
|
|
|
|
fren.store()
|
|
end
|
|
|
|
function fren.unfriend(player)
|
|
fren.friends[fren.qualify(player)] = nil
|
|
|
|
fren.store()
|
|
end
|
|
|
|
function fren.enemy(player, color, level)
|
|
local n = fren.qualify(player)
|
|
|
|
fren.enemies[n] = {placeholder = true}
|
|
fren.enemies[n].color = color
|
|
fren.enemies[n].level = level
|
|
|
|
fren.store()
|
|
end
|
|
|
|
function fren.unenemy(player)
|
|
fren.enemies[fren.qualify(player)] = nil
|
|
|
|
fren.store()
|
|
end
|
|
|
|
|
|
function fren.group(name, color, level)
|
|
fren.groups[name] = {placeholder = true}
|
|
fren.groups[name].color = color
|
|
fren.groups[name].level = level
|
|
|
|
fren.store()
|
|
end
|
|
|
|
function fren.remove_group(name)
|
|
fren.groups[name] = nil
|
|
|
|
fren.store()
|
|
end
|
|
|
|
function fren.group_add_player(group, player, level)
|
|
if fren.groups[group] then
|
|
local q = fren.qualify(player)
|
|
fren.groups[group].members[q] = {placeholder = true}
|
|
fren.groups[group].members[q].level = level
|
|
|
|
fren.store()
|
|
end
|
|
end
|
|
|
|
function fren.group_remove_player(group, player)
|
|
if fren.groups[group] then
|
|
fren.groups[group].members[fren.qualify(player)] = nil
|
|
|
|
fren.store()
|
|
end
|
|
end
|
|
|
|
|
|
function fren.is_enemy(player)
|
|
return fren.enemies[fren.qualify(player)] ~= nil
|
|
end
|
|
|
|
function fren.is_friend(player)
|
|
return fren.friends[fren.qualify(player)] ~= nil
|
|
end
|
|
|
|
function fren.is_neutral(player)
|
|
return not fren.is_enemy(player) and not fren.is_friend(player)
|
|
end
|
|
|
|
function fren.is_current_player(player)
|
|
return player == minetest.locaplayer:get_name()
|
|
end
|
|
|
|
function fren.in_group(player, group)
|
|
if fren.groups[group] then
|
|
return fren.groups[group].members[fren.qualify(player)] ~= nil
|
|
end
|
|
end
|
|
|
|
-- maybe groups should be involved?
|
|
function fren.color(player)
|
|
local q = fren.qualify(player)
|
|
|
|
if fren.is_friend(player) then
|
|
return fren.friends[q].color or fren.friend_color
|
|
elseif fren.is_enemy(player) then
|
|
return fren.enemies[q].color or fren.enemy_color
|
|
else
|
|
return friend.neutral_color
|
|
end
|
|
end
|
|
|
|
|
|
-- should be a setting
|
|
local check_interval = 1
|
|
|
|
local online_cached = {}
|
|
local online_cached_last = 0
|
|
|
|
local friend_online_cached = {}
|
|
local friend_online_cached_last = 0
|
|
|
|
local enemy_online_cached = {}
|
|
local enemy_online_cached_last = 0
|
|
|
|
local neutral_online_cached = {}
|
|
local neutral_online_cached_last = 0
|
|
|
|
local group_online_cached = {}
|
|
local group_online_cached_last = {}
|
|
|
|
local function is_time(epoch)
|
|
if epoch == nil then
|
|
return true
|
|
end
|
|
|
|
return os.clock() - epoch >= check_interval
|
|
end
|
|
|
|
local function uniq(l)
|
|
local o = {}
|
|
local oi = 1
|
|
local last
|
|
|
|
for i, v in ipairs(l) do
|
|
if last ~= v then
|
|
o[oi] = v
|
|
oi = oi + 1
|
|
end
|
|
|
|
last = v
|
|
end
|
|
|
|
return o
|
|
end
|
|
|
|
function fren.get_online_players()
|
|
if is_time(online_cached_last) then
|
|
online_cached_last = os.clock()
|
|
|
|
online_cached = minetest.get_player_names()
|
|
table.sort(online_cached)
|
|
online_cached = uniq(online_cached)
|
|
end
|
|
|
|
return online_cached
|
|
end
|
|
|
|
local function filter(filter, source)
|
|
local o = {}
|
|
|
|
for k, v in pairs(source) do
|
|
if filter(v) then
|
|
o[k] = v
|
|
end
|
|
end
|
|
|
|
return o
|
|
end
|
|
|
|
function fren.get_online_friends()
|
|
if is_time(friend_online_cached_last) then
|
|
friend_online_cached_last = os.clock()
|
|
|
|
friend_online_cached = filter(fren.is_friend, fren.get_online_players())
|
|
end
|
|
|
|
return friend_online_cached
|
|
end
|
|
|
|
function fren.get_all_friends()
|
|
if is_time(friend_online_cached_last) then
|
|
friend_online_cached_last = os.clock()
|
|
|
|
friend_online_cached = filter(fren.is_friend, fren.get_online_players())
|
|
end
|
|
|
|
return friend_online_cached
|
|
end
|
|
|
|
function fren.get_online_enemies()
|
|
if is_time(enemy_online_cached_last) then
|
|
enemy_online_cached_last = os.clock()
|
|
|
|
enemy_online_cached = filter(fren.is_enemy, fren.get_online_players())
|
|
end
|
|
|
|
return enemy_online_cached
|
|
end
|
|
|
|
function fren.get_online_neutrals()
|
|
if is_time(neutral_online_cached_last) then
|
|
neutral_online_cached_last = os.clock()
|
|
|
|
neutral_online_cached = filter(fren.is_neutral, fren.get_online_players())
|
|
end
|
|
|
|
return neutral_online_cached
|
|
end
|
|
|
|
function fren.get_online_group(group)
|
|
if is_time(group_online_cached_last[group]) then
|
|
group_online_cached_last[group] = os.clock()
|
|
|
|
group_online_cached[group] = filter(
|
|
function(v)
|
|
return fren.in_group(v, group)
|
|
end, fren.get_online_players())
|
|
end
|
|
|
|
return group_online_cached[group]
|
|
end
|
|
|
|
|
|
-- first second [opt_third]
|
|
-- converts to
|
|
-- {
|
|
-- [1] = {name = "first", required = true},
|
|
-- [2] = {name = "second", required = true},
|
|
-- [3] = {name = "opt_third", required = false}
|
|
-- }
|
|
local function parse_opts(str)
|
|
local o = {}
|
|
local opts = string.split(str, " ")
|
|
|
|
for i, v in ipairs(opts) do
|
|
if v:match("%[(.-)%]") then
|
|
o[i] = {name = v, required = false}
|
|
else
|
|
o[i] = {name = v, required = true}
|
|
end
|
|
end
|
|
|
|
return o
|
|
end
|
|
|
|
-- first second [opt_third]
|
|
-- returns {first = a, second = a, opt_third = a/nil} or nil if parsing failed
|
|
local function parse_args(str, args)
|
|
local opts = parse_opts(str)
|
|
local splargs = string.split(args, " ")
|
|
local parsed = {}
|
|
|
|
for i, v in ipairs(opts) do
|
|
if splargs[i] then
|
|
parsed[v.name] = splargs[i]
|
|
elseif v.required then
|
|
minetest.display_chat_message("Error: argument '" .. v.name .. "' is required.")
|
|
return nil
|
|
else
|
|
break
|
|
end
|
|
end
|
|
|
|
return parsed
|
|
end
|
|
|
|
|
|
minetest.register_chatcommand("friend", {
|
|
description = "Add a player as a friend.",
|
|
params = "<player> <?color>",
|
|
func = function(params)
|
|
local args = parse_args("player [color]", params)
|
|
if args then
|
|
fren.friend(args.player, args.color)
|
|
end
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("unfriend", {
|
|
description = "Remove player from friend list.",
|
|
params = "<player>",
|
|
func = function(params)
|
|
local args = parse_args("player", params)
|
|
if args then
|
|
fren.unfriend(args.player)
|
|
end
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("enemy", {
|
|
description = "Add player as an enemy.",
|
|
params = "<player> <?color>",
|
|
func = function(params)
|
|
local args = parse_args("player [color]", params)
|
|
if args then
|
|
fren.enemy(args.player, args.color)
|
|
end
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("unenemy", {
|
|
description = "Remove player from enemy list.",
|
|
params = "<player>",
|
|
func = function(params)
|
|
local args = parse_args("player", params)
|
|
if args then
|
|
fren.unenemy(args.player)
|
|
end
|
|
end
|
|
})
|
|
|
|
|
|
local function lcat(l)
|
|
return table.concat(l, ", ")
|
|
end
|
|
|
|
local function displist(l)
|
|
minetest.display_chat_message(lcat(l))
|
|
end
|
|
|
|
|
|
minetest.register_chatcommand("lfriends", {
|
|
description = "List online friends.",
|
|
func = function()
|
|
displist(fren.get_online_friends())
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("lenemies", {
|
|
description = "List online enemies.",
|
|
func = function()
|
|
displist(fren.get_online_enemies())
|
|
end
|
|
})
|
|
|
|
minetest.register_chatcommand("lgroup", {
|
|
description = "List online members of a group.",
|
|
params = "<group>",
|
|
func = function(params)
|
|
local args = parse_args("group", params)
|
|
if args then
|
|
displist(fren.get_online_group(args.group))
|
|
end
|
|
end
|
|
})
|