fivem icon indicating copy to clipboard operation
fivem copied to clipboard

Exports does not return metatable attached to the table

Open MatinHQ opened this issue 3 years ago • 4 comments

Description: Exports does not return metatable attached to the table

Repro:

  1. Create a class on a resource and inherit from that class and also add an export for the inherited class
Player = {
    health = 100,
    new = function(self, o)
		o = o or {}
		setmetatable(o, {__index = self})
		return o
	end
}

Police = Player:new({
    armor = 100,
})
console.log(Police.armor, Police.health)
-- Result will be 100, 100

exports("Police", function()
    return Police
end)
  1. On other resource call the export and try to fetch inherited class
Police = expoorts.base:Police()
console.log(police.armor, police.health)
-- Result will be 100, nil and if you try to get metatable will be also nil
getmetatable(Police) --> result will be nill

Expected behavior: exports should return metatable attached to the table

MatinHQ avatar Aug 14 '21 10:08 MatinHQ

Returning an object with just the original metatable replicated by function references would be weird: what meaning would a metatable have in other ScRTs? This'd need a lot of thought which comes down to the original concept of allowing 'meta-refs' which are just proxy objects where every field access is a metatable-like callback marshaling back to the original ScRT.

blattersturm avatar Aug 14 '21 10:08 blattersturm

Instead of relying on this being 'expected', it would be possible to return a closed wrapper object with just getter funcrefs, as the same would have to happen when using a metatable anyway.

blattersturm avatar Aug 14 '21 10:08 blattersturm

Wouldn't "table/object marshaling loses metatable" or something similar make more sense as a title?

kngrektor avatar Aug 14 '21 11:08 kngrektor

While initially meta-refs seemed logical, I believe the whole initial point of needing to share classes between resources is incorrect. Unless you want to import them (through the manifest file, as in LUA for example), sharing execution contexts in this specific case isn’t a great design choice.

IMO, and as an alternative, I would suggest having the two resources agree on a mutual definition and simply think of their communication as an API, where you remotely call different methods that interact with that class. In sum, exporting police would simply become export(“PoliceArmor”), export(“callMethodXInPolice”) or simply export(“Police”) returning a serialised version of the class, similarly to routes.

(Related to https://forum.cfx.re/t/exporting-a-class-between-two-resources/2066783/4)

plourenco avatar Oct 05 '21 11:10 plourenco