Issue with metatables and handlers

Hello, I have ran into a roadblock when dealing with handlers and metatables.
Here is an example:

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local GearLocal = {}
GearLocal.__index = GearLocal


function GearLocal:FindControlHit(x, y)
	print(x, y)
	if x == nil and y == nil then return end
	if x == nil and y ~= nil then x = 0 end
	if x ~= nil and y == nil then y = 0 end
	local length = 500
	local unitRay = self.Camera:ScreenPointToRay(x, y)
	local ray = Ray.new(unitRay.Origin, unitRay.Direction * length)
	local part, pos = workspace:FindPartOnRayWithIgnoreList(ray,{self.Player.Character})
	return part, pos
end

function GearLocal:BindBaseHandlers()
	local updateSelectedObjectConn = RunService.Heartbeat:connect(function()
		if self.LastInputPosition == nil then return end
		self.LastSelectedPart = self.SelectedPart
		local x =  self.LastInputPosition.X
		local y = self.LastInputPosition.Y
		self.SelectedPart = self:FindControlHit(x, y)
		
		print(self.SelectedPart)
	end)
	local onInputMove = UserInputService.InputChanged:connect(function(input)
		self.LastInputPosition = input.Position
		print("hi")
	end)
	table.insert(self.Handlers, onInputMove)
	table.insert(self.Handlers, updateSelectedObjectConn)
	
end

function GearLocal.new(tool)
	local newGearLocal = {}
	setmetatable(newGearLocal, GearLocal)
	
	newGearLocal.Tool = tool
	newGearLocal.Player = game.Players.LocalPlayer
	newGearLocal.Camera = game.Workspace.CurrentCamera
	newGearLocal.LastInputPosition = nil
	newGearLocal.SelectedPart = nil
	newGearLocal.LastSelectedPart = nil
	newGearLocal.Handlers = {}
	
	newGearLocal.Tool.Equipped:connect(function()
		newGearLocal:BindBaseHandlers()
	end)
	
	newGearLocal.Tool.Unequipped:connect(function()
		for _,v in pairs(newGearLocal.Handlers) do
			v:Disconnect()
		end
		newGearLocal.Handlers = {}
	end)
	
	return newGearLocal
end


return GearLocal

This runs the error of the following:

GearLocal - Roblox Studio 5_20_2020 8_13_42 PM

It seems as if the metatable no longer exists when “newGearLocal” is passed under an event handler.
How can I accomplish to fix this?

2 Likes

Can you show me what Line 51 actually is? It’s hard to tell, as you are only showing a section of the code.

This is the source of the error; line 51 is “newGearLocal:BindBaseHandlers().”

i’m guessing there is more to your code, but one reason (i can think of rn) you could be getting that error is because you don’t have an __index metamethod anywhere. Maybe try adding GearLocal.__index = GearLocal

I updated this post to show all the code

1 Like

I believe that BindBaseHandlers is a method of GearLocal? You’re using GearLocal as a metatable.

Have you tried declaring newGearLocal like this instead?

Instead of this:

local newGearLocal = {}
setmetatable(newGearLocal, GearLocal)

Try this:

local newGearLocal = setmetatable({}, GearLocal)

If this doesn’t work, you can always try making __index a function that does a rawget on the GearLocal table. Something like this:

function GearLocal:__index(index)
    local fromGearLocal = rawget(GearLocal, index)
    if fromGearLocal then
        return fromGearLocal
    end
end

Another instance where metamethods are not saved is when tables are passed to Remote Events/Functions or Bindable Events/Functions; since this is an event handler, this may be a similar issue.

I tried this, it did not work.

Another thing to point out is if I do call the “BindBaseHandlers()” method outside of the event handler, this will not error.

Try what I mentioned in my edit, and tell me if it works.

I will do some quick 5 minute research on rawget and rawset as I only have but a brief understanding of them.

I know what you are doing with this now; rawget gets the indexed item without calling __index which will end up causing a recursive firing if this was not the case.

But on topic, I have tried your method and it still dose not work.

Have you checked for any possible spelling issues?

Yes I have, it dose call the method without error if it is called outside of the handler

There are also other portions of the script which act the same way; for instance, within “BindBaseHandlers” when it is called without error, the line “self.SelectedPart = self:FindControlHit(x, y)” errors as “FindControlHit” cannot be indexed for some reason

hmm…, i can’t seem to replicate the issue, when copy and pasting your code into a module script and calling GearLocal.new with a tool, nothing errors, and everything seems to work as expected (as far as i know). Solely from your code i can’t see anything that could cause the error you are receiving, So perhaps something else is at play? Does this only happen with the event? is there any specific way you are calling the function?


Also, just as heads up, although this is likely not causing the problem you are having, connect is deprecated, so you might want to use Connect instead.

The script of the following runs it:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LocalGear = require(ReplicatedStorage.LocalGearObjects:WaitForChild("PickaxeLocal"))
--Pickaxe calls GearLocal.new()
local gearLocal = LocalGear.new(script.Parent)

AHA
Finally solved it!!!
There was nothing wrong with the GearLocal module,
There was a problem with another module which was so post to inherit the GearLocal Module
I forgot to connect the two with the following:

setmetatable(Pickaxe, GearLocal)`

This explains why the methods were able to be called outside of the event handler as when the Pickaxe called “GearLocal.new()”, there was a direct ability to call it; however, when it was inside of a event handler, it could not directly be called. This is my guess to what has happened. This may have to do with variable hierarchy → EG Global vars v.s. Local vars.

2 Likes