Page 1 of 3

What is setUserData and why do I need it?

Posted: Thu Jul 11, 2019 5:34 pm
by Jugbot
The wiki wasn't very clear about this and as far as I've seen it looks like a way to tag love objects with simple types.

Am I right in that you cannot add properties directly to a love object (such as Joint.myval = "hi")?

And as an extension to this question, what would I do if I want to modify data through setUserData? Would I have to get-modify-set every time?

Re: What is setUserData and why do I need it?

Posted: Thu Jul 11, 2019 6:52 pm
by raidho36
TL;DR: just ignore it.

This is physics-specific; yes you will have to get-set the data. This feature is generally pointless, considering that you can simply use a table lookup instead and it'll work faster on account of not having to do the C++ to Lua transition and back.

Code: Select all

userdata[physicsobject] = whatever
These non-native Lua objects are "userdata" (Lua terminology) and Lua cannot operate on them, only C++ code can; they can have metatables, so you could fill it up with C++ functions and use these types of objects the same way you use a table, except that you can't actually perform any operation on them from Lua side. This is why you can't write or read any properties from them, but you can execute "class" functions on them. With that in mind, the "set/getUserData" is a confusing name for this function, which basically just attaches Lua value as a payload to it on the C++ side, not actually does anything with userdata. And on top of that, it will actually crash your game if you attempt multithreaded use, because the Lua payload is only valid for whichever Lua thread created it.

Re: What is setUserData and why do I need it?

Posted: Thu Jul 11, 2019 7:22 pm
by Jugbot
I'm glad I asked this question, a lot of this should be on the wiki >.>

Re: What is setUserData and why do I need it?

Posted: Fri Jul 12, 2019 4:15 pm
by TheHUG
When you have some collision callback that only sees two fixtures (to which last I checked you can't assign any attributes as they are not tables), you may want some way to reference the object they came from to e.g. change its health without requiring a global variable. The simplest way is to store a reference to their 'parent' object inside them. That's what I used get/setUserdata for anyway. It was pretty handy in that sense.

Re: What is setUserData and why do I need it?

Posted: Sat Jul 13, 2019 3:32 am
by ivan
Yes, setUserData is very useful when associating a body to a Lua object.
raidho36 wrote: Thu Jul 11, 2019 6:52 pm This feature is generally pointless, considering that you can simply use a table lookup instead
But then you have to modify the table every time a body is created or destroyed.

Re: What is setUserData and why do I need it?

Posted: Sat Jul 13, 2019 8:09 am
by 4vZEROv
It's not useless a all ! You can't directly set values to a body or a fixture (fixture.x = "hello").
I use it in my physics library if you wan't a real usecase :

https://github.com/4v0v/physics

Re: What is setUserData and why do I need it?

Posted: Sat Jul 13, 2019 9:41 am
by pgimeno
ivan wrote: Sat Jul 13, 2019 3:32 am But then you have to modify the table every time a body is created or destroyed.
As for creation, in the other method you have to setUserData anyway, so there's no significant advantage - both methods require an action at creation time. As for destruction, weak tables take care of that automatically. The only extra work is a one-time setup of the weak table.

So for example:

Code: Select all

local bodyData = setmetatable({}, {__mode = 'k'})

function createBody(x, y, extradata)
  local body = love.physics.newBody(world, x, y)
  bodyData[body] = extradata -- instead of body:setUserData(extradata)
  return body
end
As an extra bonus, bodyData is not accessible from other threads so you can't get crashes as you do with setUserData if used from another thread. It can also be hidden from user code, so it can be used in libraries transparently.

As another extra bonus, this method is usable with any object, not just physics objects.

Re: What is setUserData and why do I need it?

Posted: Sat Jul 13, 2019 11:09 am
by ivan
Looks much cleaner using setUserData and it doesn't require the "bodyData" global.

PS. Also, I tried the weak table technique and it didn't work as expected.
Weak keys are collected almost right away regardless of whether the bodies are destroyed or not.

Re: What is setUserData and why do I need it?

Posted: Sat Jul 13, 2019 5:47 pm
by TheHUG
pgimeno wrote: Sat Jul 13, 2019 9:41 am
ivan wrote: Sat Jul 13, 2019 3:32 am But then you have to modify the table every time a body is created or destroyed.
As for creation, in the other method you have to setUserData anyway, so there's no significant advantage - both methods require an action at creation time. As for destruction, weak tables take care of that automatically. The only extra work is a one-time setup of the weak table.

So for example:

Code: Select all

local bodyData = setmetatable({}, {__mode = 'k'})

function createBody(x, y, extradata)
  local body = love.physics.newBody(world, x, y)
  bodyData[body] = extradata -- instead of body:setUserData(extradata)
  return body
end
As an extra bonus, bodyData is not accessible from other threads so you can't get crashes as you do with setUserData if used from another thread. It can also be hidden from user code, so it can be used in libraries transparently.

As another extra bonus, this method is usable with any object, not just physics objects.
The multithreading thing is a pretty big deal if you need good performance, I just don't like the extra shared state in bodyData, but if you keep the module its used in small and encapsulate it neatly, it should work out smoothly anyway. I'll think about doing that for my own physics library wrapper maybe.

Re: What is setUserData and why do I need it?

Posted: Sat Jul 13, 2019 9:33 pm
by raidho36
ivan wrote: Sat Jul 13, 2019 11:09 am Also, I tried the weak table technique and it didn't work as expected.
Weak keys are collected almost right away regardless of whether the bodies are destroyed or not.
Make sure your weak keys and weak values have strong references elsewhere. If their only references are weak ones, they're counted as garbage.