Player instance now created on server instead of client

Out of interest, does this have anything to do with any planned performance upgrades for the :LoadCharacter() method?

1 Like

That would be cool. But my first assumption is that it’s for the updating the way players’ join data works, so that we don’t need to rely on trusting the client.

1 Like

This thread is public and the old behavior is still enabled, so engineers can’t go into specifics, but the change is necessary for security reasons. It also fixes a few unrelated bugs like the playerlist showing Player instead of Player1/etc in test servers, and is a prerequisite for fixing some deeper-rooted bugs as well.

It’s unfortunate that some games may be impacted, but specifically for the security-related reasons it’s necessary. On the bright side, the impact this has on games sounds worse than it is. Roblox did their due diligence and tested the changes against a number of top games to confirm that there weren’t any problems. That being said, you should still make sure you’re using WaitForChild appropriately in your games, as replication order isn’t guaranteed and any number of changes could potentially break your game if you rely on it.

8 Likes

So, I’ve seen Sharksie’s post spread around a lot as some rules of thumb for using WaitForChild. Since this post appears to be incorrect based on what I’ve learned from @alexnewtron and OP, does that mean that the assumption that an object’s children will be present is also false?

If an object exists in Workspace, will the object’s children be there, or do those have a potential to load in at a later time? So do I need to nest WaitForChild indefinitely for every object I want, or only the root container (like accessing a part from a Model, is workspace:WaitForChild("Model").Part sufficient)?

Another Roblox staff member confirmed to me that when using require with an asset ID, all of its children will be guaranteed to be loaded before the ModuleScript runs. Is this the only place where that paradigm exists, or does it also extend to normal require calls?

The UserId replication was patched a long time ago (~2015).

I’ve been chatting with OP privately, and he tells me that what I’ve said in this post is correct. LocalScripts will run immediately after it is replicated to the client, no matter the state of the DataModel (game).

Services exist immediately, but its children are not replicated until anything inside ReplicatedFirst replicates. LocalScripts inside RF will run immediately while other objects are downloaded into all the other services.

LocalScripts inside of StarterPlayerScripts (as an example) run as soon as they are replicated, and can be replicated before anything in Workspace, etc is replicated.

4 Likes

The following should be true:

The script WILL NOT find NicePart if it starts in ReplicatedStorage
( Scripts do not run in ReplicatedStorage )

The script may not find NicePart if it starts in StarterPlayerScripts
( Correct )

The script MAY NOT find NicePart if it starts in StarterCharacterScripts
( This is necessarily true for the time being, but this behavior can and will possibly change in the future, so it is not recommended to rely on it. Use OPs recommendation and use WaitForChild() )

The script may not find NicePart if it starts in StarterGui
( Same reason as above )

The script could find NicePart if it is delivered to the client when the server detects the player object was added to Players
( It’s hard to say exactly when Roblox sends the “snapshot” of the game when the Player Object is created ( It could be before or after? ) I wouldn’t recommend this. )

The script will find NicePart if it is delivered to the client when the server detects the player’s character is added
( This is correct and should be okay, because the character is the last object replicated to the client. However, the behavior could change so I wouldn’t rely on it. )

The script will find NicePart if it is delivered to the client when the server detects a player has touched a specific part.
( Correct. Same as above )

I would recommend creating a feature request of being able to set a LocalScript not to run until everything replicates would be possible.

5 Likes

Even if you’re not able to create the object, it still usually has documentation in the Object Browser:

(I’m talking about this menu by the way to clear up confusion)

I mean they usually keep this menu pretty up-to-date so maybe there’s a reason why they’re not including the Player object in here? I reference this menu a lot if I just need to figure out which object a certain method is under or what kind of methods certain objects have and ironically the object I need to reference the docs most for is Player.

4 Likes

There seems to be a lot of confusion, so I made a diagram. Note that it is based only on my observations, and is not authoritative.

Each step depends on the completion of the previous step (on the same peer, anyway).

  1. The client connects.
  2. A snapshot of the objects under ReplicatedFirst is queued to be replicated.
  3. The server starts replicating objects in the queue to the client.
  4. Once the client has received all the objects, scripts under ReplicatedFirst start running.
  5. A snapshot of replicatable objects under the DataModel is queued. The server also starts queuing changes made to the DataModel.
    (note that ReplicatedFirst is not actually replicatable)
  6. Once the client receives all the objects in the snapshot, the game’s Loaded state is set to true.
    (game:IsLoaded returns true, game.Loaded is fired)
    (all snapshotted objects are protected by the Loaded state)
  7. Object under Starter containers are copied to their respective script containers, a side-effect being that LocalScripts start running.

Ambiguity arises for objects that are asynchronously added near the DataModel-snapshot point in time. That is, they may or may not be included in the snapshot, and so may or may not be present by the time LocalScripts are running. Such objects require WaitForChild in order to be accessed safely.

For example (I’m not sure if this is really true), let’s say that the Player is created on the client, but the PlayerGui is created on the server. That means the server must wait for the Player to be replicated from the client before it can insert the PlayerGui. Depending on latency, the PlayerGui may be added before or after the DataModel snapshot, which means WaitForChild must be used to get the PlayerGui safely.
If the Player were created on the server, then the server would be able to insert a PlayerGui immediately, so it would be included in the snapshot guaranteed, and would therefore not require WaitForChild.

As I understand it, the changes stated in the OP should not really affect existing scripts. The mention of WaitForChild is just a general reminder.

Edit: Updated diagram to clarify Starter containers

29 Likes

Would this change the way game.Players.Localplayer works?
Since that line isn’t usable on server scripts, I thought I’d ask just in case :stuck_out_tongue:

No

1 Like

If localScripts are run as they get replicated, wouldn’t they start running right in the middle of the DataModel replication?

In my experience, there’s no guarantee when a localscript will get replicated. The localscript needs to assume that all objects in the DataModel may or may not be loaded yet.

1 Like

Man, we really need you on the IX team.

2 Likes

So in conclusion, if I have the default map (a baseplate), will I have to do workspace:WaitForChild("Baseplate") in a LocalScript? That’s what I’m concerned about.

Well, that flies directly in the face of what alexnewtron said earlier. Two Roblox staff members liked his posts, which made me think that I was wrong and that LocalScripts don’t wait for the game to be loaded before running.

But now Anaminus said that they do wait, and Anaminus is one of the smartest guys on this forum and his post has more likes. Personally, I’ve never experienced a LocalScript running before the game was fully replicated / loaded (not including objects that are added by server Scripts manually). It looks to me that Anaminus’ diagram says “if an object is there in Studio, it’ll be there before the LocalScripts run” – which is what I believed to be the behavior as well.

It’s clear that there’s a ton of confusion about this. I don’t know who to trust. Maybe e̵v̶e̵r̸y̸o̵n̵e̶’̷s̵ right. Maybe everyone’s wrong. Maybe Roblox changes the b̸e̴h̵a̶v̶i̵o̵r̴ weekly with a FFlag to keep everyone on their t̷o̸e̶s̵. Maybe ̵a̶l̶l̷ ̵_the_ ̶n̷e̸t̴w̸o̶r̶k̶i̸n̷g̵ ̸c̶o̶d̴e̷ ̶is ̵generated ̶b̸y̵ ̴mahine ̵l̴e̶a̶r̷n̷i̶n̷g̷.̴ Maybe Roblox i̸s̸ ̷j̶u̸s̸t̸ a pyramid s̴c̶h̵e̶m̷e̴. M̸̳͆a̷̻͒y̷͍̒b̷̨̀e̷̯̕ ̷̒ͅn̵̙̈ỏ̷̹n̵͔̈́e̶̡̅ ̸̜͌ō̸̰f̷̥̈́ ̴̻̓u̵͓̿s̸̫̈́ e̶͈̲̎v̴̺͌̄̂e̵̘͇̍́̾̿ṅ̵̗̜̳̎̀͝ ̴͖͓̳͈̓̄_ḛ̷̺̗̃̇̎̽x̷̙̀͑͋̿ḯ̸̛͉̦̱̍̀s̵̡̞̺̩͠ẗ̴̖͕͋̉͗ͅ_.

R̶̲̜̜̳͌̄͝e̴͚͍̽̑ͅp̸͙̋͑̃ļ̷̰̞́ͅì̴̪͗c̶̨̗͕̰̓͛ȃ̸̺͉͊͂̈́t̴̤͝i̷̙̪͇̺͊ò̸͍͋̎͠ǹ̴̡͋̄*,̸̡̘́̑̏͝ ̷͍͌͗Ṛ̷͍̘́͌̽̕͜e̵̯͊̂̇͗ͅp̴̧̖̐͜͝͝ͅl̷̗͔̔̍̈́̕i̷͓̾́c̸͔͖̈ͅḁ̵̇ẗ̶̡̻̱́i̷̩͐̒ó̸̡̨̱̰̿̕n̵̫͋̊̚,̵̥͊̃̏͆ ̵̝̩̹̮̎̽̊́Ȓ̵͉͎̑e̷̢̛̒̿ṕ̸̡̠̲͎͐̔͋l̷̩͇͌ȋ̵̼̭̲̎c̶͖̀͐͘a̵̜̽̏̉t̷͇̦̣͂̍i̶͔̅ơ̶͎̘̊̓̀ͅn̴̬̯͌.̴̞̪͈̒́̀̐.̴̖̃͊̒.̵̨̙̐͋̋ No… stop it…R̶̲̜̜̳͌̄͝e̴͚͍̽̑ͅp̸͙̋͑̃ļ̷̰̞́ͅì̴̪͗c̶̨̗͕̰̓͛ȃ̸̺͉͊͂̈́t̴̤͝i̷̙̪͇̺͊ò̸͍͋̎͠ǹ̴̡͋̄*,̸̡̘́̑̏͝ ̷͍͌͗Ṛ̷͍̘́͌̽̕͜e̵̯͊̂̇͗ͅp̴̧̖̐͜͝͝ͅl̷̗͔̔̍̈́̕i̷͓̾́c̸͔͖̈ͅḁ̵̇ẗ̶̡̻̱́i̷̩͐̒ó̸̡̨̱̰̿̕n̵̫͋̊̚,̵̥͊̃̏͆ ̵̝̩̹̮̎̽̊́I̶̜̦͖͊̉̚s̷̺̩̲͝ ̵̹̥̼̀͐b̸̜̀͗̂̏ụ̵̏̕i̷̮͝l̷̪̯̫̩͋̅̋d̷͔͑̃̊͌ͅe̶͕̬̦̍r̴̡̤̯̪͊̑͂̂m̸̝͙̪͂͑̾ä̷͈́n̸̝̭̳͂̾͗̓ ̸̼̐̈à̵̢̜́̅̚ ̴͇͛̊͂̋ṟ̵̓̽̾e̶̛̠͒̄̕ͅp̵̱̟͈̄̋̓̔l̶̻̼͋̓̓͆͜ï̵̭̔ĉ̴̺̺̉̚a̶͚̭̳̽n̸̠͍̘̰͗̈́ţ̸̓̚̚?̶̰̼̰̍̋̀ H̵e̴l̶p̴ ̴m̶E̵….Ḯ̴͉̙̲̫̓’̵̨͙̲͕͗̿m̸̪̑̾͊͜͝ ̷͖̜̖͛̾͠b̷̘̈̅̔e̴̖͔͙̔̓ͅī̶̬͈̙̙n̸̢͔̬̆g̸̞̗̤̖̋ ̴̹̬̙̀͛̊̾͜****R̵̡̩͂̃͘͘E̴͚̥̩̒̄L̶̰̟̐͂͗͊P̸͇̘̈́̈́͆̄Ȋ̷͓̣͕̟Ć̸͇̹͕A̵̧̭̗̐̆̅T̴̛̥͈̠͌̄̇Ḝ̷̛̣̥̀D̶̨͂̈.̸̀͌ͅ.̵̨̙̻͛̌.̷̧̟̖̲̒̒

44 Likes

This is already what you should currently do. All the post did was remind that this is the best practice to use in local scripts.

Both now and after this change is live, you can’t guarantee anything exists yet on the client by the time your script tries to access it. The only change whatsoever happening here is that the player object will be created on the server after the change is live.

When in doubt, use WaitForChild. It is impossible to say with 100% certainty that an object will be there when you directly index it.

1 Like

I made a typo and wrote ReplicatedStorage instead of ReplicatedFirst, but I am confident the answer is “no it will not find NicePart”

What about Character, all the example scripts you see will use game:GetService(“Players”).LocalPlayer.Client
If you use a WaitForClient() I get an ‘infinite yield’ error.
Most of the time I will do… while not LocalPlayer.Character do wait() end

So what is the actual, correct and proper way to handle Player.Character, on the local side?

Yes, and that’s one point where it gets confusing. It is important to remember that scripts run depending on where they are located. Theoretically, a LocalScript might replicate and then begin running immediately. In most cases however, any container where they could replicate is a container where they can’t run (StarterGui, StarterPlayerScripts, almost everything, really). And, for any container where they can run (PlayerGui, etc), scripts are copied from the respective Starter container (StarterGui, etc) only after game.Loaded is set to true.

This is why I was particular about how step 7 was worded. It doesn’t say right out that LocalScripts become enabled. Rather, it is the Starter containers that depend on game.Loaded.

If you wanted, you could use a ReplicatedFirst script (which runs early) to insert another script into a script container, which would cause it to run, even if the game hasn’t finished loading. The container itself doesn’t care about game.Loaded, so the script runs unconditionally. Starter containers, which copy contained objects to their respective script containers do care about game.Loaded.

Main points

  • Script containers run scripts unconditionally.
    • Script containers include PlayerGui, PlayerScripts, Backpack, and the player’s character.
  • Each script container is created on the fly, so they and their contents may or may not be included in the DataModel snapshot.
  • Starter containers copy objects to their respective script containers.
    • Starter containers include StarterGui, StarterPlayerScripts, StarterGear, and StarterCharacterScripts.
  • Starter containers will start copying only after game.Loaded is true.

Conclusions

  • LocalScripts are not inherently protected by game.Loaded.
  • LocalScripts managed by Starter containers are protected by game.Loaded.
  • LocalScripts managed by other means (e.g. server script, ReplicatedFirst script) may not be protected by game.Loaded.

If the LocalScript is managed by a Starter container, and the BasePlate always exists, then WaitForChild does not need to be used.


All my information is based off of observing how the client behaves. Take that as you will.


How do I sign up?

10 Likes