mirror of
https://github.com/minetest-mods/digtron.git
synced 2025-01-03 01:27:28 +01:00
fakeplayer for callbacks, switch to whitelist for on_place calls
This commit is contained in:
parent
0430d423f7
commit
abc7f2e414
145
class_fakeplayer.lua
Normal file
145
class_fakeplayer.lua
Normal file
@ -0,0 +1,145 @@
|
||||
-- The purpose of this class is to have something that can be passed into callbacks that
|
||||
-- demand a "Player" object as a parameter and hopefully prevent the mods that have
|
||||
-- registered with those callbacks from crashing on a nil dereference or bad function
|
||||
-- call. This is not supposed to be a remotely functional thing, it's just supposed
|
||||
-- to provide dummy methods and return values of the correct data type for anything that
|
||||
-- might ignore the false "is_player()" return and go ahead and try to use this thing
|
||||
-- anyway.
|
||||
|
||||
-- I'm trying to patch holes in bad mod programming, essentially. If a mod is so badly
|
||||
-- programmed that it crashes anyway there's not a lot else I can do on my end of things.
|
||||
|
||||
DigtronFakePlayer = {}
|
||||
DigtronFakePlayer.__index = DigtronFakePlayer
|
||||
|
||||
local function return_value(x)
|
||||
return (function() return x end)
|
||||
end
|
||||
|
||||
local function return_nil()
|
||||
return nil
|
||||
end
|
||||
|
||||
local function return_empty_string()
|
||||
return ""
|
||||
end
|
||||
|
||||
local function return_zero()
|
||||
return 0
|
||||
end
|
||||
|
||||
local function return_empty_table()
|
||||
return {}
|
||||
end
|
||||
|
||||
function DigtronFakePlayer.update(self, pos, player_name)
|
||||
self.is_fake_player = ":digtron " .. player_name
|
||||
self.get_pos = return_value(pos)
|
||||
end
|
||||
|
||||
function DigtronFakePlayer.create(pos, player_name)
|
||||
local self = {}
|
||||
setmetatable(self, DigtronFakePlayer)
|
||||
|
||||
self.is_fake_player = ":digtron " .. player_name
|
||||
|
||||
-- ObjectRef
|
||||
self.get_pos = return_value(pos)
|
||||
self.set_pos = return_nil
|
||||
self.move_to = return_nil
|
||||
self.punch = return_nil
|
||||
self.right_click = return_nil
|
||||
self.get_hp = return_value(10)
|
||||
self.set_hp = return_nil
|
||||
self.get_inventory = return_nil -- returns an `InvRef`
|
||||
self.get_wield_list = return_empty_string
|
||||
self.get_wield_index = return_value(1)
|
||||
self.get_wielded_item = return_value(ItemStack(nil))
|
||||
self.set_wielded_item = return_value(false)
|
||||
self.set_armor_groups = return_nil
|
||||
self.get_armor_groups = return_empty_table
|
||||
self.set_animation = return_nil
|
||||
self.get_animation = return_nil -- a set of values, maybe important?
|
||||
self.set_attach = return_nil
|
||||
self.get_attach = return_nil
|
||||
self.set_detach = return_nil
|
||||
self.set_bone_position = return_nil
|
||||
self.get_bone_position = return_nil
|
||||
self.set_properties = return_nil
|
||||
self.get_properties = return_empty_table
|
||||
|
||||
self.is_player = return_value(false)
|
||||
|
||||
self.get_nametag_attributes = return_empty_table
|
||||
self.set_nametag_attributes = return_nil
|
||||
|
||||
--LuaEntitySAO
|
||||
self.set_velocity = return_nil
|
||||
self.get_velocity = return_value({x=0,y=0,z=0})
|
||||
self.set_acceleration = return_nil
|
||||
self.get_acceleration = return_value({x=0,y=0,z=0})
|
||||
self.set_yaw = return_nil
|
||||
self.get_yaw = return_zero
|
||||
self.set_texture_mod = return_nil
|
||||
self.get_texture_mod = return_nil -- maybe important?
|
||||
self.set_sprite = return_nil
|
||||
--self.get_entity_name` (**Deprecated**: Will be removed in a future version)
|
||||
self.get_luaentity = return_nil
|
||||
|
||||
-- Player object
|
||||
|
||||
self.get_player_name = return_empty_string
|
||||
self.get_player_velocity = return_nil
|
||||
self.get_look_dir = return_value({x=0,y=1,z=0})
|
||||
self.get_look_horizontal = return_zero
|
||||
self.set_look_horizontal = return_nil
|
||||
self.get_look_vertical = return_zero
|
||||
self.set_look_vertical = return_nil
|
||||
|
||||
--self.get_look_pitch`: pitch in radians - Deprecated as broken. Use `get_look_vertical`
|
||||
--self.get_look_yaw`: yaw in radians - Deprecated as broken. Use `get_look_horizontal`
|
||||
--self.set_look_pitch(radians)`: sets look pitch - Deprecated. Use `set_look_vertical`.
|
||||
--self.set_look_yaw(radians)`: sets look yaw - Deprecated. Use `set_look_horizontal`.
|
||||
self.get_breath = return_value(10)
|
||||
self.set_breath = return_nil
|
||||
self.get_attribute = return_nil
|
||||
self.set_attribute = return_nil
|
||||
|
||||
self.set_inventory_formspec = return_nil
|
||||
self.get_inventory_formspec = return_empty_string
|
||||
self.get_player_control = return_value({jump=false, right=false, left=false, LMB=false, RMB=false, sneak=false, aux1=false, down=false, up=false} )
|
||||
self.get_player_control_bits = return_zero
|
||||
|
||||
self.set_physics_override = return_nil
|
||||
self.get_physics_override = return_value({speed = 1, jump = 1, gravity = 1, sneak = true, sneak_glitch = false, new_move = true,})
|
||||
|
||||
|
||||
self.hud_add = return_nil
|
||||
self.hud_remove = return_nil
|
||||
self.hud_change = return_nil
|
||||
self.hud_get = return_nil -- possibly important return value?
|
||||
self.hud_set_flags = return_nil
|
||||
self.hud_get_flags = return_value({ hotbar=true, healthbar=true, crosshair=true, wielditem=true, breathbar=true, minimap=true })
|
||||
self.hud_set_hotbar_itemcount = return_nil
|
||||
self.hud_get_hotbar_itemcount = return_zero
|
||||
self.hud_set_hotbar_image = return_nil
|
||||
self.hud_get_hotbar_image = return_empty_string
|
||||
self.hud_set_hotbar_selected_image = return_nil
|
||||
self.hud_get_hotbar_selected_image = return_empty_string
|
||||
self.set_sky = return_nil
|
||||
self.get_sky = return_empty_table -- may need members on this table
|
||||
|
||||
self.set_clouds = return_nil
|
||||
self.get_clouds = return_value({density = 0, color = "#fff0f0e5", ambient = "#000000", height = 120, thickness = 16, speed = {x=0, y=-2}})
|
||||
|
||||
self.override_day_night_ratio = return_nil
|
||||
self.get_day_night_ratio = return_nil
|
||||
|
||||
self.set_local_animation = return_nil
|
||||
self.get_local_animation = return_empty_table
|
||||
|
||||
self.set_eye_offset = return_nil
|
||||
self.get_eye_offset = return_value({x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
|
||||
return self
|
||||
end
|
@ -327,6 +327,11 @@ function DigtronLayout.write_layout_image(self, player)
|
||||
|
||||
local dug_nodes = {}
|
||||
local placed_nodes = {}
|
||||
|
||||
-- fake_player will be passed to callbacks to prevent actual player from "taking the blame" for this action.
|
||||
-- For example, the hunger mod shouldn't be making the player hungry when he moves Digtron.
|
||||
digtron.fake_player:update(self.controller, player:get_player_name())
|
||||
-- note that the actual player is still passed to the per-node after_place_node and after_dig_node, should they exist.
|
||||
|
||||
-- destroy the old digtron
|
||||
local oldpos, _ = self.old_pos_pointset:pop()
|
||||
@ -357,18 +362,11 @@ function DigtronLayout.write_layout_image(self, player)
|
||||
|
||||
minetest.log("action", string.format("%s removes Digtron component %s at (%d, %d, %d)", player:get_player_name(), old_node.name, old_pos.x, old_pos.y, old_pos.z))
|
||||
|
||||
if modpath_awards then
|
||||
-- We're about to tell the awards mod that we're digging a node, but we
|
||||
-- don't want it to count toward any actual awards. Pre-decrement.
|
||||
local data = awards.players[player:get_player_name()]
|
||||
awards.increment_item_counter(data, "count", old_node.name, -1)
|
||||
end
|
||||
|
||||
for _, callback in ipairs(minetest.registered_on_dignodes) do
|
||||
-- Copy pos and node because callback can modify them
|
||||
local pos_copy = {x=old_pos.x, y=old_pos.y, z=old_pos.z}
|
||||
local oldnode_copy = {name=old_node.name, param1=old_node.param1, param2=old_node.param2}
|
||||
callback(pos_copy, oldnode_copy, player)
|
||||
callback(pos_copy, oldnode_copy, digtron.fake_player)
|
||||
end
|
||||
|
||||
local old_def = minetest.registered_nodes[old_node.name]
|
||||
@ -384,19 +382,12 @@ function DigtronLayout.write_layout_image(self, player)
|
||||
|
||||
minetest.log("action", string.format("%s adds Digtron component %s at (%d, %d, %d)", player:get_player_name(), new_node.name, new_pos.x, new_pos.y, new_pos.z))
|
||||
|
||||
if modpath_awards then
|
||||
-- We're about to tell the awards mod that we're placing a node, but we
|
||||
-- don't want it to count toward any actual awards. Pre-decrement.
|
||||
local data = awards.players[player:get_player_name()]
|
||||
awards.increment_item_counter(data, "place", new_node.name, -1)
|
||||
end
|
||||
|
||||
for _, callback in ipairs(minetest.registered_on_placenodes) do
|
||||
-- Copy pos and node because callback can modify them
|
||||
local pos_copy = {x=new_pos.x, y=new_pos.y, z=new_pos.z}
|
||||
local oldnode_copy = {name=old_node.name, param1=old_node.param1, param2=old_node.param2}
|
||||
local newnode_copy = {name=new_node.name, param1=new_node.param1, param2=new_node.param2}
|
||||
callback(pos_copy, newnode_copy, player, oldnode_copy)
|
||||
callback(pos_copy, newnode_copy, digtron.fake_player, oldnode_copy)
|
||||
end
|
||||
|
||||
local new_def = minetest.registered_nodes[new_node.name]
|
||||
|
20
init.lua
20
init.lua
@ -20,8 +20,28 @@ digtron.builder_read_item_substitutions = {
|
||||
["farming:desert_sand_soil_wet"] = "default:desert_sand",
|
||||
}
|
||||
|
||||
-- Sometimes we want builder heads to call an item's "on_place" method, other times we
|
||||
-- don't want them to. There's no way to tell which situation is best programmatically
|
||||
-- so we have to rely on whitelists to be on the safe side.
|
||||
|
||||
--first exact matches are tested, and the value given in this global table is returned
|
||||
digtron.builder_on_place_items = {
|
||||
["default:torch"] = true,
|
||||
}
|
||||
|
||||
-- Then a string prefix is checked, returning this value. Useful for enabling on_placed on a mod-wide basis.
|
||||
digtron.builder_on_place_prefixes = {
|
||||
["farming:"] = true,
|
||||
}
|
||||
|
||||
-- Finally, items belonging to group "digtron_on_place" will have their on_place methods called.
|
||||
|
||||
local digtron_modpath = minetest.get_modpath( "digtron" )
|
||||
|
||||
dofile( digtron_modpath .. "/class_fakeplayer.lua")
|
||||
|
||||
digtron.fake_player = DigtronFakePlayer.create({x=0,y=0,z=0}, "fake_player") -- since we only need one fake player at a time and it doesn't retain useful state, create a global one and just update it as needed.
|
||||
|
||||
dofile( digtron_modpath .. "/config.lua" )
|
||||
dofile( digtron_modpath .. "/util.lua" )
|
||||
dofile( digtron_modpath .. "/doc.lua" )
|
||||
|
@ -32,8 +32,18 @@ local function has_prefix(str, prefix)
|
||||
return str:sub(1, string.len(prefix)) == prefix
|
||||
end
|
||||
|
||||
local function blacklisted_on_place(item_name)
|
||||
if has_prefix(item_name, "stairs:slab_") then return true end
|
||||
local function whitelisted_on_place(item_name)
|
||||
for listed_item, value in pairs(digtron.builder_on_place_items) do
|
||||
if item_name == listed_item then return value end
|
||||
end
|
||||
|
||||
for prefix, value in pairs(digtron.builder_on_place_prefixes) do
|
||||
if has_prefix(item_name, prefix) then return value end
|
||||
end
|
||||
|
||||
if minetest.get_item_group(item_name, "digtron_on_place") > 0 then return true end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function copy_pointed_thing(pointed_thing)
|
||||
@ -77,7 +87,7 @@ digtron.item_place_node = function(itemstack, placer, place_to, param2)
|
||||
pointed_thing.under = {x=place_to.x, y=place_to.y - 1, z=place_to.z}
|
||||
|
||||
-- Handle node-specific on_place calls as best we can.
|
||||
if def.on_place and def.on_place ~= minetest.nodedef_default.on_place and not blacklisted_on_place(itemstack:get_name()) then
|
||||
if def.on_place and def.on_place ~= minetest.nodedef_default.on_place and whitelisted_on_place(itemstack:get_name()) then
|
||||
if def.paramtype2 == "facedir" then
|
||||
pointed_thing.under = vector.add(place_to, minetest.facedir_to_dir(param2))
|
||||
elseif def.paramtype2 == "wallmounted" then
|
||||
@ -128,7 +138,7 @@ digtron.item_place_node = function(itemstack, placer, place_to, param2)
|
||||
|
||||
local take_item = true
|
||||
|
||||
-- Run callback
|
||||
-- Run callback, using genuine player for per-node definition.
|
||||
if def.after_place_node then
|
||||
-- Deepcopy place_to and pointed_thing because callback can modify it
|
||||
local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z}
|
||||
@ -139,7 +149,10 @@ digtron.item_place_node = function(itemstack, placer, place_to, param2)
|
||||
end
|
||||
end
|
||||
|
||||
-- Run script hook
|
||||
-- Run script hook, using fake_player to take the blame.
|
||||
-- Note that fake_player:update is called in the DigtronLayout class's "create" function,
|
||||
-- which is called before Digtron does any of this building stuff, so it's not necessary
|
||||
-- to update it here.
|
||||
local _, callback
|
||||
for _, callback in ipairs(minetest.registered_on_placenodes) do
|
||||
-- Deepcopy pos, node and pointed_thing because callback can modify them
|
||||
@ -147,7 +160,7 @@ digtron.item_place_node = function(itemstack, placer, place_to, param2)
|
||||
local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2}
|
||||
local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2}
|
||||
local pointed_thing_copy = copy_pointed_thing(pointed_thing)
|
||||
if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then
|
||||
if callback(place_to_copy, newnode_copy, digtron.fake_player, oldnode_copy, itemstack, pointed_thing_copy) then
|
||||
take_item = false
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user