like speed, acceleration, player attachment. The handler will
likely be called many times per second, so the function needs
to make sure that the event is handled properly.
+
+Key API
+-------
+
+The key API allows mods to add key functionality to nodes that have
+ownership or specific permissions. Using the API will make it so
+that a node owner can use skeleton keys on their nodes to create keys
+for that node in that location, and give that key to other players,
+allowing them some sort of access that they otherwise would not have
+due to node protection.
+
+To make your new nodes work with the key API, you need to register
+two callback functions in each nodedef:
+
+
+`on_key_use(pos, player)`
+ * Is called when a player right-clicks (uses) a normal key on your
+ * node.
+ * `pos` - position of the node
+ * `player` - PlayerRef
+ * return value: none, ignored
+
+The `on_key_use` callback should validate that the player is wielding
+a key item with the right key meta secret. If needed the code should
+deny access to the node functionality.
+
+If formspecs are used, the formspec callbacks should duplicate these
+checks in the metadata callback functions.
+
+
+`on_skeleton_key_use(pos, player, newsecret)`
+
+ * Is called when a player right-clicks (uses) a skeleton key on your
+ * node.
+ * `pos` - position of the node
+ * `player` - PlayerRef
+ * `newsecret` - a secret value(string)
+ * return values:
+ * `secret` - `nil` or the secret value that unlocks the door
+ * `name` - a string description of the node ("a locked chest")
+ * `owner` - name of the node owner
+
+The `on_skeleton_key_use` function should validate that the player has
+the right permissions to make a new key for the item. The newsecret
+value is useful if the node has no secret value. The function should
+store this secret value somewhere so that in the future it may compare
+key secrets and match them to allow access. If a node already has a
+secret value, the function should return that secret value instead
+of the newsecret value. The secret value stored for the node should
+not be overwritten, as this would invalidate existing keys.
+
+Aside from the secret value, the function should retun a descriptive
+name for the node and the owner name. The return values are all
+encoded in the key that will be given to the player in replacement
+for the wielded skeleton key.
+
+if `nil` is returned, it is assumed that the wielder did not have
+permissions to create a key for this node, and no key is created.
default_snow.png
default_snow_side.png
default_snowball.png
+ default_key.png
+ default_key_skeleton.png
asl97 (CC BY-SA 3.0):
default_ice.png
}
})
+minetest.register_craft({
+ output = 'default:skeleton_key',
+ recipe = {
+ {'default:gold_ingot'},
+ }
+})
+
minetest.register_craft({
output = 'default:chest',
recipe = {
recipe = "default:clay_lump",
})
+minetest.register_craft({
+ type = 'cooking',
+ output = 'default:gold_ingot',
+ recipe = 'default:skeleton_key',
+ cooktime = 5,
+})
+
+minetest.register_craft({
+ type = 'cooking',
+ output = 'default:gold_ingot',
+ recipe = 'default:key',
+ cooktime = 5,
+})
+
--
-- Fuels
--
end
local function has_locked_chest_privilege(meta, player)
- local name = ""
if player then
if minetest.check_player_privs(player, "protection_bypass") then
return true
end
- name = player:get_player_name()
+ else
+ return false
+ end
+
+ -- is player wielding the right key?
+ local item = player:get_wielded_item()
+ if item:get_name() == "default:key" then
+ local key_meta = minetest.parse_json(item.get_metadata())
+ local secret = meta:get_string("key_lock_secret")
+ if secret ~= key_meta.secret then
+ return false
+ end
+
+ return true
end
- if name ~= meta:get_string("owner") then
+
+ if player:get_player_name() ~= meta:get_string("owner") then
return false
end
+
return true
end
return itemstack
end,
on_blast = function() end,
+ on_key_use = function(pos, player)
+ local secret = minetest.get_meta(pos):get_string("key_lock_secret")
+ local itemstack = player:get_wielded_item()
+ local key_meta = minetest.parse_json(itemstack:get_metadata())
+
+ if secret ~= key_meta.secret then
+ return
+ end
+
+ minetest.show_formspec(
+ player:get_player_name(),
+ "default:chest_locked",
+ get_locked_chest_formspec(pos)
+ )
+ end,
+ on_skeleton_key_use = function(pos, player, newsecret)
+ local meta = minetest.get_meta(pos)
+ local owner = meta:get_string("owner")
+ local name = player:get_player_name()
+
+ -- verify placer is owner of lockable chest
+ if owner ~= name then
+ minetest.record_protection_violation(pos, name)
+ minetest.chat_send_player(name, "You do not own this chest.")
+ return nil
+ end
+
+ local secret = meta:get_string("key_lock_secret")
+ if secret == "" then
+ secret = newsecret
+ meta:set_string("key_lock_secret", secret)
+ end
+
+ return secret, "a locked chest", owner
+ end,
})
},
sound = {breaks = "default_tool_breaks"},
})
+
+minetest.register_tool("default:skeleton_key", {
+ description = "Skeleton Key",
+ inventory_image = "default_key_skeleton.png",
+ groups = {key = 1},
+ on_place = function(itemstack, placer, pointed_thing)
+ if pointed_thing.type ~= "node" then
+ return itemstack
+ end
+
+ local pos = pointed_thing.under
+ local node = minetest.get_node(pos)
+
+ if not node then
+ return itemstack
+ end
+
+ local on_skeleton_key_use = minetest.registered_nodes[node.name].on_skeleton_key_use
+ if on_skeleton_key_use then
+ -- make a new key secret in case the node callback needs it
+ local random = math.random
+ local newsecret = string.format(
+ "%04x%04x%04x%04x",
+ random(2^16) - 1, random(2^16) - 1,
+ random(2^16) - 1, random(2^16) - 1)
+
+ local secret, _, _ = on_skeleton_key_use(pos, placer, newsecret)
+
+ if secret then
+ -- finish and return the new key
+ itemstack:take_item()
+ itemstack:add_item("default:key")
+ itemstack:set_metadata(minetest.write_json({
+ secret = secret
+ }))
+ return itemstack
+ end
+ end
+ return nil
+ end
+})
+
+minetest.register_tool("default:key", {
+ description = "Key",
+ inventory_image = "default_key.png",
+ groups = {key = 1, not_in_creative_inventory = 1},
+ stack_max = 1,
+ on_place = function(itemstack, placer, pointed_thing)
+ if pointed_thing.type ~= "node" then
+ return itemstack
+ end
+
+ local pos = pointed_thing.under
+ local node = minetest.get_node(pos)
+
+ if not node or node.name == "ignore" then
+ return itemstack
+ end
+
+ local ndef = minetest.registered_nodes[node.name]
+ if not ndef then
+ return itemstack
+ end
+
+ local on_key_use = ndef.on_key_use
+ if on_key_use then
+ on_key_use(pos, placer)
+ end
+
+ return nil
+ end
+})
end
if clicker and not minetest.check_player_privs(clicker, "protection_bypass") then
+ -- is player wielding the right key?
+ local item = clicker:get_wielded_item()
local owner = meta:get_string("doors_owner")
- if owner ~= "" then
+ if item:get_name() == "default:key" then
+ local key_meta = minetest.parse_json(item:get_metadata())
+ local secret = meta:get_string("key_lock_secret")
+ if secret ~= key_meta.secret then
+ return false
+ end
+
+ elseif owner ~= "" then
if clicker:get_player_name() ~= owner then
return false
end
if def.protected then
def.can_dig = can_dig_door
def.on_blast = function() end
+ def.on_key_use = function(pos, player)
+ local door = doors.get(pos)
+ door:toggle(player)
+ end
+ def.on_skeleton_key_use = function(pos, player, newsecret)
+ local meta = minetest.get_meta(pos)
+ local owner = meta:get_string("doors_owner")
+ local pname = player:get_player_name()
+
+ -- verify placer is owner of lockable door
+ if owner ~= pname then
+ minetest.record_protection_violation(pos, pname)
+ minetest.chat_send_player(pname, "You do not own this locked door.")
+ return nil
+ end
+
+ local secret = meta:get_string("key_lock_secret")
+ if secret == "" then
+ secret = newsecret
+ meta:set_string("key_lock_secret", secret)
+ end
+
+ return secret, "a locked door", owner
+ end
else
def.on_blast = function(pos, intensity)
minetest.remove_node(pos)
function _doors.trapdoor_toggle(pos, node, clicker)
node = node or minetest.get_node(pos)
if clicker and not minetest.check_player_privs(clicker, "protection_bypass") then
+ -- is player wielding the right key?
+ local item = clicker:get_wielded_item()
local meta = minetest.get_meta(pos)
local owner = meta:get_string("doors_owner")
- if owner ~= "" then
+ if item:get_name() == "default:key" then
+ local key_meta = minetest.parse_json(item:get_metadata())
+ local secret = meta:get_string("key_lock_secret")
+ if secret ~= key_meta.secret then
+ return false
+ end
+
+ elseif owner ~= "" then
if clicker:get_player_name() ~= owner then
return false
end
end
def.on_blast = function() end
+ def.on_key_use = function(pos, player)
+ local door = doors.get(pos)
+ door:toggle(player)
+ end
+ def.on_skeleton_key_use = function(pos, player, newsecret)
+ local meta = minetest.get_meta(pos)
+ local owner = meta:get_string("doors_owner")
+ local pname = player:get_player_name()
+
+ -- verify placer is owner of lockable door
+ if owner ~= pname then
+ minetest.record_protection_violation(pos, pname)
+ minetest.chat_send_player(pname, "You do not own this trapdoor.")
+ return nil
+ end
+
+ local secret = meta:get_string("key_lock_secret")
+ if secret == "" then
+ secret = newsecret
+ meta:set_string("key_lock_secret", secret)
+ end
+
+ return secret, "a locked trapdoor", owner
+ end
else
def.on_blast = function(pos, intensity)
minetest.remove_node(pos)