ObjectValue Instance Problems. Properties that hold an Instance require innovation, RefPropDescriptor

Description

While this may not seem like a bug. It’s an issue I encountered once which I never expected.

There can be a case where an Instance is bound to a ObjectValue, which has yet not been initialized and returns nil. Even though it’s statically linked. Workarounds exist, but there shouldn’t be.

Additionally there are issues when using “Cut” in Studio on an ObjectValue’s bound instance, and pasting it back. There aren’t issues for cloning ObjectValues.

Theoretically, Engine Developers could fix this, by checking if the referent is available in a ObjectValue, to then replace it automatically. But I’d to bring into awarness that eventually it may break someone elses workflow.

However… another issue related to this is for PLUGINS

Developers can't copy multiple Instances like the Engine can. The Engine is able to re-connect ObjectValue's that have been copied with no Parent container node. Developers don't have this logic by default, and need to implement one by themselves.

You can copy paste things like so:

image

The Engine is somehow capable of linking the right things back together. We developers can only achieve this by putting both of these things in a Folder and Cloning the Folder. But for Plugins… no chance

for k,v in ipairs(game.Selection:Get()) do v:Clone().Parent = game.Workspace end

this wouldn’t work, I don’t know how the built-in method does it, but it’s part of the issue

I am glad, that the Engine doesn’t break ObjectValue’s for StarterGui, but only if it’s contained inside a container.

Basically, things get cloned, but for things outside of containers, they do get cloned, but they won’t reconnect their cloned things. This is a very slight issue. But it should emphasize what I am trying to say with, how the Engine can copy things and re-connect them, and how developers can’t do that without actually implementing logic, while the Engine has it.
image

 

ObjectValue a Instance that holds another Instance as a Value. Very useful to link things together. Designed to be simple bridges in Scripts. But simplicity breaks, because there’s complexity that ObjectValue Instances were not prepared for.

It has problems.

Problems that I’d not like to solve with something like this

while (ObjectValue.Value == nil) do
    task.wait()
end

This above code, works. but… I don’t like it.
I don’t want a ModuleScript Handler for one thing. I like C++ implementations. They’re faster, they’re built-in, they’re supported.

 

I am looking at a ObjectValue something designed to hold values. But currently it’s nothing but something like all the other Value Instances. Something that is just Value. This does not work for Instances. Instances need a help-care support to work with ObjectValue’s, which is not available.

Problems that can’t be solved with .Changed because if the Instance already exists it won’t trigger Changed. And Changed seems to listen for Changes for the referent. There used to be a bug report about moving things from Client to Server, not triggering Changed or something, or destroying the instance. That’s normal. If you want to change the ObjectValue’s Value. Change the Value.

 

We are talking about something that you can’t see, which seems to be referred to as RefPropDescriptor, the referent value.

The referent value seems to be an Instance’s ID, used for Replication across Server and Client.

 

When you assign something, this is how it looks like

<Item class="ObjectValue" referent="RBXF35BF9CC7CB04266BBCE5BB07A6CA382">
	<Properties>
		<BinaryString name="AttributesSerialize"></BinaryString>
		<SecurityCapabilities name="Capabilities">0</SecurityCapabilities>
		<bool name="DefinesCapabilities">false</bool>
		<UniqueId name="HistoryId">00000000000000000000000000000000</UniqueId>
		<string name="Name">Value</string>
		<int64 name="SourceAssetId">-1</int64>
		<BinaryString name="Tags"></BinaryString>
		<UniqueId name="UniqueId">1672313e268e785a06b1e17200004812</UniqueId>
		<Ref name="Value">RBXB41D84CE01954EB88936335940F57DF8</Ref>
	</Properties>
</Item>

 

The issue

  • Value always returns nil, this is fine.
    • What’s not fine is that there’s nothing else.
  • You’re unable to check if an ObjectValue is bound to something.
    • In this case I bound an ObjectValue to a server-side Object. As it is a permanent way to demonstrate the issue.

Further issues occur internally on Roblox.

The AccessoryDescription holds a value called Instance, which is linked to a Instance (a referent). It is going to have the same issues, just like ObjectValue. And it’s a question whether Roblox themselves is having repetitive code for that in their Engine.

 

I can’t verify whether ObjectValue is reliable for all cases, the Engine seems to pause things when someone alt-tabs or something.

Re-production Steps

ObjectValue_test.rbxlx (75.4 KB)

  1. Run the game
  2. nil is being printed
  3. Comment out the while loop
  4. nil won’t be printed anymore, it will print TextLabel
  5. You have just witnessed a Luau workaround for this issue.

This is expected. This is not a bug. But this is the demonstration of the issue. Since, the ObjectValue is put inside ReplicatedFirst it’s replicated first! Before anything else.

Expected Result

Built-in implementations that make ObjectValue more robust against performance and replication cases.

Currently, you can compare ObjectValue to a Symbolic Link Folder on Windows, without a :WaitForChild feature.

Actual Result

  • You can’t check whether a ObjectValue is actually bound to something or not, in its Value Property. EVEN Though the Reflection has information for it.
  • There’s no built-in implementation to WaitForInstance (Only if it’s bound to something)

And due to that, it’s not a simple bridge anymore.

 

Additional notes.

I had the idea that ObjectValue’s could only be created if their Value exists. Don’t actually do that. I believe it’s an awful design. And it won’t work with AccessoryDescription either.

There’s no Attribute for Instances. I don’t see the issue why there can’t be one. But there is an issue for implementing one. Since the ReflectionMetadata would call this type of Attribute an Instance, it would not expose extra methods…

Perhaps there will have to be a new type of thing called InstanceRef, a property that holds a reference to an Instance, with designed methods to retrieve the Instance if it is available. (This is sorta reflecting me back to ObjectValue)

Thanks for all the info!! Looking for someone to check this out.

2 Likes

Until we have a better solution for this, you can use
local value = ObjectValue.Value or ObjectValue.Changed:Wait()
which gives a WaitForChild-like behavior.

:thinking: I had to test this.

image

Thus are all Client Scripts.

Test A:

local test = game.ReplicatedStorage:WaitForChild("TheObjectValue")

local abc = test.Value

print(abc)

Result is nil

 

Test B:

local test = game.ReplicatedStorage:WaitForChild("TheObjectValue")

local abc = test.Changed:Wait()

print(abc)

Result, it waits and then prints the result, aslong a change happens.

 

Test C:

image

with the same script as Test B, results in infinite waiting, because .Changed won’t trigger here, because there’s nothing to change

 

So it’s either this or nothing at the moment.

local test = game.ReplicatedStorage:WaitForChild("TheObjectValue")

if (test.Value == nil) then
	test.Changed:Wait() -- Yield until it triggers
end

print(test.Value)

 

Cloning ObjectValues with their references is still an issue compared to how Studio can do it.

Yup, cloning is still an issue! Also, try the full code fragment I sent - the or should stop Test C from happening as there was already a value, so it short-circuits and doesn’t wait for .Changed.

1 Like

oh… or, I see it now, that makes more sense.

For the previous message, I saw it as local value = ObjectValue.Value OR the other one

instead of:

local value = ObjectValue.Value or ObjectValue.Changed:Wait()
1 Like