mirror of
https://github.com/minetest-mods/digtron.git
synced 2024-08-19 20:34:04 +02:00
Executing all on-dig callbacks "properly" with a fake player
This commit is contained in:
parent
426cd4d82d
commit
b90e5713aa
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
|
@ -30,8 +30,7 @@ local create_new_id = function()
|
|||||||
while mod_meta:get_string(digtron_id..":layout") ~= "" do
|
while mod_meta:get_string(digtron_id..":layout") ~= "" do
|
||||||
digtron_id = "digtron" .. tostring(math.random(1, 2^21))
|
digtron_id = "digtron" .. tostring(math.random(1, 2^21))
|
||||||
end
|
end
|
||||||
local inv = minetest.create_detached_inventory(digtron_id, detached_inventory_callbacks)
|
return digtron_id
|
||||||
return digtron_id, inv
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Deletes a Digtron record. Note: just throws everything away, this is not digtron.disassemble.
|
-- Deletes a Digtron record. Note: just throws everything away, this is not digtron.disassemble.
|
||||||
@ -273,7 +272,8 @@ digtron.assemble = function(root_pos, player_name)
|
|||||||
-- use this info, but it's small and IMO not worth the complexity.
|
-- use this info, but it's small and IMO not worth the complexity.
|
||||||
get_all_digtron_nodes(root_pos, digtron_nodes, digtron_adjacent, player_name)
|
get_all_digtron_nodes(root_pos, digtron_nodes, digtron_adjacent, player_name)
|
||||||
|
|
||||||
local digtron_id, digtron_inv = create_new_id(root_pos)
|
local digtron_id = create_new_id(root_pos)
|
||||||
|
local digtron_inv = retrieve_inventory(digtron_id)
|
||||||
|
|
||||||
local layout = {}
|
local layout = {}
|
||||||
|
|
||||||
@ -536,7 +536,7 @@ digtron.is_buildable_to = function(digtron_id, root_pos, player_name, ignore_nod
|
|||||||
if not (
|
if not (
|
||||||
(node_def and node_def.buildable_to)
|
(node_def and node_def.buildable_to)
|
||||||
or ignore_hashes[node_hash]) or
|
or ignore_hashes[node_hash]) or
|
||||||
minetest.is_protected(target_pos, player_name)
|
minetest.is_protected(node_pos, player_name)
|
||||||
then
|
then
|
||||||
if return_immediately_on_failure then
|
if return_immediately_on_failure then
|
||||||
return false -- no need to test further, don't return node positions
|
return false -- no need to test further, don't return node positions
|
||||||
@ -656,27 +656,80 @@ local predict_dig = function(digtron_id, player_name)
|
|||||||
return leftovers, dug_positions, cost
|
return leftovers, dug_positions, cost
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Removes nodes and records node info so on-dig callbacks can be called later
|
||||||
|
local get_and_remove_nodes = function(nodes_to_dig)
|
||||||
|
local ret = {}
|
||||||
|
for _, pos in ipairs(nodes_to_dig) do
|
||||||
|
local record = {}
|
||||||
|
record.pos = pos
|
||||||
|
record.node = minetest.get_node(pos)
|
||||||
|
record.meta = minetest.get_meta(pos)
|
||||||
|
minetest.remove_node(pos)
|
||||||
|
table.insert(ret, record)
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
digtron.execute_cycle = function(digtron_id, player_name)
|
digtron.execute_cycle = function(digtron_id, player_name)
|
||||||
local leftovers, nodes_to_dig, cost = predict_dig(digtron_id, player_name)
|
local leftovers, nodes_to_dig, cost = predict_dig(digtron_id, player_name)
|
||||||
local root_pos = retrieve_pos(digtron_id)
|
local old_root_pos = retrieve_pos(digtron_id)
|
||||||
local root_node = minetest.get_node(root_pos)
|
local root_node = minetest.get_node(old_root_pos)
|
||||||
local new_pos = vector.add(root_pos, cardinal_dirs[facedir_to_dir_index(root_node.param2)])
|
local new_root_pos = vector.add(old_root_pos, cardinal_dirs[facedir_to_dir_index(root_node.param2)])
|
||||||
local buildable_to, succeeded, failed = digtron.is_buildable_to(digtron_id, new_pos, player_name, nodes_to_dig, return_immediately_on_failure)
|
local buildable_to, succeeded, failed = digtron.is_buildable_to(digtron_id, new_root_pos, player_name, nodes_to_dig)
|
||||||
|
|
||||||
if buildable_to then
|
if buildable_to then
|
||||||
|
digtron.fake_player:update(old_root_pos, player_name)
|
||||||
local removed = digtron.remove_from_world(digtron_id, player_name)
|
local removed = digtron.remove_from_world(digtron_id, player_name)
|
||||||
minetest.bulk_set_node(nodes_to_dig, {name="air"})
|
|
||||||
digtron.build_to_world(digtron_id, new_pos, player_name)
|
local nodes_dug = get_and_remove_nodes(nodes_to_dig)
|
||||||
minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_pos})
|
|
||||||
|
local nodes_dug_count = #nodes_to_dig
|
||||||
|
if nodes_dug_count > 0 then
|
||||||
|
local pluralized = "node"
|
||||||
|
if nodes_dug_count > 1 then
|
||||||
|
pluralized = "nodes"
|
||||||
|
end
|
||||||
|
minetest.log("action", nodes_dug_count .. " " .. pluralized .. " dug by "
|
||||||
|
.. digtron_id .. " near ".. minetest.pos_to_string(new_root_pos)
|
||||||
|
.. " operated by by " .. player_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
digtron.build_to_world(digtron_id, new_root_pos, player_name)
|
||||||
|
minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_root_pos})
|
||||||
|
|
||||||
|
-- Don't need to do fancy callback checking for digtron nodes since I made all those
|
||||||
|
-- nodes and I know they don't have anything that needs to be done for them.
|
||||||
|
-- Just check for falling nodes.
|
||||||
for _, removed_pos in ipairs(removed) do
|
for _, removed_pos in ipairs(removed) do
|
||||||
minetest.check_for_falling(removed_pos)
|
minetest.check_for_falling(removed_pos)
|
||||||
end
|
end
|
||||||
for _, dug_pos in ipairs(nodes_to_dig) do
|
|
||||||
-- TODO: other on-dug callbacks
|
-- Execute various on-dig callbacks for the nodes that Digtron dug
|
||||||
minetest.check_for_falling(dug_pos)
|
-- Must be called after digtron.build_to_world because it triggers falling nodes
|
||||||
|
for _, dug_data in ipairs(nodes_dug) do
|
||||||
|
local old_pos = dug_data.pos
|
||||||
|
local old_node = dug_data.node
|
||||||
|
local old_name = old_node.name
|
||||||
|
|
||||||
|
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_name, param1=old_node.param1, param2=old_node.param2}
|
||||||
|
callback(pos_copy, oldnode_copy, digtron.fake_player)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local old_def = minetest.registered_nodes[old_name]
|
||||||
|
if old_def ~= nil then
|
||||||
|
local old_after_dig = old_def.after_dig_node
|
||||||
|
if old_after_dig ~= nil then
|
||||||
|
old_after_dig(old_pos, old_node, dug_data.meta, digtron.fake_player)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
commit_predictive_inventory(digtron_id)
|
commit_predictive_inventory(digtron_id)
|
||||||
else
|
else
|
||||||
|
clear_predictive_inventory(digtron_id)
|
||||||
digtron.show_buildable_nodes({}, failed)
|
digtron.show_buildable_nodes({}, failed)
|
||||||
minetest.sound_play("digtron_squeal", {gain = 0.5, pos=new_pos})
|
minetest.sound_play("digtron_squeal", {gain = 0.5, pos=new_pos})
|
||||||
end
|
end
|
||||||
|
4
init.lua
4
init.lua
@ -5,6 +5,10 @@ digtron.mod_meta = minetest.get_mod_storage()
|
|||||||
|
|
||||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||||
|
|
||||||
|
|
||||||
|
dofile(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(modpath.."/config.lua")
|
dofile(modpath.."/config.lua")
|
||||||
dofile(modpath.."/entities.lua")
|
dofile(modpath.."/entities.lua")
|
||||||
dofile(modpath.."/functions.lua")
|
dofile(modpath.."/functions.lua")
|
||||||
|
@ -137,7 +137,7 @@ local clear_predictive_inventory = function(digtron_id)
|
|||||||
minetest.remove_detached_inventory("digtron_predictive_"..digtron_id)
|
minetest.remove_detached_inventory("digtron_predictive_"..digtron_id)
|
||||||
predictive_inventory[digtron_id] = nil
|
predictive_inventory[digtron_id] = nil
|
||||||
|
|
||||||
if not next(predictive_inventory) then
|
if next(predictive_inventory) ~= nil then
|
||||||
minetest.log("warning", "[Digtron] multiple predictive inventories were in existence, this shouldn't be happening. File an issue with Digtron programmers.")
|
minetest.log("warning", "[Digtron] multiple predictive inventories were in existence, this shouldn't be happening. File an issue with Digtron programmers.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user