As a Roblox developer, it is currently too difficult to hold persistent references to Roblox instances beyond the Lua VM. Object values are currently the best way to hold these references, however managing them is another challenge in itself. I want to be able to refer to instances from my code, and have these references persist between Lua VM’s.
I am in need of this feature for a component system, not too dissimilar from that which exists in Unity. I wish to store instance metadata in a module. At present, this is done by using object values to refer to an instance, and then a module script inside of that value which holds the metadata. While this seems like a reasonable approach, there are some issues which I have:
I need to wait for the ObjectValue and ModuleScript to replicate before I can use them.
The instance that the Value property on ObjectValue refers to may not have replicated.
All instances require a reference, even if there is no metadata associated with it, so that I can ensure that replication has occured.
With some sort of UID for an instance, I could use a single module which keys the metadata against the instances UID, this would be the only instance I’d need to worry about.
An implementation might be as follows:
readonlystring Instance.Id or string Instance.InstanceId
A unique identifier which mirrors the referrent attribute saved in Roblox’s XML.
Instance DataModel:GetInstanceById(string InstanceId)
Returns the instance with the given id, if it exists.
Instance DataModel:WaitForInstanceWithId(string InstanceId[, number timeout])
Waits for an instance with the given id to be added to the datamodel or returns nil if the timeout expires.
Since instances can exist outside of the datamodel, it may be worth considering having these methods located elsewhere, such as CollectionService
If I am correct, the referrent attribute already replicates, so instances which have been replicated would share their Id.
I’m a little curious; do you mind sharing what specific data you are storing about instances?
I ran across this problem too while making my table serializer support instances, so I had to make an instanceid module, but I haven’t found a use for it anywhere else yet
(I use a custom table serializer to allow for very mixed, nested and replicated tables)
I’m also wondering when you would use (or already use) WaitForInstanceWithId
In my work with my instanceid module, I haven’t needed to yield for instances because generally what I do is
initialize instance
potentially wait some time
parent instance to replicated container
add instance to replicated table
I’ve just never really needed to tell the client of an instance before replicating it because those can both be done at once anyways
Is the primary use for this instead in streaming enabled to replace WaitForChild? Is there a use here other then to avoid organizing workspace? Or is relying on workspace organization a very significant issue?
Most of this is just me wanting to learn more about the usefulness, but I think it could help if you provided specific examples anyways
I am essentially storing key, value pairs for properties related to components. Some of these properties may themselves be instances. I am generating the modules programmatically, so being able to refer to instances in a simple way is desirable.
Originally I did not include it, but I can see it being beneficial for dealing with replication. We might be able to survive without this API, but for the sake of completeness I decided to add it.
One thing I would note is that assuming that workspace is organized is a luxury. There are cases when you’re writing modules for other developers and you have no idea how they might organize their game.
There are plenty of other-use cases for such a feature. The thread posted by @Corecii is just one such example. Others might include storing references to instances in plugin settings for some reason.
Properties are defined by user-written modules, so they could range from built-in data types to complex table structures and Roblox instances.
Sure! So, there’s my own use-case which is essentially being able to store references beyond the Lua VM. This is useful if you want to identify an instance in your code without needing to update your code every time the instance’s ancestry changes.
Some other use cases could be:
A package dependency lock. You could bind the name of a package to an instanceId, making it quick to perform a lookup and find the package in your game.
Useful when you want to deduplicate package dependencies.
WaitForInstanceWithId would allow you to ensure all of a packages dependencies have replicated.
A prefab plugin might want to keep track of a mapping between duplicates.
When one of the instances is updated, the corresponding instance can be updated in the duplicate.
This could be saved in plugin settings.
Debugging non-unique instances.
You’d be able to distinguish between two identical instances in your code because they would have different instanceIds.
There are ways to handle some of these already, this would just make them far easier to do.
To further clarify, there are no specific properties that I am storing. Properties are defined in the components definition which may look like the following.
A package lock file is used to lock specific versions of dependencies where the required version may be ambiguous.
Imagine three modules, A, B and C. The modules have the following properties:
Module A relies on Module C version 1.0.0 to 1.9.0…
Module B relies on Module C version 2.0.0 or above.
Module C does not rely on any other modules.
When I insert one of these modules, I want to ensure that I also have any modules which they rely on.
I insert Module A into my game.
Since Module A relies upon Module C at version 1.0.0 to 1.9.0, I insert version 1.9.0 of Module C into my game.
Now that Module C is in my game, I add a lock file to Module A, telling it where to find Module C.
I then insert Module B into my game.
Since Module B relies upon Module C at version 2.0.0 or higher, I insert version 2.0.0 of Module C despite another version of Module C already existing.
Now that another version of Module C is in my game, I add a lock file to Module B, telling it where to find its version of Module C.
Having custom instanceIds makes it easy to reference the corresponding version of Module C that each of these instances are using, no matter where we place Module C in the games hierarchy.
While the use-case I gave was not brilliant, I was focusing more on being able to save an instanceId to plugin settings. Perhaps a better example would be to detect when a specific save file is opened, or model inserted (assuming instanceIds are persistent for this case).
I was initially confused as to why this would be useful bit while trying to express that confusion I solved my own problem.
Being able to store hierarchy independent references would be incredible. It would get rid of the horrible “FindFirstChild and hope” problem, and get rid of one of the current problems with Roblox’s hierarchy, which is ambiguity. If I have two nearly identical Instances with the same names I can’t easily distinguish between them without comparing properties. Ids would guarantee I was getting a specific Instance.
As a note though, using the internal referent attribute would be a bad idea since there’s actually no guarantee that they’re unique. You can set them to whatever manually and Roblox will import it just fine as long as they all line up.
I need exactly this for serializing which parts were touched in a game replay. The recorded part IDs must survive a map edit, and using an internal engine ID like this is the best way.
I was going to write a feature request for this, but I realized that this is sort of intentional - with streaming, you can’t even guarantee the instance will be there in the first place, so in the end I think what I was looking for was a way to throw in an instance ID through the remote on the server-side, and yield for it on the client, since newly created instances will never exist on streaming, since it’s not ordered anymore.
I am implementing AoE (area of effect) into one of my boss fights, and I started to script up a new system for it so it’ll take less effort in the future. There’s code that runs on the server, as well as the client, for each mob that gets initialized on the server.
While creating this ring effect that expands, I realized that I wanted to create the ring instance on the server, and then replicate tween info to all clients, so they can handle expansion of the ring - this leads to less data needed to be sent across the network, as well as a smoother appearance on each client.
So I create the ring instance, parent it, and immediately run a wrapper which fires a remote to the client.
I passed the Ring instance, the tweenInfo array, and the properties dictionary, which gets received by the client. The client receives everything, except for the Ring instance, which is null.
I even tried yielding for the Instance somehow, if this is just an empty instance, but it quite literally gives me nothing, so there’s no way I could yield for this instance unless if I gave it a Name & Parent to listen to, or use other hacky workarounds. Issue with the first one, is that what if I wanted multiple names? I’d have to setup a separate event to listen to each DescendantAdded in workspace, or ChildAdded in the OnClientEvent function and stop listening & clean-up after a second or so… yeah, I’m sure you can see how this gets pretty messy for something that should be self-explanatory.
One thing to note, is that it appears this happens only in experiences which have StreamingEnabled toggled on. I don’t exactly know how replication works on a deeper level, like how it’s ordered, when exactly they’re sent, hence why I don’t want to just yield on the server and hope and pray that it’ll exist on the server, but it seems that replication is ordered in experiences without streaming, which sort of makes sense.
It appears that a few others had the same issue, and were confused on how to efficiently solve it as well.
TL;DR
As a Roblox developer, it is currently too hard to create & parent instances on the server, and to yield for it to exist by its reference on the client in experiences with content streaming enabled.
It makes sense that since streaming is on, you can’t ever guarantee it’ll be there, because it might be out of their region, or it might be close but streaming conditions won’t allow it to be streamed in instantly. There are cases where I want to yield for it to exist, without having to throw in a parent & give it a randomized name so there can’t ever be cases where when yielding, it grabs one of the few rings which exist already, which share the same name, instead of the one that’s waiting to replicate.
If Roblox is able to address this issue, it would improve my development experience because I’d have an accurate way to yield for a new instance on the client-side which might not be streamed in yet, without having to randomize names and throw in a parent. I feel GetDebugId shouldn’t be used in live experiences.
Create a Part on the server and place a Script with the RunContext “Client” on it, and then make it .Position itself towards really really far away.
You get a funny result, since the StreamingEnabled tries to Stream this strange outstreamed Part back, it will go away again because the RunContext “Client” Script triggers, which is very funny.
You stop this effect by deleting the Part on the Client immediately. It’s as if it doesn’t need to know about it anymore.
For your workaround you’d need to create the Ring on the Client as well idk.
The only reason I like Instance References, is literally due to the main fact, that you have a reference to the actual Instance.
Problem is, it’s kinda pointless if you can’t resolve back into it.
You can create a function, that stores and creates a custom reference to resolve back into it, and send that cusotm generated ID to other environments, such as the client. But then it’s not standardized, but that’s the only workaround if you want to keep a Pseudo Reference towards an Instance
Pseudo References are useful for In-Game Explorers that should support Client and Server view
both are different environments
There’s sooo many instances, I don’t know how good it would even be to store all references in a table,.
Instance’s on the Luau are userdata and those have a memory address, along with something else I feel like, but I don’t know what. But if it can be retrieved from the Lua State, it’d be interesting.
GetDebugId looks nice, at least it would allow the creation of a standardized reference. You can’t do :GetInstanceFromDebugId but at least you have something more standardized.
Also I don’t know how many posts I’ve found about this Instance thing.
But ngl, it would be nice to have something to tell which Instance is which one and what is tied to the server or something.