diff options
| -rw-r--r-- | class_layout.lua | 118 | ||||
| -rw-r--r-- | nodes/node_axle.lua | 16 | ||||
| -rw-r--r-- | util_execute_cycle.lua | 13 | 
3 files changed, 90 insertions, 57 deletions
diff --git a/class_layout.lua b/class_layout.lua index cf9a7bb..7c263a6 100644 --- a/class_layout.lua +++ b/class_layout.lua @@ -18,18 +18,12 @@ local get_node_image = function(pos, node)  	-- Record what kind of thing we've got in a builder node so its facing can be rotated properly  	if minetest.get_item_group(node.name, "digtron") == 4 then -		-- https://github.com/minetest-mods/digtron/issues/17 had a user encounter a crash here, -		-- adding logging to hopefully catch it if it happens again. -		if node_image.meta.inventory.main ~= nil then -			local build_item = node_image.meta.inventory.main[1] -			if build_item ~= "" then -				local build_item_def = minetest.registered_nodes[ItemStack(build_item):get_name()] -				if build_item_def ~= nil then -					node_image.build_item_paramtype2 = build_item_def.paramtype2 -				end +		local build_item = node_image.meta.inventory.main[1] +		if build_item ~= "" then +			local build_item_def = minetest.registered_nodes[ItemStack(build_item):get_name()] +			if build_item_def ~= nil then +				node_image.build_item_paramtype2 = build_item_def.paramtype2  			end -		else -			minetest.log("error", string.format("Digtron node in group 4 lacks a 'main' inventory. Please update the issue at https://github.com/minetest-mods/digtron/issues/17. Node image: %s", dump(node_image)))  		end  	end  	return node_image @@ -327,43 +321,12 @@ function DigtronLayout.can_write_layout_image(self)  	return true  end -function DigtronLayout.write_layout_image(self, player) - -	-- We need to call on_dignode and on_placenode for dug and placed nodes, -	-- but that triggers falling nodes (sand and whatnot) and destroys Digtrons -	-- if done during mid-write. So we need to defer the calls until after the -	-- Digtron has been fully written. -	 -	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() -	while oldpos ~= nil do -		local old_node = minetest.get_node(oldpos) -		local old_meta = minetest.get_meta(oldpos) -		minetest.remove_node(oldpos) -		table.insert(dug_nodes, {oldpos, old_node, old_meta}) -		oldpos, _ = self.old_pos_pointset:pop() -	end		 +-- We need to call on_dignode and on_placenode for dug and placed nodes, +-- but that triggers falling nodes (sand and whatnot) and destroys Digtrons +-- if done during mid-write. So we need to defer the calls until after the +-- Digtron has been fully written. -	-- create the new one -	for k, node_image in pairs(self.all) do -		local new_pos = node_image.pos -		local new_node = node_image.node -		local old_node = minetest.get_node(new_pos) -		minetest.set_node(new_pos, new_node) -		minetest.get_meta(new_pos):from_table(node_image.meta) -		 -		table.insert(placed_nodes, {new_pos, new_node, old_node}) -	end -	 -	 +local node_callbacks = function(dug_nodes, placed_nodes, player)  	for _, dug_node in pairs(dug_nodes) do  		local old_pos = dug_node[1]  		local old_node = dug_node[2] @@ -403,8 +366,69 @@ function DigtronLayout.write_layout_image(self, player)  		if new_def ~= nil and new_def.after_place_node ~= nil then  			new_def.after_place_node(new_pos, player)  		end +	end +end +local set_node_with_retry = function(pos, node) +	local start_time = minetest.get_us_time() +	while not minetest.set_node(pos, node) do +		if minetest.get_us_time() - start_time > 1000000 then -- 1 second in useconds +			return false +		end +	end +	return true +end + +local set_meta_with_retry = function(meta, meta_table) +	local start_time = minetest.get_us_time() +	while not meta:from_table(meta_table) do +		if minetest.get_us_time() - start_time > 1000000 then -- 1 second in useconds +			return false +		end  	end +	return true +end + +function DigtronLayout.write_layout_image(self, player) +	local dug_nodes = {} +	local placed_nodes = {} +	 +	-- destroy the old digtron +	local oldpos, _ = self.old_pos_pointset:pop() +	while oldpos ~= nil do +		local old_node = minetest.get_node(oldpos) +		local old_meta = minetest.get_meta(oldpos) + +		if not set_node_with_retry(oldpos, {name="air"}) then +			minetest.log("error", "DigtronLayout.write_layout_image failed to destroy old Digtron node, aborting write.") +			return false +		end + +		table.insert(dug_nodes, {oldpos, old_node, old_meta}) +		oldpos, _ = self.old_pos_pointset:pop() +	end + +	-- create the new one +	for k, node_image in pairs(self.all) do +		local new_pos = node_image.pos +		local new_node = node_image.node +		local old_node = minetest.get_node(new_pos) + +		if not (set_node_with_retry(new_pos, new_node) and set_meta_with_retry(minetest.get_meta(new_pos), node_image.meta)) then +			minetest.log("error", "DigtronLayout.write_layout_image failed to write a Digtron node, aborting write.") +			return false +		end +		 +		table.insert(placed_nodes, {new_pos, new_node, old_node}) +	end +	 +	-- 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. +	node_callbacks(dug_nodes, placed_nodes, player) +	 +	return true  end diff --git a/nodes/node_axle.lua b/nodes/node_axle.lua index 99dda4b..bdfaf76 100644 --- a/nodes/node_axle.lua +++ b/nodes/node_axle.lua @@ -45,13 +45,15 @@ minetest.register_node("digtron:axle", {  		local image = DigtronLayout.create(pos, clicker)  		image:rotate_layout_image(node.param2)  		if image:can_write_layout_image() then -			image:write_layout_image(clicker) -			 -			minetest.sound_play("whirr", {gain=1.0, pos=pos}) -			meta = minetest.get_meta(pos) -			meta:set_string("waiting", "true") -			meta:set_string("infotext", nil) -			minetest.get_node_timer(pos):start(digtron.config.cycle_time*2) +			if image:write_layout_image(clicker) then +				minetest.sound_play("whirr", {gain=1.0, pos=pos}) +				meta = minetest.get_meta(pos) +				meta:set_string("waiting", "true") +				meta:set_string("infotext", nil) +				minetest.get_node_timer(pos):start(digtron.config.cycle_time*2) +			else +				meta:set_string("infotext", "unrecoverable write_layout_image error") +			end  		else  			minetest.sound_play("buzzer", {gain=1.0, pos=pos})  			meta:set_string("infotext", S("Digtron is obstructed.")) diff --git a/util_execute_cycle.lua b/util_execute_cycle.lua index 3997395..b294aa2 100644 --- a/util_execute_cycle.lua +++ b/util_execute_cycle.lua @@ -283,7 +283,9 @@ digtron.execute_dig_cycle = function(pos, clicker)  	--move the array  	layout:move_layout_image(dir) -	layout:write_layout_image(clicker) +	if not layout:write_layout_image(clicker) then +		return pos, "unrecoverable write_layout_image error", 1 +	end  	local oldpos = {x=pos.x, y=pos.y, z=pos.z}  	pos = vector.add(pos, dir)  	meta = minetest.get_meta(pos) @@ -412,7 +414,10 @@ digtron.execute_move_cycle = function(pos, clicker)  	minetest.sound_play("truck", {gain=1.0, pos=pos})  	--move the array -	layout:write_layout_image(clicker) +	if not layout:write_layout_image(clicker) then +		return pos, "unrecoverable write_layout_image error", 1 +	end +	  	pos = vector.add(pos, dir)  	if move_player then  		clicker:moveto(vector.add(clicker:getpos(), dir), true) @@ -520,7 +525,9 @@ digtron.execute_downward_dig_cycle = function(pos, clicker)  	--move the array  	layout:move_layout_image(digtron.facedir_to_down_dir(facing)) -	layout:write_layout_image(clicker) +	if not layout:write_layout_image(clicker) then +		return pos, "unrecoverable write_layout_image error", 1 +	end  	local oldpos = {x=pos.x, y=pos.y, z=pos.z}  	pos = vector.add(pos, dir)  	meta = minetest.get_meta(pos)  | 
