Hi Creators,
We are excited to share that we have made improvements to Streaming Enabled giving developers a new Model.StreamingMode
to make streaming easier to implement. Some of these updates have been requested by the community for a long time and we’re excited to see what you create using them!
New Property: Model.StreamingMode
- Atomic - Models stream in or out as a unit
- Non-atomic - Models stream before descendants
- Default - Same as non-atomic (we are exploring ways to optimize this for the future)
- Persistent - Always streamed
- PersistentPerPlayer - Always streamed for specified players
New Functions:
- Model:AddPersistentPlayer(Player)
- Model:RemovePersistentPlayer(Player)
New Event: Workspace.PersistentLoaded(Player)
- Fires when all persistent models are loaded for the player
Note: the properties and functions are server-side only and should not be used in local scripts. Changing the streaming mode on a client via a local script or client module script is not supported. The PersistentLoaded even will fire on server and client, but it will normally fire before the player is loaded.
Model Streaming Behavior Differences
In current streaming, models under the workspace that don’t have part ancestors are always sent during loading. As a result, if those models have part descendants those models may exist on the client with partial descendants or no descendants at all until the parts are streamed to the client.
With atomic models, the model and all its descendants are sent as soon as any descendant of the model is required due to streaming. This typically happens when a part descendant of the model is close enough to a client’s replication focus to be streamed. Atomic models can be sent due to other descendants being streamed, such as a joint being needed for other streamed parts. When an atomic model is sent to a client, the model will not be parented into the workspace until all the model descendants have been received.
Stream out is also handled differently for atomic models. With nonatomic models, only parts and their descendants are subject to stream out. For atomic models, when all descendants of the model are safe to stream out and stream out is occurring due to memory pressure or opportunistic stream out, the model itself will be streamed out on the client.
Currently, Default means the same as Nonatomic, but we expect this to change in the future. We will give developers the option to opt into that change when it happens, and advance notice will be given.
Once the PersistentLoaded signal has fired persistent models always exist on clients and are never streamed out. Similar to atomic models, Persistent models are added under their parent when they are complete. A lobby your experience uses frequently is a good example of something you may want to persist.
PersistentPerPlayer models behave the same as atomic models, except for those players that have been added using AddPersistentPlayer. For those players, the model will behave as a persistent model.
Streaming Relative to Experience Loading
Traditional nonatomic streaming models that are not descendants of parts will always be sent during loading. However, this behavior is changing with the addition of atomic, persistent, and persistent per-player models;
An atomic model with part descendants will be streamed when any of its part descendants are needed by the client.
An atomic model with no part descendants will not be sent during load. Persistent models are also not sent during game loading, but soon after as part of streaming. The PersistentLoaded event should be used to determine when persistent and part-less atomic models have been sent to clients. PersistentPerPlayer models behave like atomic models, except for specified players where they behave the same as persistent models.
Advantages:
Fewer Unnecessary Models on Clients
Traditional streaming sends all models to all clients unless a model is a descendant of a part. This means that models that may not have their descendant parts are always sent during join, and are present on all clients. This is particularly a concern when it comes to player models, as the models and their descendant animations, scripts, etc. are present on all clients even when the player is far away and possibly streamed out.
Video: Models and non-parts exist even for streamed out NPCs
With atomic player models, the model and its descendants are streamed in and out as needed. Notice in the following video that when avatars that have been set to atomic models are streamed out, the entire model is removed.
Video: Models are streamed out for streamed out NPCs
Fewer WaitForChild Calls
With existing, nonatomic models, there is no guarantee that all descendants of the model will be streamed at the same time. This means that if a local script needs to access descendants of the model then multiple calls to [WaitForChild](https://create.roblox.com/docs/reference/engine/classes/Instance#WaitForChild)
may be necessary. For example, if an experience has the following model:
A local script that needs to access all descendants of the model might need to use multiple calls to WaitForChild
:
local Workspace = game:GetService("Workspace")
local Model = Workspace.Model
local Part1 = Model:WaitForChild("Part1")
local Part2 = Model:WaitForChild("Part2")
local Sound = Part2:WaitForChild("Sound")
However, if the model were atomic then a single WaitForChild
would be sufficient:
local Workspace = game:GetService("Workspace")
local Model = Workspace:WaitForChild("Model")
Tips/Suggestions:
-
Persistence is meant to be used in rare circumstances, such as when a small number of parts must always be present on clients for local script use. If possible server scripts should be used, or local scripts should be written to be tolerant of parts being streamed in and out.
- Persistence is not intended to circumvent Streaming, such as to improve visual quality or to avoid using level of detail. Overusing persistence can have negative impacts on the performance of your experience and cause issues on low memory clients.
-
Avoid making models containing very large numbers of instances atomic as this can cause bursty networking transmission and impact player experience.
-
Note that in the unusual case that your script adds a subtree to an already replicated atomic model the subtree will not appear atomically on clients. Subsequent stream in or out of the model will function atomically as expected.
Known Issues (last updated on 08/15/2023):
We are aware of the following issues, and are working on fixes:
-
There is a known issue with animations with player/NPC character models that have been set to atomic. If a character is playing an animation and is streamed out and back in, then the previously played animation may not be applied.[Fixed 8/8/2023] -
Character models for players that have been streamed out may not be fully removed from memory. We are working on a fix for this, but developers should use caution when setting player models to atomic.[Fixed 2/23/2023] -
Models with[Fixed 3/1/2023]ModelStreamingBehavior == Persistent
will not be sent until a character or replication focus exists. Fix is coming soon. Workaround is to manually set a replication focus if automatic character loading is disabled. -
Models with[Fixed 3/1/2023]ModelStreamingBehavior != Default
may not replicate correctly when stored outside of Workspace (ReplicatedStorage, ETC). Fix is coming soon. Workaround is to not set the streaming model until the model is under the Workspace. -
Calling[Fixed 3/1/2023]ModelInstance:RemovePersistentPlayer
(orAddPersistentPlayer
) for a player that is in the process of being removed, such as via the CharacterRemoved callback can result in a crash. Fix coming soon, until available please avoid callingModelInstance:RemovePersistentPlayer
(orAddPersistentPlayer
) in such situations.
We understand that we threw a lot at you in this post so if you have any questions or concerns, please comment below! We will be happy to help.
For additional information on streaming enabled please see: Content Streaming
If you encounter a bug related to this API and are able to file bug reports please create a separate bug report for better visibility.
Thank you.