Altering RaycastParams.FilterDescendantsInstances with table.insert/table.remove causes no changes to occur

This post has out of context replies as it was initially a falsy bug report.

Right now, the page for RaycastParams does not reflect on a very important limitation of the object, which is that it cannot be altered via table.insert and table.remove.

The DevHub should be updated to reflect that the object cannot be altered with these methods, and the property must explicitly be set to a table rather than edited inline.

My general habits with Lua say to simply use table.insert inline, and when I did this, I spent a great deal of time confused as to why it wasn’t working. This could be very easily alleviated with a documentation change.

6 Likes

This is intended behaviour

I see. I seem to have missed that. I’ll get this thread moved elsewhere and change its request to a documentation update.

The documentation makes a false statement which threw me off as well. Highlighted in a blue box on the RaycastParams page is:

Unlike most datatypes on Roblox, all the members of RaycastParams can be changed without creating a new object. When raycasting repeatedly, you should re-use the same object.

In addition to table.insert, I tried re-assigning via
RaycastParams.FilterDescendantsInstances = {inst1, inst2}
as well as
RaycastParams.FilterDescendantsInstances = {unpack(table_of_instances)}
and neither of these resulted in any actual change to FilterDescendantsInstances once it had an existing value. The only method that worked for me was creating a new RaycastParams and starting over.

That statement isn’t incorrect. You can modify all the members of the RaycastParams object.

The problem that has to be described is that when you query FilterDescendantsInstances, you get a table that is a copy of what you originally put in it. It is not a pointer that lets you directly modify FilterDescendantsInstances. This would be infeasible since the representation on the engine side is different than on the Lua side. The difference with other API on FilterDescendantInstances is that they are not tables (numbers, strings, enums, etc) and so they are always passed by value rather than by reference, so you don’t see this issue occur there since you are forced to set them in RaycastParams directly.

The documentation should mention that when you query FilterDescendantsInstances, you get back a pointer to a copy of the contents of FilterDescendantsInstances. You can then modify those contents and set it back.

Interesting. When I tried to create a simple testcase I observed the behavior you described. It looks like the behavior I observed originally occurs only under certain conditions. I was able to repro it with the following test case:

  1. Paste the below code into a LocalScript under StarterPlayerScripts
  2. Put a breakpoint on the “table.insert” line
  3. Single-step around the loop and observe that the error condition is hit.

local part1 = Instance.new("Part")
part1.Anchored = true
part1.CanCollide = false
part1.Parent = workspace

local part2 = Instance.new("Part")
part2.Anchored = true
part2.CanCollide = false
part2.Parent = workspace

local params = RaycastParams.new()
params.CollisionGroup = "Test"
params.FilterType = Enum.RaycastFilterType.Blacklist

local function OnRenderStepped(step)
	local ignoreList = {part1}
	while true do
		params.FilterDescendantsInstances = {unpack(ignoreList)}
		print(#params.FilterDescendantsInstances)
		if #params.FilterDescendantsInstances ~= #ignoreList then
			error(string.format("expected %d entries, got %d", #ignoreList, #params.FilterDescendantsInstances))
		end

		local res = workspace:Raycast(Vector3.new(0,0,-5), Vector3.new(0,0,5), params)
		if res then
			table.insert(ignoreList, res.Instance)
		else
			break
		end
	end
end

game:GetService("RunService").RenderStepped:Connect(OnRenderStepped)

Interestingly, if I add a guard variable to prevent re-entrancy on the above routine, the problem does not manifest. Perhaps the Raycast() routine can run concurrently with other code on the client and temporarily locks the FilterDescendantsInstances table while it is in progress.

1 Like

I should add that, if my above analysis is correct, then this page:

should mark Raycast() as being a “yielding function” in the same way that other such functions are marked, e.g.,

Yikes, something is going terribly wrong here, you’re certainly right about that!

However, it isn’t an issue with the RaycastParams, the RaycastParams is dutifully storing and returning the values that are getting passed to it, rather the script debugger is somehow passing garbage into the RaycastParams when single stepping.

Thanks for the repro, will look into it.

4 Likes