diff --git a/README.md b/README.md index efa1d94..6c6518d 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,12 @@ Chat commands /npc_talk_debug Allows to debug dialogs. +/npc_talk_privs grant, revoke or list the privs of NPC. NPC need privs for + some dangerous things like executing lua code. Examples: + /npc_talk_privs list lists the privs of all NPC + /npc_talk_privs grant n_3 effect_exec_lua + grants NPC n_3 the right to execute + lua code as an effect/result Terminology =========== @@ -65,6 +71,20 @@ npc_master allows players to edit *any* NPC supported by this mod. code without restrictions. Only grant this to staff members you really trust. + +NPC can have privs as well. The NPC... + +precon_exec_lua ..is allowed to excecute lua code as a precondition + +effect_exec_lua ..is allowed to execute lua code as an effect + +effect_give_item ..can give items to the player, created out of thin air + +effect_take_item ..can accept and destroy items given to it by a player + +effect_move_player ..can move the player to another position + + Tools ===== There are diffrent staffs for diffrent functionality: @@ -207,6 +227,8 @@ take a look at In order to add custom functions, you need to be able to edit that file or execute Lua code on the server. +NPC may need extra privs for some actions. + Alternate Text ============== Sometimes you may encounter a situation where your NPC ought to answer to diff --git a/exec_apply_effects.lua b/exec_apply_effects.lua index 0fa2d02..277a13d 100644 --- a/exec_apply_effects.lua +++ b/exec_apply_effects.lua @@ -307,12 +307,18 @@ yl_speak_up.execute_effect = function(player, n_id, o_id, r) return true elseif(r.r_type == "function") then -- this can only be set and edited with the staff + if(not(yl_speak_up.npc_has_priv(n_id, "effect_exec_lua"))) then + return false + end return yl_speak_up.eval_and_execute_function(player, r, "r_") -- this can only be set and edited with the staff elseif(r.r_type == "give_item") then if(not(r.r_value)) then return false end + if(not(yl_speak_up.npc_has_priv(n_id, "effect_give_item"))) then + return false + end local item = ItemStack(r.r_value) if(not(minetest.registered_items[item:get_name()])) then yl_speak_up.debug_msg(player, n_id, o_id, tostring(r.r_id).." ".. @@ -328,6 +334,9 @@ yl_speak_up.execute_effect = function(player, n_id, o_id, r) return true -- this can only be set and edited with the staff elseif(r.r_type == "take_item") then + if(not(yl_speak_up.npc_has_priv(n_id, "effect_take_item"))) then + return false + end if(not(r.r_value)) then return false end @@ -346,6 +355,9 @@ yl_speak_up.execute_effect = function(player, n_id, o_id, r) return true -- this can only be set and edited with the staff elseif(r.r_type == "move") then + if(not(yl_speak_up.npc_has_priv(n_id, "effect_move_player"))) then + return false + end -- copeid/moved here from AliasAlreadyTakens code in functions.lua local target_pos = nil local target_pos_valid = false diff --git a/exec_eval_preconditions.lua b/exec_eval_preconditions.lua index ee48383..f866606 100644 --- a/exec_eval_preconditions.lua +++ b/exec_eval_preconditions.lua @@ -87,6 +87,9 @@ yl_speak_up.eval_precondition = function(player, n_id, p, other_options_true_or_ -- a precondition set by using the staff; kept for compatibility return true elseif(p.p_type == "function") then + if(not(yl_speak_up.npc_has_priv(n_id, "precon_exec_lua"))) then + return false + end -- a precondition set by using the staff; -- extremly powerful (executes any lua code) return yl_speak_up.eval_and_execute_function(player, p, "p_") diff --git a/privs.lua b/privs.lua index 3ac716b..287619e 100644 --- a/privs.lua +++ b/privs.lua @@ -1,23 +1,159 @@ +-- the privs players need some privs +-- npc_talk_owner is required to set up an NPC; +-- npc_talk_master allows to edit NPC of other players; +-- npc_master allows to set dangerous commands like execute lua code + local npc_master_priv_definition = { description="Can use the staffs to command NPCs", give_to_singleplayer = false, give_to_admin = true, } - minetest.register_privilege("npc_master", npc_master_priv_definition) + local npc_talk_owner_priv_definition = { description="Can edit the dialogs of his/her own NPCs", give_to_singleplayer = false, give_to_admin = true, } - minetest.register_privilege("npc_talk_owner", npc_talk_owner_priv_definition) + local npc_talk_master_priv_definition = { description="Can edit the dialogs of NPCs independent of owner", give_to_singleplayer = false, give_to_admin = true, } - minetest.register_privilege("npc_talk_master", npc_talk_master_priv_definition) + + +-- NPC also need privs to execute more dangerous commands + +-- this table will hold the actual privs in the form of +-- indices of the form t[][] = True +yl_speak_up.npc_priv_table = {} + +-- where shall the privs be stored so that they will be available after server restart? +yl_speak_up.npc_priv_path = minetest.get_worldpath().."/yl_speak_up_npc_privs.data" + +-- these are deemed dangerous and checked +yl_speak_up.npc_priv_names = { + "precon_exec_lua", + "effect_exec_lua", "effect_give_item", "effect_take_item", "effect_move_player", +} + +yl_speak_up.npc_has_priv = function(n_id, priv_name) + -- fallback: disallow + if(not(n_id) or not(priv_name)) then + return false + end + if(not(yl_speak_up.npc_priv_table[n_id]) + or not(yl_speak_up.npc_priv_table[n_id][priv_name])) then + minetest.log( + "action", + "[MOD] yl_speak_up: NPC with ID "..tostring(n_id).. + " was denied the NPC priv "..tostring(priv_name)..".") + return false + end + return true +end + + +yl_speak_up.npc_privs_load = function() + local file,err = io.open( yl_speak_up.npc_priv_path, "rb") + if (file == nil) then + yl_speak_up.npc_priv_table = {} + return + end + local data = file:read("*all") + file:close() + yl_speak_up.npc_priv_table = minetest.deserialize(data) +end + + +yl_speak_up.npc_privs_store = function() + local file,err = io.open( yl_speak_up.npc_priv_path, "wb") + if (file == nil) then + return + end + file:write(minetest.serialize(yl_speak_up.npc_priv_table)) + file:close() +end + + +-- a chat command to grant or deny or disallow npc these privs; +-- it is not checked if the NPC exists +minetest.register_chatcommand( 'npc_talk_privs', { + description = "Grants or revokes the privilege to the ".. + "yl_speak_up-NPC with the ID .\n".. + "Call: [grant|revoke] \n".. + "If called with parameter [list], all granted privs for all NPC are shown.", + privs = {privs = true}, + func = function(pname, param) + if(not(param) or param == "") then + minetest.chat_send_player(pname, + "Usage: [grant|revoke|list] \n".. + "The following privilege exist:\n\t".. + table.concat(yl_speak_up.npc_priv_names, ", ")..".") + return + end + local parts = string.split(param, " ") + if(parts[1] == "list") then + local text = "This list contains the privs of each NPC in the form of ".. + ": " + -- create list of all existing extra privs for npc + for n_id, v in pairs(yl_speak_up.npc_priv_table) do + text = text..".\n"..tostring(n_id)..":" + local found = false + for priv, w in pairs(v) do + text = text.." "..tostring(priv) + found = true + end + if(not(found)) then + text = text.." " + end + end + minetest.chat_send_player(pname, text..".") + return + end + if((parts[1] ~= "grant" and parts[1] ~= "revoke") or #parts ~= 3) then + minetest.chat_send_player(pname, "Usage: [grant|revoke] ") + return + end + local command = parts[1] + local n_id = parts[2] + local priv = parts[3] + if(table.indexof(yl_speak_up.npc_priv_names, priv) == -1) then + minetest.chat_send_player(pname, + "Unknown priv \""..tostring(priv).."\".\n".. + "The following privilege exist:\n\t".. + table.concat(yl_speak_up.npc_priv_names, ", ")..".") + return + end + if(command == "grant" and not(yl_speak_up.npc_priv_table[n_id])) then + yl_speak_up.npc_priv_table[n_id] = {} + end + if(command == "grant") then + yl_speak_up.npc_priv_table[n_id][priv] = true + elseif(yl_speak_up.npc_priv_table[n_id]) then + yl_speak_up.npc_priv_table[n_id][priv] = nil + end + local text = "New privs of NPC "..tostring(n_id)..":" + local found = false + if(yl_speak_up.npc_priv_table[n_id]) then + for k, v in pairs(yl_speak_up.npc_priv_table[n_id]) do + text = text.." "..tostring(k) + found = true + end + end + if(not(found)) then + text = text.." " + yl_speak_up.npc_priv_table[n_id] = nil + end + minetest.chat_send_player(pname, text..".") + yl_speak_up.npc_privs_store() + end +}) + +-- when the game is started: load the npc privs +yl_speak_up.npc_privs_load()