Signal | Create Custom RBXScriptSignal Objects!

Signal

Download

Signal is an open-sourced module used to make custom RBXScriptSignal events. It replicates the behavior while being as light-weight as possible. This makes use of BindableEvents, and also is coded so arguments passed when firing a Signal does not get deep copied when being sent through the BindableEvents.

Where can I download it?

You can download and view the module with the following links:

Roblox Download

What about documentation?

For complete understanding of the module, it is highly recommended you view the following documentation:

Signal.new

Used to create a new Signal object to use :Fire, :Connect, etc functions on.

Usage:

Signal.new()

Parameters:

nil

Returns:

Signal

Signal:Connect

Connects a function to whenever the Signal fires

Usage:

Signal:Connect(handler)

Parameters:

handler -- The function you wish to connect to the Signal

Returns:

Connection -- The connection between the function and Signal

Aliases:

Signal:connect(handler)

Signal:Wait

Yields until the :Fire method is called on the Signal.

Usage:

Signal:Wait()

Parameters:

nil

Returns:

... -- All parameters passed by the :Fire() function that triggered the :Wait() function

Aliases:

Signal:wait()

Signal:Fire

Fires the Signal and triggers any functions connected with :Connect() and stops yielding any current :Wait()s.

Usage:

Signal:Fire(...)

Parameters:

... -- Any amount of parameters

Returns:

nil

Aliases:

Signal:fire()

Example of use:

local Signal = require(script.Signal) -- In this example, it assumes that the module is located under the script. Change this to wherever your module is located.

local Clicked = Signal.new() -- I created a signal for whenever the player clicks

Clicked:Connect(function(Count)
	print("Click detected! Click #"..Count)
end)

local ClickCount = 0

game:GetService("UserInputService").InputBegan:Connect(function(Input) -- This uses UIS to detect whenever input begins
	if Input.UserInputType == Enum.UserInputType.MouseButton1 then -- Checks if the input is a left click
		ClickCount += 1
		Clicked:Fire(ClickCount) -- I pass the amount of clicks here when firing
	end
end)
Source Code
-- I did not include debug traceback to make it as light-weight as possible
local Signal = {}
Signal.__index = Signal
Signal.ClassName = "Signal"

-- Constructor
function Signal.new()
	return setmetatable({
		_bindable = Instance.new("BindableEvent"),
		
		_args = nil,
		_argCount = nil, -- To stay true to _args, even when some indexes are nil
		
	},Signal)
end

function Signal:Fire(...)
	-- I use this method of arguments because when passing it in a bindable event, it creates a deep copy which makes it slower
	self._args = {...}
	self._argCount = select("#", ...)
	
	self._bindable:Fire()
end

function Signal:fire(...)
	return self:Fire(...)
end

function Signal:Connect(handler)
	if not (type(handler) == "function") then
		error(("connect(%s)"):format(typeof(handler)), 2)
	end
	
	return self._bindable.Event:Connect(function()
		handler(unpack(self._args,1,self._argCount))
	end)
end

function Signal:connect(...)
	return self:Connect(...)
end

function Signal:Wait()
	self._bindableEvent.Event:Wait()
	assert(self._argData, "Missing argument data, likely due to :TweenSize/Position corrupting.")
	return unpack(self._args, 1, self._argCount)
end

function Signal:wait()
	return self:Wait()
end

function Signal:Destroy()
	if self._bindable then
		self._bindable:Destroy()
		self._bindable = nil
	end

	self._args = nil
	self._argCount = nil

	setmetatable(self, nil)
end

function Signal:destroy()
	return self:Destroy()
end

return Signal

If you have any questions at all, do not hesitate to post a reply to this thread. Thanks for reading!

31 Likes

Mainly, its just to simulate RBXScriptSignal objects and solve a few minor issues with BindableEvents.

2 Likes

In addition to this, using bindable events, Roblox deep-copies parameters before sending them when you fire the event. This is resolved in this so it makes sure that parameters are not deep-copied.

Also this provides a nicer interface for events and makes the overall experience less messy.

8 Likes

Some functions in the module are directly copied from Quenty’s signal module, such as Signal:Connect, Signal:Wait and Signal:Destroy is a near copy as well.

Overall, the module looks very similar to Quenty’s signal module and does the exact same job, why use this instead? This module is just a copy, and doesn’t provide anything useful than other signal modules.

I wouldn’t use this module, useless.

5 Likes

As @SilentsReplacement said, this is legit just a carbon copy of Quenty’s signal module. The source code from your module and Quenty’s is the exact same, and traditional Object Oriented Programming doesn’t include the following line:

module.ClassName = "CustomClassName"

I’ve really only seen Quenty do that, since he has a custom class module in his NevermoreEngine called “ClassName”.

Not to mention your variable names happen to be the exact same, and the comments are just a simplified version of what Quenty’s comments say.

Please don’t come on here taking other peoples work and claiming that it’s yours. That’s a really wrong thing to do.

5 Likes

I hate to agree with you, but yes. (Though there wasn’t a purpose bumping this just to say that)

It should break after some time if you :Fire multiple times with Deferred Events on, + Quenty’s was never compatible with firing inside listeners anyway;

What I did on mine was have it so that arguments aren’t “_argData” but instead, each fire has their key inside the Signal object as a increasing number (turned out the same as Knit’s)

With Deferred Events though, it breaks apart. Problem is the connections only start fire after I clean up the arguments, on my solution I ended up just not cleaning them up at all.

1 Like

I have fixed up Knits signal module to support deferred enabled with just a few lines of code and events, look up my reply in the deferred events thread for the updated code.

1 Like

@OnlyRoids
@SilentsReplacement

Now that you mention it, it does look a whole lot like Quenty’s version. It is possible I may have seen Quentys version when searching a while ago and some of it just stuck in my brain and I unknowingly wrote a carbon-copy of quentys version.

Would I go as far as to say it is totally useless? Absolutely not. That is not the correct tone when replying to someone. You are saying it in a very accusatory way. I did not do it on purpose. Considering that stuff between you and I are cool, I would hope that you don’t talk to friends the way you talk to me.

1 Like

My post is no place to advertise your signal module. If the only reason you are here is to advertise, please leave. I have opted to flag your post. Think before posting!

2 Likes

@iamtryingtofindname

I am not saying it in a very accusatory way, sorry if it’s like that. Even if you carbon copied Quenty’s module without knowing, it doesn’t take away the fact that it is in fact still copied.

Would I go as far as to say it is totally useless? Absolutely not. That is not the correct tone when replying to someone. You are saying it in a very accusatory way. I did not do it on purpose. Considering that stuff between you and I are cool, I would hope that you don’t talk to friends the way you talk to me.

Yes, it is infact useless as it doesn’t provide any useful functionality than other signal modules. What point do you intent to make?

My post is no place to advertise your signal module. If the only reason you are here is to advertise, please leave. I have opted to flag your post. Think before posting!

Please read my posts clearly, never have I ever advertised my signal module. It was just a solution to fix up Knit’s signal module to support deferred events. As Lucas’s signal module turned out as Knit’s signal module, I provided him a solution.

3 Likes

That it is still usable and people can still learn from the code.

Whether you think of it as advertisement or not, it is still off-topic and should be avoided.

It’s ok. When you communicate with other’s with text only, it is easy for messages to be misinterpreted.

1 Like

You can do this without BindableEvents, you don’t need to create extra instances.

local signal = {}

function signal.new()
	return setmetatable({}, {__index = signal})
end

function signal:Connect(callback)
	self.callback = callback
end

function signal:Fire(...)
	if self.callback then
		self.callback(...)
	end
	if self.thread then 
		coroutine.resume(self.thread) 
	end
end

function signal:Wait()
	if self.callback then
		self.thread = coroutine.running()
		coroutine.yield()
	end
end

return signal
2 Likes

This doesn’t support multiple connections or multiple :Wait’s

1 Like

You can just add that, this was just an example.

2 Likes

You kind of can, but it’s not just a question of having an array of functions, it doesn’t work properly in certain situations if you do that. BEvents are the easiest way to do it, + it respects usual Roblox Signal behaviour. It’s kind of hard because you need to understand data structures which is something I believe isn’t as much as a priority with Lua than with other programming languages, so I never touched any of that;

1 Like