Tables Sent Through Bindable* Lose Data [Fairly Critical]

I’ve been noticing this strange behavior for a long time. In summary, when you send a table from one script to another, the hexadecimal address changes and sometimes data (such as methods in a table-powered OOP) is lost.

I consider this bug fairly critical, as it’s currently hampering all my attempts to create a good object-oriented system with lots of inter-script communication.

This place provides a good repro, and I can tell you that from experience I’ve seen this same behavior in multiple places (for instance, my Defend your Castle with Magic game).

Attached here is a confidential repro place.
[confidential][/confidential]

As a visual example, I’m creating an object in one script and passing it to another script. In both scripts, I print the object and a method of that object. Here is the output (copy-pasted):

table: 1E8D03A8 function: 1E90D678 table: 1E8D0348 nil
This is a really big problem. I need to be able to make sure that when I pass an object between scripts I am actually passing the same object (that is, the same address in memory).

are you doing something like this?

:Fire({table2 = {}})

you have to use the [“”] method instead: I don’t know what that is called qq

:Fire({["table2"]={}})

It is even mentioned on the wiki that bindables and remotes lose the first type of variable and not the second.

EDIT: I misread what OP was doing

This is not a bug, it’s the intended behavior.

That’s because Bindable*'s serialize the objects that you pass through them, even though those objects are still going to the same client. And there’s no way for the serialization to correctly handle the functions that you’re passing, so they’re getting discarded.

You should never ever try to pass raw Lua-side OOP style objects through Bindable*'s for that reason. The references to those objects will not be preserved because they’ve gone through serialization and deseralization before coming out the other end. (And if you have any complicated linked data structures like linked lists or trees they’ll get totally clobbered)

If you really need to pass Lua-side objects through Bindable*'s, then there’s a workaround (Which we’ve used in some of the games team projects). You can pass a lambda function wrapped around your object instead of the object itself, and if you pass that lambda function to the Bindable*'s then they will correctly handle it:

SomeBindable:Invoke('Foo', function() return myObject end)
SomeBindable.OnInvoke = function(action, object)
    object = object()
end

And similar for RemoteEvents.

5 Likes

[quote] are you doing something like this? […]
you have to use the [“”] method instead […]
It is even mentioned on the wiki that bindables and remotes lose the first type of variable and not the second. [/quote]

This is incorrect regardless of what the wiki says. Those two forms you described are semantically identical. The Reflection layer wouldn’t be able to tell the difference between them even if it wanted to.

[quote] This is not a bug, it’s the intended behavior.

That’s because Bindable*'s serialize the objects that you pass through them, even though those objects are still going to the same client. And there’s no way for the serialization to correctly handle the functions that you’re passing, so they’re getting discarded.

You should never ever try to pass raw Lua-side OOP style objects through Bindable*'s for that reason. The references to those objects will not be preserved because they’ve gone through serialization and deseralization before coming out the other end. (And if you have any complicated linked data structures like linked lists or trees they’ll get totally clobbered)

If you really need to pass Lua-side objects through Bindable*'s, then there’s a workaround (Which we’ve used in some of the games team projects). You can pass a lambda function wrapped around your object instead of the object itself, and if you pass that lambda function to the Bindable*'s then they will correctly handle it:

SomeBindable:Invoke('Foo', function() return myObject end)
SomeBindable.OnInvoke = function(action, object)
    object = object()
end

And similar for RemoteEvents. [/quote]

This makes me sad, but I can work with that lambda solution. Where are our TableValues? :stuck_out_tongue:

Another alternative that I’ve used is to recreate the object with the class.new and pass the data from the previous object as an argument in the .new
class.new(data) would run through data if it exists and create the object with a specific set of variables. However you end up losing any custom methods attached to the object. So if you’ve overwritten :OnEat() in that specific object, it will recieve the class’s default OnEat, alternatively in the instantiating function (where the object is “created”) you can edit the functions, so that
if self.type == “food” then
function self:OnEat(p)
p:GiveHealth(11)
end
things like that will work, but passing the objects along with their custom functions can get messy any way you go about it.

[quote] are you doing something like this? […]
you have to use the [“”] method instead […]
It is even mentioned on the wiki that bindables and remotes lose the first type of variable and not the second. [/quote]

This is incorrect regardless of what the wiki says. Those two forms you described are semantically identical. The Reflection layer wouldn’t be able to tell the difference between them even if it wanted to.[/quote]

I get (or, at the very least, got) data loss for tables using the {value = true} method, whereas {[“value”]=true} has never lost any data for me, even for identical tables.

Here’s a Signal class I’ve been using for about a month that allows me to get around this. It hasn’t failed me yet so I think it’s pretty solid.

It returns a Signal creator class that you can use to create objects that resemble RBXScriptSignals. The difference being that there’s a :fire method of the signal.

[quote] [quote=“Spacek531” post=88995]are you doing something like this? […]
you have to use the [“”] method instead […]
It is even mentioned on the wiki that bindables and remotes lose the first type of variable and not the second. [/quote]

This is incorrect regardless of what the wiki says. Those two forms you described are semantically identical. The Reflection layer wouldn’t be able to tell the difference between them even if it wanted to.[/quote]

I get (or, at the very least, got) data loss for tables using the {value = true} method, whereas {[“value”]=true} has never lost any data for me, even for identical tables.[/quote]

[quote] Here’s a Signal class I’ve been using for about a month that allows me to get around this. It hasn’t failed me yet so I think it’s pretty solid.

It returns a Signal creator class that you can use to create objects that resemble RBXScriptSignals. The difference being that there’s a :fire method of the signal. [/quote]

There’s quite a bit of room for improvement still on that Signal class. Check out my implementation, which functions 100% identically to normal Roblox events (other than copying all of it’s signal arguments by reference instead of by value, solving the problem that you’re talking about), and doesn’t have a significantly higher cost than your implementation:

1 Like

[quote] [quote=“Stravant” post=88997][quote=“Spacek531” post=88995]are you doing something like this? […]
you have to use the [“”] method instead […]
It is even mentioned on the wiki that bindables and remotes lose the first type of variable and not the second. [/quote]

This is incorrect regardless of what the wiki says. Those two forms you described are semantically identical. The Reflection layer wouldn’t be able to tell the difference between them even if it wanted to.[/quote]

I get (or, at the very least, got) data loss for tables using the {value = true} method, whereas {[“value”]=true} has never lost any data for me, even for identical tables.[/quote]

it does not really matter what the semantic difference is between the two, because when I first started using bindables and remotes, I observed data loss using the .Value method, and did not observe data loss using [“Value”]. It would have been better if you actually understood what I said in my previous post.

OK. I get this post. But wtf is Bindable*?

Bindable* →
BindableFunction
BindableEvent

Is that a joke or…

Bindable* →
BindableFunction
BindableEvent

Is that a joke or…[/quote]

I don’t think it is, and you don’t have to be rude about it. Everyone doesn’t know what everything means.

Bindable* →
BindableFunction
BindableEvent

Is that a joke or…[/quote]

I don’t think it is, and you don’t have to be rude about it. Everyone doesn’t know what everything means.[/quote]
I don’t think he meant it in a rude way. I’ve seen some users on here joke like that. he answered the question, but wasn’t sure if it was meant to be answered since it could of been a joke.