I know that instances metatable are locked so I made a class that modify the instance, in this case the local player. Currently if ever I want to print the player, I used the __call function so when called it just returns the player; However instead of doing this is it possible to just return the player when the table is called, like:
print(modifiedPlayer) -- returns the table
-- instaed of
print(modifiedPlayer()) -- returns the player
Script
local function ReturnMethod(index)
return function(...)
local args = { ... }
args[1] = PlayerObject
return PlayerObject[index](unpack(args))
end
end
local LocalPlayer = setmetatable({}, {
__index = function(_, index)
local Success, Value = pcall(function()
return PlayerObject[index]
end)
if Success then
if typeof(Value) == "function" then
return ReturnMethod(index)
end
return Value
end
end,
__call = function()
return PlayerObject
end,
})
... -- methods
I found out that you can use the __tostring but not the solution I want since this requires to return a string so doing something like this is not possible:
__tostring = function(_)
return PlayerObject -- must be PlayerObject.Name
end,
Edit: Also this only works if tostring function was used on the table
I think the only possible way to do this (and I’m not even sure if it would work) would be to alter the environment using setfenv. To be honest what you’re trying to do seems like a bad practice anyway. If you have created an extension class for players I don’t see why not just access the built-in player instance via an attribute e.g. modifiedPlayer.Instance.
The only thing on the class are methods so accessing the attribute would still be the same as using the instance expect this time there are other methods not just the default methods that the instance have.
Like getting the character and include some stuff or get the tool and equip it:
local Character, HumanoidRootPart = LocalPlayer:GetCharacter("HumanoidRootPart") -- and more
LocalPlayer:GetTool("Sword", true) -- this method checks both the Backpack and the Character, the second argument is equip
This also still allows you to access the PlayerGui, UserId etc, simply by just indexing the object you wish to access:
About this I’m not to sure if whether this really is a bad practice or not, seems to me it’s just the same as accessing the PlayerObject but with added methods.
print(modifiedPlayer) -- returns the table
-- instaed of
print(modifiedPlayer()) -- returns the player
This sort of behaviour / trying to get the reference to the modifiedPlayer to somehow refer to the base player instance.
Having all the extra helper methods is fine. My suggestions boils down to getting rid of the __call metamethod usage as it currently stands and adding an attribute to your class which references the built-in player.
In code my suggestions boil down to changing to something like this:
local ModifiedPlayer = {}
function ModifiedPlayer:__index(key)
if ModifiedPlayer[key] then
return ModifiedPlayer[key]
else
return self.Instance[key]
end
end
function ModifiedPlayer.new(player: Player)
local self = {}
self.Instance = player
return setmetatable(self, ModifiedPlayer)
end
function ModifiedPlayer:Foo()
return self.Instance:GetFullName()
end
-- other methods
local player = ModifiedPlayer.new(game.Players.LocalPlayer)
print(player.Name)
print(player:Foo())
Although this doesn’t seem to play well with the default player methods (attributes are fine though). I’m not sure how your code addresses that either as the error I get is:
"Expected ':' not '.' calling member function GetFullName"
from doing player:GetFullName() directly. You may want to check for that bug in your current code too.
My solution to that was create a function then return another function since self is the reference of the table not the player object which is this:
local function ReturnMethod(index)
return function(...)
local args = { ... }
args[1] = PlayerObject
return PlayerObject[index](unpack(args))
end
end
The __call was for the server side for firing an event to the player since local Player = ModifiedPlayer.new() is table and doing something like this Player.Instance seems that __index rather pointless and just change it to ModifiedPlayer. This was my main question is it possible to instead of Player() just use Player, I have been searching on google about this and still no luck.
My original response included the only way I think this is possible, i.e. use setfenv to overwrite the Player reference. This still seems like a worse way to go about things than to just use something like Player.Instance.
As for your function wrapper thing I don’t see how that solves the issue as it still uses PlayerObject[index] which is the problem expression.
Edit: I have tested your code and agree that your version does solve the error I just don’t understand how yet ;-;
I saw that but, I have no idea how that works all I know is that the first argument is a function and inside the function you can change the variable’s value from the second argument.
That’s because self is the reference of the table not the player object, doing so I need to change the first index to player object, so self is the reference of the player, not the table, this only works on methods, since all of the available function on an instance are method this is the only solution to that.
Okay happy days I get it now, here is the new version of what I would say is a better way to go about this.
local ModifiedPlayer = {}
function ModifiedPlayer:__index(key)
if ModifiedPlayer[key] then
return ModifiedPlayer[key]
else
local value = self.Instance[key]
if type(value) == "function" then
return function(...)
local args = {...}
args[1] = self.Instance
return value(table.unpack(args))
end
end
return value
end
end
function ModifiedPlayer.new(player: Player)
local self = {}
self.Instance = player
return setmetatable(self, ModifiedPlayer)
end
function ModifiedPlayer:Foo()
return self.Instance:GetFullName()
end
local player = ModifiedPlayer.new(game.Players.LocalPlayer)
print(player.Name)
print(player:Foo())
print(player:GetFullName())
Thanks for the help, but I’ll stick with the __call metamethod, not sure I just don’t like seeing Player.Instance or Player._Instance. Other than that I don’t think it’s really possible to change the reference of the table.
I do know for sure that exploiter can do that I just saw it a while ago on youtube, I think it was called hookmetamethod. It seems that this function allows you to hook to an instance metamethod without activating its __metatable.
Turns out you don’t need setfenv and can just use getfenv. This should give a rough idea of how to switch it out (again not recommending you actually do this as it turns off some optimisations that luau does).
getfenv just create a global variable so it’s still not possible. I use it on variable name Screen which is a table with width and height. The same as on Unity though mine only has 2 index and no funcitons.