Player instance now created on server instead of client

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

This is what I wanted answered this whole thread. My scripts shouldn’t be affected by this. I feel the OP should’ve made this more clear, it would’ve cleared all this confusion.

Agreed, this was my issue as well. Everything posted was extremely ambiguous up until Anaminus’ post.

1 Like

Just as a heads up, to add to what @Anaminus is saying above (which I believe is correct):

The earlier version of this change (which was current when this thread was made) did invalidate an implicit contract about LocalScripts accessing existing content in game. Specifically, because of how the change affected PlayerGui replication, if you did not use StarterGui container and instead parented LocalScripts to PlayerGui on the server, the change would’ve replicated these parts of PlayerGui before Workspace, thereby breaking some Workspace accesses.

We are revising the change based on testing & feedback to not do this. The new version of the change would create Player on the server, but only replicate contents of PlayerGui and other descendants after game.Loaded, similarly to what happens right now.

New version of the change shouldn’t have any observable behavior changes wrt replication timing; we’ll still announce it when we enable it - it’s hard to anticipate all consequences of a change like this - but that’ll happen after we make sure we don’t have any known instances of games changing behavior.

Sorry about the confusion :slight_smile:

16 Likes

Wanting to note that until the Player’s character has spawned. NO contents from StarterGui will be replicated to PlayerGui. This breaks my game as the Player’s character is only spawned upon being requested, and causes all of my PlayerGui:WaitForChild()'s to time out because the contents simply do not exist until the server has spawned the Player’s character.

Is this intentional or a bug? @zeuxcg

What do you mean by “breaks my game”? Are you saying that this used to work and is currently broken on production, or are you saying that this behavior gets in your way and you’d like it changed, or something else?

I believe that this is intentional for CharacterAutoSpawn=True; for CharacterAutoSpawn=False it’s an unfortunate side-effect. We wanted to have special logic to start PlayerGui copying process on game.Loaded instead when CharacterAutoSpawn=False, but this can negatively impact games that rely on the current behavior - so we can’t easily change this now.

1 Like