Instance:FindFirstChild() recursive should accept a fully qualified path

I expected Instance:FindFirstChild() in the recursive mode would work like this:

local catapult = game:FindFirstChild(“Workspace.MovingModels.Catapult”, true)

To my horror it works this way:

local catapult = game:FindFirstChild(“Catapult”, true)

Not only does this do a full tree search of the datamodel in the worst case(!), it also means any .Name property of something I am finding needs to be globally unique, which is a much higher bar than having a unique path.

My specific use case is that I need to do a recursive WaitForChild() on a bunch of assets before my script can run, which I believe is fairly common and everyone hacks together a kludgy way to handle it.

3 Likes

It’s defined for all instances, what’s wrong with game.Workspace.MovingModels:FindFirstChild('Catapult', true);? You can also write some code to reconstruct such a call from a “dotted” string, but I don’t see why you’d want that.

1 Like

If MovingModels isn’t loaded yet that will error.

I am trying to avoid writing game:WaitForChild(“Workspace”):WaitForChild(“MovingModels”):WaitForChild(“Catapult”)

If FindFirstChild fails to find the child, it does so after searching the entire subtree. This has the potential to be really really expensive.

I’d rather WaitForChild accepts a path

12 Likes

Yes, please!

If MovingModels exists in the original DataModel (thus not created by a script), you can be certain it’ll always exist since the entire DataModel is replicated from the server to all clients before any script runs (and the server loads it too before starting scripts, of course).

Thus I’d just use game.Workspace.MovingModels:WaitForChild("Catapult") given that Catapult is dynamically created.

I’m against introducing such an API. If anything, I’d argue it should accept a table containing the full path, as strings are Lua-syntax.

Something like

game:WaitForChild( {“Workspace”, “MovingModels”, “Catapult”} )

1 Like

I would prefer a single string instead of a table for readability.

1 Like

A string wouldn’t work because Names can contain any character

1 Like

Alright, in that case it would be nice if it was a variant and that you could either supply a full path string or a table with names. I think instance names with non-alphanumeric characters are kinda a niche case to begin with.

I hate that syntax. I hate it because it is different than the other ways you specify a path in code. There should only be one way of specifying a path. All this does is increase the learning curve.

If your names have non-alphanumerics in them you are already in the weeds and I don’t care about you.

2 Likes

So you’re saying that it should accept a sort of pseudo-Lua syntax that only implements the indexing part? Could work.

Workspace["thing.with.\"non-alphanumeric\".characters"].Model.Part

I don’t think that’s what Shedletsky meant exactly (rather he just wants it to work with simple alphanumeric names and doesn’t care about the rest). But I guess that could work in a way. I don’t know if anyone would actually use it to look for instances with problematic characters in the full path though.

Well, since we’re stuck with names allowing all characters, the path syntax should also support all characters. Half-assery is forbidden!

3 Likes

I wrote a module to make this easier:

Use:

local find=require(the module)
print(find.workspace.MovingModels.Catapult())

Prints nil if the path is not valid, instead of erroring. You could use pcall instead, but thats more verbose.
You can use any variable in place of workspace and it should work (Assuming I finally understand how getfenv works)

–[[
Use:
local find=require(insert reference to module here)
local maybeInstance = find.game.A.B.C.D()
Note:
-Remember the parentheses at the end
–]]
function findHelper(root, method)
return setmetatable({},{
__index=function(self, name)
return findHelper(root and root[method](root, name), method)
end,
__call=function(self)
return root
end
})
end
find=setmetatable({},{
__index=function(self, name)
return findHelper(getfenv(2)[name] or error(“Root is nil”), “FindFirstChild”)
end
})

return find

1 Like

I think there is a bunch of code already where people do this:

game.Workspace.MovingModels.Catapult:DoSomething()

For the sake of readability/learning curve it is desirably that any ROBLOX API function that accepts a path (I can think of none at the moment) use this same idiom. So the path should be the string “game.Workspace.MovingModels.Catapult”

For the disgusting case where people are putting UTF-snowmen in their Instance names, we should let them wallow in it like javascript does:

“game.Workspace.:snowman_with_snow::snowman_with_snow::snowman_with_snow::snowman_with_snow:.Cata​:snowman_with_snow:pult” could be a legal argument to a function that accepts a path.

Then there are a couple of special characters like ‘.’ and ’ ’ that need special escaping. Anyone who has dealt with the character knows this idiom:

game.Players.LocalPlayer.Character[“Right Arm”]

In the spirit of keeping paths WYSIWYG, making “game.Players.LocalPlayer.Character[“Right Arm”]” a valid way to escape spaces makes sense. But then you need a way to escape []s. I suggest we use UTF-snowmen to do this. One UTF-snowman should be the escape character, giving:

"game.Players.LocalPlayer.Character​:snowman_with_snow:[“Right Arm":snowman_with_snow:]”

Which is a punishment dished out by the language for renaming the arm of the character to “[Right Arm]”. This is in vogue at the moment and is called an opinionated framework.

If you want to get UTF-snowman you just double escape it like so “:snowman_with_snow::snowman_with_snow:” for one snowman. We should also look into hotkeying “type UTF-snowman” so that people don’t have to constantly be copying it from other sources.

3 Likes

Note that it wouldn’t be necessary to escape brackets, since they would just need to be contained within the quote-delimited string. That is: game.Players.LocalPlayer.Character["[Right Arm]"]. Ideally, the whole thing would already be valid Lua syntax, and you’d just have to drop it in as a string argument to the function.

That said, there is still a need to escape the quote characters. While your UTF-snowman-escapes are innovative, I feel it would be rather inconsistent with Lua’s syntax. To maintain the spirit of the Lua language, I propose, as the escape character, the UTF-moon (:full_moon:), which is Lua’s namesake.

However, there are multiple characters that represent the moon. The crescent-shaped-moon character (:crescent_moon:) would be best, since it is more distinct. To appeal to Roblox’s younger audience, the face variant (:first_quarter_moon_with_face:) is greatly advised.

2 Likes

I think we should consider making the moon character that ROBLOX accepts as valid escape syntax reflect the current phase of the moon as observed from the user’s inferred lat/longitude based on IP geolocation.

As a developer quality of life feature we could implement a plugin that will Find/Replace out-of-date moon symbols in your code and automatically update them to the current valid moon symbol.

I think we should also consider adding some language to our terms of service that prohibits sun-worshippers from publishing updates to ROBLOX.com while the moon is waxing.

2 Likes

bumping this feature. A waitforchild that can take a path would be great, or built in recursive with findfirstchild, or even both.