When to wait for replication to the client

Hi Devs!

I wanted to share some information about some unclear points regarding the starting of localscripts and when replication occurs. I will also share the guarantees that we will provide soon.

Until now, developers need to guess when localscripts starts running and what instances can be referenced at start. Today, I wanted to explain a little about some guarantees that will go live soon that will help you with what is needed and what’s not needed when writing code in your localscripts.

To reiterate, localscripts runs on client-only and will only run if they are descendants of the following places

  • The Localplayer’s Backpack
  • The Localplayer’s Character model
  • The Localplayer’s Gui
  • The Localplayer’s PlayerScripts
  • Inside the ReplicatedFirst service

From the list above, we can separate the location of a localscript into 2 categories:

  1. LocalScripts within ReplicatedFirst service
  2. Other LocalScripts that are in the backpack, playergui, the character model, and playerscripts

ReplicatedFirst Localscripts
These scripts are good when you want to make a custom loading screens, as these localscripts will be the first ones executing; they are extremely handy when running preparation on the clients during the client is loading the game.

The timing of these localscripts in ReplicatedFirst works like the following:

  1. When client first connects to the game, ReplicatedFirst service is replicated. Within this service, it will contain LocalScripts that you intended to run first.
  2. After ReplicatedFirst service is completely replicated to the run, the localscripts within this service will start running.
  3. It is important to note that these scripts will be running on the client while the rest of the game is being loaded. This means, at the starting point of these scripts, you can only rely on game.ReplicatedFirst to be completely done replicating. Other services (also including game.Workspace) will most likely be incompletely replicated at this point.

Therefore, if you want to reference any instance that you have already created (for example, if you had created these parts in Studio), you will need to wait for game:isLoaded() to return true. For example:
while not game:IsLoaded() do wait() end

However, if you want to reference the instance as soon as it is replicated, you may use WaitForChild() to wait for the replication of this instance in your ReplicatedFirst localscripts.
Please keep in mind about the following about WaitForChild():

  1. If a timeout is not specified in the WaitForChild() call, 5 seconds after the call; a warning message will be printed stating that WaitForChild() may not return.
  2. If a timeout is specified in the WaitForChlid() call, this call may return nil after the timeout time has elapsed.
  3. If the WaitForChild() call returns a non-nil value, it is safe to access that part/instance only. Descendants are not guaranteed to exist under this condition.

Which one should you use in your ReplicatedFirst scripts? (Loop until game:IsLoaded() == true or WaitForChild() )

  1. If you would like to have access to all pre-made parts in Studio and don’t mind waiting until the game is completely loaded, waiting for game:IsLoaded() == true is simpler, but might take longer.
  2. If you need access the part as soon as it arrives but don’t mind having to repeatedly call WaitForChild(), WaitForChild() will give you a quicker response.

After ReplicatedFirst service is completely replicated and the scripts in ReplicatedFirst service are running, the rest of your game is loaded on the client. During this time, other services and the workspace are replicated to the client.

Other localscripts - Player’s Backpack, Character Model, PlayerGui, and PlayerScripts
Next are the scripts that are in the player’s backpack, character model, playergui, and playerscripts.

These localscripts have a different start time than the ones in ReplicatedFirst. These scripts will start after the client has completely loaded the rest of the game.

The timing of these localscripts works like the following:

  1. After ReplicatedFirst is completely replicated, the rest of the game ( not including the player’s backpack, gui, character model, or playerscripts) are replicated to the client.
  2. After client has received all of those, the client will populate the localscripts in the localplayer’s backpack, gui, character model, and playerscripts (if any)
  3. The localscripts from step 2 are started when they are received.

Therefore, if you had made a part in Studio (and had not modified/deleted this part when the player joins), you can reliability reference that part.

Localscripts and dynamically created instances
After game is in progress, localscripts can continue to run. However, caution should be taken when localscripts are accessing dynamically generated instances.

  • If you want to access parts/instances that are dynamically created by a server script, WaitForChild() will be needed in the localscript before accessing. Since network replication is not instantaneous, we will need to ensure the part/instance has replicated to the client using WaitForChild(). After WaitForChild() returns a non-nil value, the instance can be accessed and its properties are valid and can be accessed.
  • Again, after WaitForChild() returns a non-nil value, the instance is ready to be worked with and all its properties can be referenced.

If your localscript is dynamically cloned then parented to the local player’s backpack, player’s character model, or playergui, please use WaitForChild() before accessing the instance for the safest results as these localscripts will run as soon as it is replicated. However, on the server, if the part/instance is created and parented to the data model before cloning and parenting to the player’s backpack, character model, or playergui, it is ok to access the instance and its properties without WaitForChild() since replication is done in order.

To summarize:
What can you expect when writing any localscripts?

  • ‘game.Players.LocalPlayer’ will always be non-nil
  • ‘game.Players.LocalPlayer.PlayerGui’ will always be valid Not yet live Live

What can you expect when writing localscripts within ReplicatedFirst?

  • All instances within ReplicatedFirst are readily accessible.
  • These localscripts will be running while the rest of the game is being loaded on the client.
  • It is best not to reference instances outside of ReplicatedFirst; however, it can be done if you wait for ‘game:IsLoaded() == true’ or call WaitForChild() before trying to reference that instance

What can you expect when writing localscripts within the Player’s backpack, character model, playerGui, and playerscripts?

  • Any part you have pre-made in Studio will be readily accessible. Clarification : Any instances which were published with the place in Studio will be readily accessible; however, if an instance is dynamically created during gameplay, you must use WaitForChild(). This situation is applicable for instances parented within any Services which are replicated from server to client (e.g. Workspace, ReplicatedStorage).

Here’s a visualization of it:

Please keep in mind that these guarantees are not yet active. We will keep this post updated to let you know when these go live!

Feel free to ask questions about this update, or provide feedback on any effects it might have on your game!

156 Likes

Thank you for clearing up the unclear points regarding these things on local scripts. :heart:

4 Likes

Thanks for the clarification. One question: when WaitForChild returns on an instance (excluding services), will all of its descendants (static descendants, i.e. created before the server started running) be there too?

3 Likes

Thank you so much for this!
Are there any kind of script start times we need to worry about on the server? For example, can I reliably access an instance and its descendants under Workspace on the server as soon as a script starts, or are certain things not guaranteed to exist at server script run-time?

Hi CodingLucas,

Sorry, at this time, it is not guaranteed for descendants to be replicated for WaitForChild(). When WaitForChild() returns a non-nil, it is only for that specific instance to be guaranteed ok for access.

3 Likes

Hi nomer888,

I will check up on this topic (regarding Server-side scripts). I will likely make another post regarding specifics of server scripts in the future.

1 Like

Thanks for clarifying things up.

One question I’ve had, that I’m not sure is cleared up anywhere. How reliable is it to return a reference of a newly created instance through RemoteFunction? Let’s say you have a RF to instantiate a new Part, parent it to Workspace and then return its reference; how reliable is it that the part would have already replicated in between it being parented and returned in the same call?
Visualization:
image

4 Likes

Having ReplicatedFirst is useless if the code needs to wait for PlayerGui to even put a custom loading screen, hence why we can’t have our own loading screen from the very start of the game loading.

5 Likes

Possible fix to this issue? Let’s hope so :wink:

2 Likes

Hi Reruzzyrd,

I believe that the example you gave should yield a reliable result.

For the example:

  1. Client localscript invokes RemoteFunction:InvokeServer()
  2. Client signals server to execute the remote function
  3. Server performs the work (making Test part and parenting it into workspace)
  4. Server queues up the part for replication
  5. Server completes the work and queues a signal for success (the return Part; in the RemoteFunction)
  6. Client gets Test part replicated then gets the signal for success.

Since the replication of these information happens in order, I believe that you can reliably reference the Part returned by RemoveFunction:InvokeServer(). If you observe a different behavior, please let me know!

5 Likes

Hi @EmeraldSlash and @wravager,

Your observations are absolutely correct! After these changes go live, this will advance the timing for localPlayer:WaitForChild("PlayerGui"); this should return immediately after the guarantees go live.

3 Likes

In my game, the server gives the player the client code, can we expect this to work the same from the server as well when parenting to PlayerGui on player entered?

Hi @wravager

the server gives the player the client code

Do you mean server clones a script and parents it to a player, say in the server script: (SourceScript below is a local script and something similar the following is in a server script)

game:GetService("Players").PlayerAdded:connect(function(player)
    local clonedScript = game.ServerStorage.SourceScript
    local targetPlayer = Game.Player:FindFirstChild("KnightTakami")
    clonedScript.Parent = targetPlayer:WaitForChild("PlayerGui")
end)

In the above case, on client-side, you can expect the guarantees above as described in the section Other localscripts - Player's Backpack, Character Model, PlayerGui, and PlayerScripts.

Let me know if I had answered your question or if I had mistaken your question :smiley:

1 Like

Ok thanks that answered it!

Finally! I’ve always hated having that one :WaitForChild() at the start of my scripts.

1 Like

Thank you for sharing! :grinning:

Seems like this is still not live after over 3 months? What happened? I still need to wait for a character to spawn to use my custom loading screen, which mostly defeats the purpose of having one in the first place.

EDIT: I also noticed just now, even with CharacterAutoLoads set to true and the character being spawned instantly(?), it still takes quite a bit of time before PlayerGui appears.

7 Likes

A post was merged into an existing topic: WIP Bug Reports

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.