Wait what, really? This is literally new information to me.
Yes. It’s easy to be confused by the unfortunate popularity of people using WaitForChild
on static content that’s guaranteed to be there anyways “just to be safe” perpetuating this misconception.
Roblox 1.0 when? 0.589??? Good stuff tho…
For the sake of clarity, when does WaitForChild
need to be used? As far as I know, it’s only for stuff that is replicating across the network boundary like player characters or new instances. Is that correct?
TL;DR:
-
Static content which exists at game startup is replicated all at once as part of the game-join block and is safe to directly access (except for
BaseParts
under StreamingEnabled which are obviously streamed in as needed). -
Anything dynamically spawned may be replicated piecewise and requires
WaitForChild
calls for both the root and the contents. -
Except for any models with StreamingMode = Atomic which have their own set of rules. Yes it’s annoying that they’re different, there’s work underway to try to bring things back to a more unified state.
Lacking better APIs, you might as well use WaitForChild all the time. It’s the one single, consistent, robust method that can be used everywhere. Easy to reason about.
Also, WaitForChild isn’t necessarily the only option. There are plenty of other APIs that provide guarantees, depending on your situation:
My game’s team colors are randomly generated, and I’d sometimes get several shades of white a couple times. Glad to see it was addressed.
One thing to note about wfc is that it has no concept of bailing out in situations where its calling thread dies while it’s yielding. This behavior is observed most easily when its thread is killed using coroutine.close
or task.cancel
.
Omitting the timeout parameter and calling wfc on a child’s name which doesn’t exist and never will, then proceeding to kill the thread will allow you to observe wfc’s signature “Infinite yield possible on X...
” warning. To most people, and for good reason, this doesn’t make much sense.
Providing the timeout parameter and calling wfc, then proceeding to kill the thread before the timeout expires will allow you to observe wfc’s lesser known “cannot resume dead coroutine
” error in all of its unintended glory.
That said, are a couple of warnings and errors that big of a deal?
To the untrained eye, no, nothing to see here, have a nice day. To the lesser untrained eye, this proves that wfc’s world-renowned Infinite yield possible on X...
warning is a plain sight sign that it causes memory leaks, at the C level. Why? Because it must be holding a reference to the thread it was called in somewhere in order for it to be able to resume its execution + a timestamp of some sort for scenarios where the timeout parameter is specified (Edit 2: There will always be a timestamp no matter if the timeout parameter was specified because the warning is time based).
If anyone can prove me wrong, please do, that’d be much appreciated.
Edit 1: Forgot to mention, calling :Destroy()
on the instance which wfc was called on (aka, the parent), will yield the same fate. pun intended
We previously used the wonderful rand()
for this, which is a low-quality PRNG that also depends on which platform you run it on (e.g. Windows has particularly bad results).
This replaced it with our high quality/fast internal RNG. It’s equivalent to Random.new() and math.random().
That’s how randomness works though, right? Sometimes you get a lot of the same thing in a row.
If you want more “predictable randomness” which goes out of its way to be aesthetically pleasing I would recommend shuffle based randomization instead. Something like this:
local colors = {}
for i = 1, 32 do
table.insert(colors, Color3.fromHSV(i / 32, 1, 1))
end
function shuffle(tb) -- Fischer-Yates shuffle
for i = #tb, 2, -1 do
local j = math.random(1, i)
tb[i], tb[j] = tb[j], tb[i] -- Swap elements at indices i and j
end
end
while true do
shuffle(colors)
for _, color in colors do
-- No annoying repetitions, perfectly even distribution
end
end
Couldn’t you provide a warning in console or related to mitigate? It’s possible that many people simply do not know or care where to find the “fine print”. I dislike the misconception.
A :WaitForChildren
would be highly useful. Not a fan of chaining wfc. I do it manually and share it across places via a utils module but, having it first class might be a lot better. Dunno
Regarding the second part about improving the randomness and performance of BrickColor:random()
, I believe it has something to do with the random generation function used to generate these random colours in the first place.
As you may know, random numbers generated from computers are not exactly “random”. There are many ways these random numbers can stem from, for instance, in Minecraft’s case, using a seed to generate entire randomised worlds.
As @SubtotalAnt8185 has stated here, some colours can be generated more often than others. This is likely what the fix is primarily about, since repetition tends to break the illusion of randomness. However, I’m not a Roblox employee and you’ll have to ask one yourself for more details.
Do note that I’m not an expert in computer science, so this is my personal interpretation of random number generation, but hopefully this widens your understanding of this fix.
Only in the simplest cases. It’s really hard to do the static analysis necessary to actually be confident a WaitForChild
call is unneeded. TL;DR: You need a lot of context, looking at the interactions between client and server scripts to do this, making it too complicated to be worth a full solution.
It may be interesting to add some lint for the simple cases of WaitForChild
on stuff in ReplicatedStorage for instance, but I don’t see much of that in the wild anyways.
The new atomic model replication mode under streaming mostly removes the need for this, by letting you specify exactly the granularity of replication you want.
That is how odds work, however it can happen way more than it should. If 5/10 times you got a duplicate there is probably something wrong.
I’ve noticed the addition of two properties, but I’m uncertain about their accessibility for developers.
Similar to other developers, I’m facing a similar challenge. However, my situation is a bit more intricate because I utilize my custom module for UI input detection. Consequently, my input doesn’t pass through as intended. Interestingly, my UI still accepts input when its visibility is set to “false.” Therefore, if these properties are indeed accessible to developers, I could potentially resolve my issue by leveraging them.
GuiState
isn’t enabled yet but it is planned to be a public property once it is. I believe there will be an announcement for that + the interactable property mechanics.
thank you for the response, sounds great and i look forward to using this
Would be great to have more documentation for this. Trying to fully understand how replication works for things outside of workspace and this reply is the best I could find. Also the AI assistant thing says WaitForChild should be used
I don’t think there’s much that we can do to train that out of the assistant. There’s so much code / bad advice out there using WaitForChild
wrong it’s bound to get confused. One of the weaknesses of LLMs is training data being deficient in some area.