From 3bc2fd1c86ce8af983e2f5dc1b8f9db1147d5762 Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Tue, 12 Nov 2019 20:29:46 +0200 Subject: [PATCH] Kits, better godmode, permissions per warp --- ess/init.lua | 3 + ess/kits.lua | 215 +++++++++++++++++++++++++++++++++++++++++++ ess/player.lua | 104 ++++++++++++--------- ess/settingtypes.txt | 16 +++- ess/warp.lua | 46 +++++---- 5 files changed, 319 insertions(+), 65 deletions(-) create mode 100644 ess/kits.lua diff --git a/ess/init.lua b/ess/init.lua index aafd569..766c034 100644 --- a/ess/init.lua +++ b/ess/init.lua @@ -13,3 +13,6 @@ dofile(modpath.."/warp.lua") -- Player related commands dofile(modpath.."/player.lua") + +-- Kits +dofile(modpath.."/kits.lua") diff --git a/ess/kits.lua b/ess/kits.lua new file mode 100644 index 0000000..e3fbd42 --- /dev/null +++ b/ess/kits.lua @@ -0,0 +1,215 @@ + +local kits_cache = {} +local kits_give_timers = {} +local path = minetest.get_worldpath() + +local kit_privs = minetest.settings:get_bool("ess_privilege_per_kit", false) +local kit_give = minetest.settings:get("ess_default_kit") +if not kit_give then kit_give = "" end + +local function load_from_file() + local file = io.open(path.."/kits.ref") + if not file then return end + + local str = "" + for line in file:lines() do + str = str..line + end + + file:close() + kits_cache = minetest.deserialize(str) + if not kits_cache then + kits_cache = {} + end + + for kit in pairs(kits_cache) do + minetest.register_privilege("ess.kits.kit." .. kit, { + description = "Kit " .. kit, + give_to_singleplayer = false, + }) + end +end + +local function write_to_file() + local data = minetest.serialize(kits_cache) + minetest.safe_file_write(path.."/kits.ref", data) +end + +local function give_kit(player, kit) + kit = kit:lower() + if not kits_cache[kit] then return false, "Invalid kit name." end + if type(player) == "string" then + player = minetest.get_player_by_name(player) + end + + if not player then return false, "Invalid player." end + + local pname = player:get_player_name() + local allprivs = ess.priv_match(pname, "ess.kits.all") + if kit_privs and not ess.priv_match(pname, "ess.kits.kit." .. kit) and + not allprivs and kit ~= kit_give then + return false, "Insufficient permissions." + end + + local kdata = kits_cache[kit] + + if kits_give_timers[pname] and kits_give_timers[pname][kit] and kdata._timeout and + kits_give_timers[pname][kit] > minetest.get_gametime() - kdata._timeout and not allprivs then + return false, string.format("You have to wait %d more seconds before giving this kit again.", + (kdata._timeout + (kits_give_timers[pname][kit] - minetest.get_gametime()))) + end + + local inv = player:get_inventory() + local stacks_to_discard = {} + for i,tbl in pairs(kdata) do + if i ~= "_timeout" then + local stack = ItemStack(tbl) + if inv:room_for_item("main", stack) then + inv:add_item("main", stack) + else + table.insert(stacks_to_discard, stack) + end + end + end + + if not kits_give_timers[pname] then + kits_give_timers[pname] = {} + end + kits_give_timers[pname][kit] = minetest.get_gametime() + + return true, stacks_to_discard +end + +local function cmd_create(name, param, splitparams) + if param == "" or kits_cache[param] then + return false, "Invalid name for kit." + end + + local player = minetest.get_player_by_name(name) + local inv = player:get_inventory() + local stacks = {} + for _,stack in pairs(inv:get_list("main")) do + if not stack:is_empty() then + table.insert(stacks, stack:to_table()) + end + end + + if #stacks == 0 then + return false, "Please put some items in your inventory before attempting to create a kit!" + end + + local timeout = tonumber(splitparams[2]) + local kitname = splitparams[1]:lower() + + kits_cache[kitname] = stacks + + if timeout then + kits_cache[kitname]._timeout = timeout + end + + write_to_file() + + return true, "Successfully created the kit \""..kitname.."\"!" +end + +local function cmd_delete(name, param) + if param == "" or not kits_cache[param] then + return false, "Invalid name for kit." + end + + kits_cache[param] = nil + write_to_file() + + return true, "Successfully removed the kit." +end + +local function cmd_kits(name, param, splitparams) + -- Include convenience functions in this command + local fst = splitparams[1] + if fst == "create" or fst == "make" or fst == "remove" or fst == "delete" then + if not ess.priv_match(name, "ess.kits.create") then + return ess.reject_permission() + end + + if fst == "create" or fst == "make" then + return cmd_create(name, splitparams[2], {splitparams[2], splitparams[3]}) + else + return cmd_delete(name, splitparams[2], {splitparams[2], splitparams[3]}) + end + end + + -- Give a specific kit + if param ~= "" then + local target = name + if splitparams[2] and splitparams[2] ~= name and not ess.priv_match(name, "ess.kits.other") then + return ess.reject_permission() + end + + local give, leftover = give_kit(target, param) + if not give then + return false, "Could not give kit. " .. leftover + end + + return true, "Kit given successfully." + end + + -- List available kits + local available = {} + for kit in pairs(kits_cache) do + local perms = true + if kit_privs then + perms = ess.priv_match(name, "ess.kits.kit." .. kit) or ess.priv_match(name, "ess.kits.all") + end + + if perms then + table.insert(available, kit) + end + end + + return true, "Kits: " .. table.concat(available, ", ") +end + +local commands = { + ["kits"] = { + description = "Give or list available kits", + params = "( (delete | create) [] ) | ( [] [] )", + aliases = {"kit"}, + privs = { + ["ess.kits"] = true, + ["ess.kits.all"] = true, + ["ess.kits.other"] = true, + }, + func = cmd_kits, + }, + ["createkit"] = { + description = "Create a kit from the items in your inventory", + params = " []", + aliases = {"mkkit", "addkit"}, + privs = { + ["ess.kits.create"] = true, + }, + func = cmd_create, + }, + ["deletekit"] = { + description = "Delete a kit", + params = "", + aliases = {"rmkit", "delkit"}, + privs = { + ["ess.kits.create"] = true, + }, + func = cmd_delete, + } +} + +ess.autoregister(commands, "kits") + +local loaded = false + +if kit_give ~= "" then + minetest.register_on_newplayer(function(player) + give_kit(player, kit_give) + end) +end + +-- Load for the first time +load_from_file() diff --git a/ess/player.lua b/ess/player.lua index 427be57..12cba31 100644 --- a/ess/player.lua +++ b/ess/player.lua @@ -26,10 +26,6 @@ if not newplayer then newplayer = "%s has joined the server for the first time! Welcome!" end --- Debug - -local globalstepskip = minetest.settings:get_bool("ess_skip_globalstep", false) - -- Commands local function cmd_god(name, params, splitparams) @@ -38,18 +34,30 @@ local function cmd_god(name, params, splitparams) target = splitparams[1] end - if not player_cache[target] then + local player = minetest.get_player_by_name(target) + if not player then return false, "No such player." end + local grps = player:get_armor_groups() local status = not player_cache[target].god + + if grps["immortal"] == 1 then + status = false + end + player_cache[target].god = status local msg = "God mode enabled." + local grp = 1 if not status then + grp = 0 msg = "God mode disabled." end + grps["immortal"] = grp + player:set_armor_groups(grps) + if target ~= name then minetest.chat_send_player(target, msg) end @@ -60,6 +68,13 @@ local function cmd_god(name, params, splitparams) end local commands = { + ["god"] = { + description = "Toggle invincibility mode.", + aliases = {"godmode", "invincible"}, + privs = true, + params = "[]", + func = cmd_god + }, ["whois"] = { description = "Get network information of player", params = "[]", @@ -108,6 +123,8 @@ local commands = { } } +ess.autoregister(commands, "player") + -- AFK kick immunity privilege minetest.register_privilege("ess.player.afk", { description = "AFK kick immunity", @@ -115,62 +132,49 @@ minetest.register_privilege("ess.player.afk", { give_to_admin = true, }) -if not globalstepskip then - commands["god"] = { - description = "Toggle invincibility mode.", - aliases = {"godmode", "invincible"}, - privs = true, - params = "[]", - func = cmd_god - } - +if afk_check then local check_timer = 0 minetest.register_globalstep(function(dtime) - -- Loop through all connected players - for _,player in ipairs(minetest.get_connected_players()) do - local player_name = player:get_player_name() + check_timer = check_timer + dtime + if check_timer > afk_check_interval then + check_timer = 0 - -- Only continue if the player has an entry in the player cache - if player_cache[player_name] then + -- Loop through all connected players + for _,player in ipairs(minetest.get_connected_players()) do + local player_name = player:get_player_name() + + -- Only continue if the player has an entry in the player cache -- Check for afk players - if afk_check and not ess.priv_match(player_name, "ess.player.afk") then - check_timer = check_timer + dtime - if check_timer > afk_check_interval then - check_timer = 0 + if player_cache[player_name] and not ess.priv_match(player_name, "ess.player.afk") then + local kicked = false - -- Kick player if he/she has been inactive for longer than afk_max_time seconds - if player_cache[player_name].active + afk_max_time < - minetest.get_gametime() then - minetest.kick_player(player_name, "Kicked for inactivity") - end + -- Kick player if he/she has been inactive for longer than afk_max_time seconds + if player_cache[player_name].active + afk_max_time < minetest.get_gametime() then + minetest.kick_player(player_name, "Kicked for inactivity") + kicked = true + end - -- Warn player if he/she has less than afk_warn_time seconds to move or be kicked - if afk_warn_time > 0 and player_cache[player_name].active + afk_max_time - afk_warn_time < minetest.get_gametime() then - local seconds = tostring(player_cache[player_name].active + afk_max_time - minetest.get_gametime()) - minetest.chat_send_player(player_name, string.format(afk_warn_msg, seconds)) - end + -- Warn player if he/she has less than afk_warn_time seconds to move or be kicked + if not kicked and afk_warn_time > 0 and player_cache[player_name].active + + afk_max_time - afk_warn_time < minetest.get_gametime() then + local seconds = tostring(player_cache[player_name].active + afk_max_time - minetest.get_gametime()) + minetest.chat_send_player(player_name, string.format(afk_warn_msg, seconds)) end -- Check if this player is doing an action - for _,keyPressed in pairs(player:get_player_control()) do - if keyPressed then - player_cache[player_name].active = minetest.get_gametime() + if not kicked then + for _,keyPressed in pairs(player:get_player_control()) do + if keyPressed then + player_cache[player_name].active = minetest.get_gametime() + end end end end - - -- Check if player has godmode turned on - if player_cache[player_name].god == true then - player:set_hp(20) - player:set_breath(11) - end end end end) end -ess.autoregister(commands, "player") - -- New player message if newplayer ~= "" then minetest.register_on_newplayer(function (player) @@ -186,6 +190,18 @@ minetest.register_on_joinplayer(function (player) active = minetest.get_gametime(), god = ess.get_player_meta(player_name, "god") == true, } + + -- Ensure god mode persistance + local grps = player:get_armor_groups() + if grps["immortal"] ~= 1 and player_cache[player_name].god then + if not ess.priv_match(player_name, "ess.player.god") then + player_cache[player_name].god = false + ess.set_player_meta(player_name, "god", false) + return + end + grps["immortal"] = 1 + player:set_armor_groups(grps) + end end) -- Leave diff --git a/ess/settingtypes.txt b/ess/settingtypes.txt index 3d83efb..89498ec 100644 --- a/ess/settingtypes.txt +++ b/ess/settingtypes.txt @@ -6,6 +6,9 @@ ess_tpa_timeout (tpask request timeout in seconds) int 120 # Seconds to wait before teleporting ess_teleport_delay (Teleportation delays) int 0 +# Register a privilege for each warp +ess_privilege_per_warp (Register a privilege for each warp) bool false + [Players] # Save death position so that the player can return to it via /back @@ -24,12 +27,15 @@ ess_afk_time (Max AFK time in seconds) int 300 ess_afk_warn_time (AFK warning time before kicking in seconds) int 20 # AFK warn message -ess_afk_warn_msg (AFK warning) string "Warning, you have %d seconds to move or be kicked" +ess_afk_warn_msg (AFK warning) string Warning, you have %d seconds to move or be kicked # Broadcast new players -ess_new_player (Broadcast new players) string "%s has joined the server for the first time! Welcome!" +ess_new_player (Broadcast new players) string %s has joined the server for the first time! Welcome! -[Debug] +[Kits] -# For debug purposes. If you're having unexplainable lag, try setting this to true. Breaks AFK timers and god mode. -ess_skip_globalstep (Skip GlobalStep) bool false +# Register a privilege for each kit +ess_privilege_per_kit (Register a privilege for each kit) bool false + +# Kits to give to new players +ess_default_kit (Default kit to give new players) string diff --git a/ess/warp.lua b/ess/warp.lua index f8c876e..efdb3dd 100644 --- a/ess/warp.lua +++ b/ess/warp.lua @@ -1,4 +1,5 @@ -local warpcache = nil +local warps_cache = {} +local warp_privs = minetest.settings:get_bool("ess_privilege_per_warp", false) local function get_warps() local p = ess.get_world_meta("warps") @@ -10,13 +11,23 @@ local function get_warps() p[name] = minetest.string_to_pos(pos) end end - warpcache = p + warps_cache = p + if warp_privs then + for warp in pairs(warps_cache) do + local wp = "ess.warp.warp." .. warp + if not minetest.registered_privileges[wp] then + minetest.register_privilege(wp, { + description = "Warp " .. warp, + give_to_singleplayer = false, + }) + end + end + end return p end local function save_warps() - if not warpcache then return end - local e = table.copy(warpcache) + local e = table.copy(warps_cache) for name,pos in pairs(e) do e[name] = minetest.pos_to_string(pos) end @@ -24,23 +35,20 @@ local function save_warps() end local function warp_exists(name) - if not warpcache then get_warps() end - if not warpcache[name:lower()] then return nil end - return warpcache[name:lower()] + if not warps_cache[name:lower()] then return nil end + return warps_cache[name:lower()] end local function set_warp(name, pos) name = name:lower() - if not warpcache then get_warps() end - warpcache[name] = pos + warps_cache[name] = pos save_warps() end local function cmd_warp(name,params,splitparams) if params == "" then - if not warpcache then get_warps() end local warps = {} - for name in pairs(warpcache) do + for name in pairs(warps_cache) do table.insert(warps, name) end return true, "Warps: " .. table.concat(warps, ", ") @@ -49,7 +57,7 @@ local function cmd_warp(name,params,splitparams) local warpee = name local location = params if #splitparams > 1 then - if splitparams[1] ~= name and not ess.priv_match(name, "ess.warp.warp.other") then + if splitparams[1] ~= name and not ess.priv_match(name, "ess.warp.other") then return false, "You do not have permission to warp other players!" else warpee = splitparams[1] @@ -57,9 +65,13 @@ local function cmd_warp(name,params,splitparams) end end + if warp_privs and not (ess.priv_match(pname, "ess.warps.warp." .. location) or ess.priv_match(pname, "ess.warp.all")) then + return ess.reject_permission() + end + local exists = warp_exists(location) if not exists then - return false, "Warp \""..location.."\" not found." + return false, string.format("Warp \"%s\" not found.", location) end local user = minetest.get_player_by_name(warpee) @@ -83,9 +95,9 @@ local function cmd_delwarp(name,params,splitparams) local location = splitparams[1] local exists = warp_exists(location:lower()) if not exists then - return false, "Warp \""..location.."\" not found." + return false, string.format("Warp \"%s\" not found.", location) end - warpcache[location] = nil + warps_cache[location] = nil save_warps() return true, "Successfully removed the warp point." end @@ -97,7 +109,7 @@ local commands = { aliases = {"warps"}, privs = { ["ess.warp.warp"] = true, - ["ess.warp.warp.other"] = true, + ["ess.warp.other"] = true, }, func = cmd_warp, }, @@ -116,3 +128,5 @@ local commands = { } ess.autoregister(commands, "warp") + +get_warps()