Reason for Datastore.prefabViewModels going internal?
Hey guys! More curious than anything, but wanted to know why the prefabViewModel set in the datastore will be switched over to internal in the long run.
Is it more of an unreliability thing? i.e. it's not wise to look at it as a representation of how many realtime objects are in the room?
On the surface it seems like a pretty nice way to set up some async task for an addressables delegate on a room - by - room basis, but I could be misunderstanding it's function.
Admittedly only going off of what the docs are saying.
7 Replies
Hi @foundrey. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
----------------------
You're correct in noticing that the
Datastore.prefabViewModels
property is being switched from public to internal in a future major version. According to the documentation, this property (along with others like sceneViewModels
) is currently marked as "Internal. Do not use." and will become internal in the next major release. The docs do not provide a detailed rationale for this change, but the warning suggests that these properties are considered implementation details rather than stable APIs for public use.
Your intuition is also on point: relying on prefabViewModels
as a representation of all realtime objects in the room is not recommended. The documentation implies that these internals may not be reliable for application logic, and their structure or behavior could change without notice. This is likely why they're being made internal—to prevent developers from building dependencies on unstable or non-guaranteed aspects of the API, which could break in future updates.
For tasks like managing addressables or tracking realtime objects, it's better to use the supported APIs and extension points, such as implementing IRealtimePrefabLoadDelegate
for custom prefab loading logic, rather than depending on internal collections like prefabViewModels
Recipe: Using Addressables.
In summary, the move to internal is to discourage reliance on implementation details that are not part of the stable, supported API surface, ensuring future compatibility and maintainability of your projects Datastore Reference.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------<@719968242445058051> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
It’s always been an internal API but when Normcore was first released, Realtime was included in the assets folder and so the internal keyword had no affect so we weren’t using it at the time
fwiw its not the only collection that stores realtime objects and so I wouldn’t recommend it for counting RealtimeViews or realtime prefabs in the scene
in general we recommend having a custom component that registers with a static collection in OnEnable to track all instances of a specific type of object in the scene
Makes sense.
Been working with addressables (both for object pooling and streaming) and wanted to see if it was worth setting up an async process for the first major instantiation on load into a new room (i.e. if you changed lobbies)
The goal would have been:
1. Connect to realtime room instance
2. Scoop the current count of realtime objects
3. Queue an async process that ticks down incrementally every time we successfully run either LoadRealtimePrefab or InstantiateRealtimePrefab in the delegate.
4. Finish a transition (something like a hidden loading screen) for the client loading in.
Tbh it looks like it would be a little too unreliable/unstable to try and preempt the instantiate call with any detection logic so I think I'll probably settle for just handling it the delegate for each object as their own load/instantiation is called.
Really appreciate the clarification!
Do you expect the number of objects to change drastically between rooms? If I were you I would just allocate the maximum number of instances you'd reasonably expect if it's not too slow
if you've got RAM, you may as well use it. there's no benefit to leaving it unallocated.
you can always empty the unused instances in the pool asynchronously once you're connected
but the logic would be simpler and less error prone as you'd be able to allocate everything before you try to connect
Apologies for not seeing this!
It's actually all good for our game we're working in tandem between Normcore and SpacetimeDB to help manage the persistent data in our lobbies/general game data.
We actually ended up keeping a manifest of the runtime keys of whatever addressable assets that have been spawned into the room in Spacetime
For our use case that's totally fine and gives us more control over when and what data is delivered to the user prior to Normcore taking over the sync. We expect a lot of assets being spawned in and we already keep a dictionary of what's been spawned in the room itself but that's on a more scene-specific instance basis for a different use case and wouldn't help for this transition.
Tbh mostly been seeing where we'd rely on Normcore for persistence and where we'd want to keep some data outside even if it's functionally duplicate.
Got another question though as it kind of pertains to this issue:
What's the reason Normcore enforces a synchronous load for object delegates on Realtime.Instantiate?
It's not a huge issue and we have a reliable work around I'm just curious if it's been something that you guys enforced internally for sync or like registering the gameObject with Realtime on the same frame? Being able to throw an async task on the load section of the call would be pretty convenient, but I understand if it would cause Normcore specific problems.
It’s because Realtime.Instantiate is synchronous so any hooks called internally need to be synchronous too
and Realtime.Instantiate is synchronous because GameObject.Instantiate is too
We’ve talked about making it an async operation, but there are too many guarantees in Normcore atm with respect to when events fire and models are ready that it would need to wait until we can make API breaking changes