find_path segmentation fault

Discussion of Lua and LuaWML support, development, and ideas.

Moderator: Forum Moderators

Post Reply
vghetto
Posts: 755
Joined: November 2nd, 2019, 5:12 pm

find_path segmentation fault

Post by vghetto »

Hi,

The call to wesnoth.find_path is causing segmentation fault.
Any help would be appreciated.

Thanks

Code: Select all

local helper = wesnoth.require "lua/helper.lua"
local LS = wesnoth.require "location_set"

-- [road_path]
-- 	from_x=10
--	from_y=10
-- 	to_x=20
-- 	to_y=20
-- 	include_borders=no
-- 	road_windiness=3
-- 	[road_cost]
-- 		terrain=Kh
-- 		cost=2
-- 		convert_to=Kh
-- 	[/road_cost]
-- 	[road_cost]
-- 		terrain=Ch
-- 		cost=2
-- 		convert_to=Ch
-- 	[/road_cost]
-- 	[road_cost]
-- 		terrain=Rr
-- 		cost=2
-- 		convert_to=Rr
-- 	[/road_cost]
-- 	[road_cost]
-- 		terrain=Gg
-- 		cost=10
-- 		convert_to=Rr
-- 	[/road_cost]
-- 	[road_cost]
-- 		terrain=Hh
-- 		cost=20
-- 		convert_to=Rr
-- 	[/road_cost]
-- 	[road_cost]
-- 		terrain=Ww
-- 		cost=50
-- 		convert_to_bridge=Ww^Bw|, Ww^Bw/, Ww^Bw\
-- 		convert_to=Ch
-- 	[/road_cost]
-- [/road_path]

local function on_board(x, y)
        if type(x) ~= "number" or type(y) ~= "number" then
                return false
        end
        local w, h = wesnoth.get_map_size()
        return x >= 1 and y >= 1 and x <= w and y <= h
end

local function insert_locs(x, y, locs_set)
	if locs_set:get(x,y) or not on_board(x, y) then
		return
	end
	locs_set:insert(x,y)
end

local function place_road(to_x, to_y, from_x, from_y, road_ops)
	if not on_board(to_x, to_y) then
		return
	end

	local tile_op = road_ops[wesnoth.get_terrain(to_x, to_y)]
	if tile_op then
		if tile_op.convert_to_bridge and from_x and from_y then
			local bridges = {}
			for elem in tile_op.convert_to_bridge:gmatch("[^%s,][^,]*") do
				table.insert(bridges, elem)
			end
			local dir = wesnoth.map.get_relative_dir(from_x, from_y, to_x, to_y)
			if dir == 'n' or dir == 's' then
				wesnoth.set_terrain(to_x, to_y, bridges[1])
			elseif dir == 'sw' or dir == 'ne' then
				wesnoth.set_terrain(to_x, to_y, bridges[2])
			elseif dir == 'se' or dir == 'nw' then
				wesnoth.set_terrain(to_x, to_y, bridges[3])
			end
		elseif tile_op.convert_to then
			local tile = helper.rand(tile_op.convert_to)
			wesnoth.set_terrain(to_x, to_y, tile)
		end
	end
end

function wesnoth.wml_actions.road_path(cfg)
	local from_x = tonumber(cfg.from_x) or helper.wml_error("[road_path] expects a from_x= attribute.")
	local from_y = tonumber(cfg.from_y) or helper.wml_error("[road_path] expects a from_y= attribute.")
	local to_x = tonumber(cfg.to_x) or helper.wml_error("[road_path] expects a to_x= attribute.")
	local to_y = tonumber(cfg.to_y) or helper.wml_error("[road_path] expects a to_y= attribute.")
	local windiness = tonumber(cfg.road_windiness) or 1
	local map_width, map_height = wesnoth.get_map_size()

	local road_costs, road_ops = {}, {}
	for road in helper.child_range(cfg, "road_cost") do
		road_costs[road.terrain] = road.cost
		road_ops[road.terrain] = road
	end

	local path, cost = wesnoth.find_path(from_x, from_y, to_x, to_y, {
		calculate = function(x, y, current_cost)
			local res = 1.0
			local tile = wesnoth.get_terrain(x, y)
			res = road_costs[tile] or 1.0
			if windiness > 1 then
				res = res * wesnoth.random(windiness)
			end
			return res
		end }, map_width, map_height)

	for i, loc in ipairs(path) do
		local locs_set = LS.create()
		insert_locs(loc[1], loc[2], locs_set)
		local prev_x, prev_y
		for x,y in locs_set:stable_iter() do
			place_road(x, y, prev_x, prev_y, road_ops)
			prev_x, prev_y = x, y
		end
	end
end
Edit:
Passing a unit as the first parameter won't crash find_path.
I need to use find_path without having a unit defined. How can I do that?
How can I call the map generation version of find_path?
User avatar
Pentarctagon
Project Manager
Posts: 5526
Joined: March 22nd, 2009, 10:50 pm
Location: Earth (occasionally)

Re: find_path segmentation fault

Post by Pentarctagon »

It shouldn't be able to crash Wesnoth in the first place, actually. A full, minimal example of how to reproduce the crash would be helpful.
99 little bugs in the code, 99 little bugs
take one down, patch it around
-2,147,483,648 little bugs in the code
vghetto
Posts: 755
Joined: November 2nd, 2019, 5:12 pm

Re: find_path segmentation fault

Post by vghetto »

Hi

Running the attached crashes with a segmentation fault on linux. wesnoth version 1.15.7
I didn't test it on 1.14.14

You'll have to uncomment/comment 4 lines to fix. It starts on line 80.

Thanks
Attachments
_main.cfg
(6.62 KiB) Downloaded 229 times
User avatar
Pentarctagon
Project Manager
Posts: 5526
Joined: March 22nd, 2009, 10:50 pm
Location: Earth (occasionally)

Re: find_path segmentation fault

Post by Pentarctagon »

It looks like part of the issue as per the wiki is likely that there must be a unit at the source location. The crashing part is still a bug within Wesnoth though, so I opened #5354 for it.
99 little bugs in the code, 99 little bugs
take one down, patch it around
-2,147,483,648 little bugs in the code
vghetto
Posts: 755
Joined: November 2nd, 2019, 5:12 pm

Re: find_path segmentation fault

Post by vghetto »

Hi,

I'd like to draw your attention to find_path ignoring max_cost.
It is looking for an argument called stop_at instead.

stop_at = luaW_table_get_def<double>(L, arg, "stop_at", stop_at);

It might need to be changed to:
stop_at = luaW_table_get_def<double>(L, arg, "max_cost", stop_at);

Edit:
Looking at the issue comments, the following did not crash wesnoth 1.15:

Code: Select all

        path, cost = wesnoth.find_path(from_x, from_y, to_x, to_y, {
                viewing_side = 1, ignore_units = true, ignore_teleport = true,
                calculate = function(x, y, current_cost)
                        local res = 1.0
                        local tile = wesnoth.get_terrain(x, y)
                        res = road_costs[tile] or 1.0
                        if windiness > 1 then
                                res = res * wesnoth.random(windiness)
                        end
                        return res
                end })
But changing viewing_side to viewing_side = 0, will crash it as already known.

The following worked and did not crash on wesnoth 1.14.14

Code: Select all

        path, cost = wesnoth.find_path(from_x, from_y, to_x, to_y,
                function(x, y, current_cost)
                        local res = 1.0
                        local tile = wesnoth.get_terrain(x, y)
                        res = road_costs[tile] or 1.0
                        if windiness > 1 then
                                res = res * wesnoth.random(windiness)
                        end
                        return res
                end )
User avatar
Pentarctagon
Project Manager
Posts: 5526
Joined: March 22nd, 2009, 10:50 pm
Location: Earth (occasionally)

Re: find_path segmentation fault

Post by Pentarctagon »

vghetto wrote: December 14th, 2020, 7:27 am Hi,

I'd like to draw your attention to find_path ignoring max_cost.
It is looking for an argument called stop_at instead.

stop_at = luaW_table_get_def<double>(L, arg, "stop_at", stop_at);

It might need to be changed to:
stop_at = luaW_table_get_def<double>(L, arg, "max_cost", stop_at);
Probably in this case it would be better to update the wiki instead, so anyone who's already using stop_at doesn't need to change their code.
vghetto wrote: December 14th, 2020, 7:27 am Edit:
Looking at the issue comments, the following did not crash wesnoth 1.15:

Code: Select all

        path, cost = wesnoth.find_path(from_x, from_y, to_x, to_y, {
                viewing_side = 1, ignore_units = true, ignore_teleport = true,
                calculate = function(x, y, current_cost)
                        local res = 1.0
                        local tile = wesnoth.get_terrain(x, y)
                        res = road_costs[tile] or 1.0
                        if windiness > 1 then
                                res = res * wesnoth.random(windiness)
                        end
                        return res
                end })
But changing viewing_side to viewing_side = 0, will crash it as already known.

The following worked and did not crash on wesnoth 1.14.14

Code: Select all

        path, cost = wesnoth.find_path(from_x, from_y, to_x, to_y,
                function(x, y, current_cost)
                        local res = 1.0
                        local tile = wesnoth.get_terrain(x, y)
                        res = road_costs[tile] or 1.0
                        if windiness > 1 then
                                res = res * wesnoth.random(windiness)
                        end
                        return res
                end )
Do other invalid side values for viewing_side like 99 or -5 cause issues?
99 little bugs in the code, 99 little bugs
take one down, patch it around
-2,147,483,648 little bugs in the code
vghetto
Posts: 755
Joined: November 2nd, 2019, 5:12 pm

Re: find_path segmentation fault

Post by vghetto »

Pentarctagon wrote: December 14th, 2020, 4:06 pm Probably in this case it would be better to update the wiki instead, so anyone who's already using stop_at doesn't need to change their code.
I doubt anyone is passing stop_at as an argument.
A quick grep of mainline data/ai/* and data/lua/* shows 0 uses of stop_at, but multiple uses of max_cost.
Pentarctagon wrote: December 14th, 2020, 4:06 pm Do other invalid side values for viewing_side like 99 or -5 cause issues?
Yes, it will segfault with an invalid viewing_side.
I tried 3, 99, -5. I only have side=1 and side=2.
setting it to 2 did not crash.

Thanks

Edit: stop_at was introduced in this commit. It used to be max_cost.
https://github.com/wesnoth/wesnoth/comm ... c98a363521
Post Reply