Action Handler becomes nil

Hi everyone, first post. I am having an issue trying to use ContextActionService to create keybindings for a custom tool system I’m creating.

Specifically, I am using

remoteEvent:FireClient(player, actionName, actionHandler, false, unpack(bindings))

inside of a ModuleScript that I am using as a class. On the server end, my actionHandler is non-nil but when received by OnClientEvent in my LocalScript, I get the error Argument 2 missing or nil since the actionHandler argument becomes nil.

I’ve searched the forums for the same issue but I’m unable to find anything that helps. I’m happy to provide more code if necessary. For clarification, I have ensured that I am not calling the action handler when firing the client.

1 Like

Can you show your full script? (Server and Local script)

The relevant part for the server side is

local function AttachBindings(self, character)
	for _, bindingEvent in ipairs(Constants.BindingEvents) do
		local bindings = self.KeyBindings[bindingEvent]
		if bindings then
			local bindingRemote = ReplicatedStorage.RemoteEvents.BindingRemote
			local player = game.Players:GetPlayerFromCharacter(character)
			bindingRemote:FireClient(player, bindingEvent, self.Handler, false, unpack(bindings))
		end
	end
end

where Constants.BindingEvents is just a table of event names, self.KeyBindings[bindingEvent] is accessing my current ‘object’ to retrieve a table with the keybindings for the given bindingEvent (a string), and self.Handler is the function I pass in to handle events.

The current value for self.KeyBinding that I’m testing with is

{
	Primary = {Enum.UserInputType.MouseButton1, Enum.KeyCode.F}
}

and the action handler for self.Handler is

function(actionName, inputState, inputObject)
	if actionName == "Primary" then
		if inputState == Enum.UserInputState.Begin then
			print("Handler called")
			print(inputObject)
		end
	end
end

and due to the error that this function ends up nil when entering my LocalScript, those statements are not printing.

For the client side, my entire script is

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ContextActionService = game:GetService("ContextActionService")
 
local bindingRemote = ReplicatedStorage.RemoteEvents.BindingRemote
 
local function bindKeys(actionName, functionToBind, createTouchButton, ...)
	ContextActionService:BindAction(actionName, functionToBind, createTouchButton, ...)
end

bindingRemote.OnClientEvent:Connect(bindKeys)

It’ll be helpful also get the full error message.
Is self.handler inside the server script? Can you make sure that the severscript knows that self.handler is a valid function before calling it via print?

It’ll be helpful also get the full error message.

The full error message, as mentioned in the post, is just Argument 2 missing or nil. I am sure that it is coming from trying to call BindAction since no print statements run after that line when tested.

Is self.handler inside the server script?

In the server script I am creating a new tool ‘object’ using the ‘constructor’ below

function Tool.new(Handler, KeyBindings)
	local newTool = {}
	setmetatable(newTool, Tool)

	newTool.Handler = Handler
	newTool.KeyBindings = KeyBindings
	
    return newTool
end

and whenever I intend to equip my tool, I make sure to associate my keybindings through

function Tool:EquipOn(character)
	AttachBindings(self, character)
end

where self is referring to the current ‘object’ I created before with the values I mentioned in my last comment.

Can you make sure that the severscript knows that self.handler is a valid function before calling it via print?

I am not sure what you mean by a valid function (would an invalid function mean that it’s nil?). When I print out the values in the server script, before and after firing the client inside AttachBindings, I get function: followed by its memory address while in the local script it becomes nil.

I believe ActionHandler is a function, please correct me if I am wrong?
You can’t send functions over RemoteEvents, so what I’d recommend you do is to instead store the function in the client. You could do this by having a module parented to ReplicatedStorage, and instead of trying to send a function over, you could send over the name of the function, and do

ContextActionService:BindAction(actionName, moduleScript[functionName], createTouchButton, ...)
1 Like

I believe ActionHandler is a function, please correct me if I am wrong?

Right

You can’t send functions over RemoteEvents

Can you please show me your source for this? Or do you just know from experience? I did not find this when looking for a solution and it would’ve saved me some time haha

But besides that, your method works. Thank you!

I think I answered my own question with BindableFunction | Documentation - Roblox Creator Hub

A BindableFunction is a Roblox object that allows you to give access to functions to external scripts. Functions put in BindableFunctions will not be replicated, therefore making it impossible to use these objects to pass functions between scripts.

I based it on my own experience and some quick testing in Roblox Studio.

1 Like