From abc7f2e4147734783176e6cce84891b99b2c001b Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Mon, 16 Oct 2017 21:35:24 -0600 Subject: [PATCH] fakeplayer for callbacks, switch to whitelist for on_place calls --- class_fakeplayer.lua | 145 +++++++++++++++++++++++++++++++++++++++ class_layout.lua | 23 ++----- init.lua | 20 ++++++ util_item_place_node.lua | 25 +++++-- 4 files changed, 191 insertions(+), 22 deletions(-) create mode 100644 class_fakeplayer.lua diff --git a/class_fakeplayer.lua b/class_fakeplayer.lua new file mode 100644 index 0000000..7976cb4 --- /dev/null +++ b/class_fakeplayer.lua @@ -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 \ No newline at end of file diff --git a/class_layout.lua b/class_layout.lua index d18004a..e6f6923 100644 --- a/class_layout.lua +++ b/class_layout.lua @@ -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] diff --git a/init.lua b/init.lua index 981bb39..0b457c6 100644 --- a/init.lua +++ b/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" ) diff --git a/util_item_place_node.lua b/util_item_place_node.lua index 07a28d1..0ee32ac 100644 --- a/util_item_place_node.lua +++ b/util_item_place_node.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