SignalsService - Connect with us!

[11/25/2021] Invocation Update


SignalsService - KuperStudio

SignalsService is a simple signal system that will facilitate the connection of multiple scripts or for return systems like DataStores where you can receive ‘.Changed’ as a connection type.

What are the benefits the service gives?

  • A simple and easy-to-learn system.

  • It provides multiple connections between scripts that makes it easier and better to use them.

  • Derivation and typing that amplifies your working area.

  • Secure and optimized code, without the need for instantiations or anything that worsens the performance of your code.

Why should I use SignalsService?

SignalsService brings a large and clean workspace that facilitates the connection of multiple scripts and optimizes your code, thus giving you more security.


SignalsService was developed by HottylewrSlip!

HottylewrSlip

SignalsService

3 Likes

This seems to be a signal library, so I have just some questions:

  • Are connections stored in a linked list? Or are you using an array? Arrays a bit problematic on Immediate mode.

  • Does :Wait use task.spawn, coroutine.yield, etc or are you using a Run service event to handle that? You should be handling it using coroutine.yield and resuming it yourself.

These are pretty common mistakes when making a signal API for yourself.

Can’t confirm anything because there’s no GitHub, and I’m on mobile. (It’s nice to have these things as a gist when you’re not into the full effort of making it into a repository)

Slietnick’s already made this in his Knit Github Page under the name “Signals.lua”

Well, thanks for the attention to the project!

The lists are stored in the signal table itself as a meta-table of connections that is directly linked to the service.
As for the use of coroutines or RunService, there is no need, as the service works with the reading of the metatable mentioned above and, of course, the timing methods used are “task”.

For another question that may arise, as I said before, the service stores meta-tables locating all existing connections within the service, after the service is requested, their functions are returned locally to the script and that means functions like " Wait" will not interfere with other requests for the same service.

I’m doing the repository for the systems, I just didn’t do it at the moment because of the lack of time I have.

There are other methods of signals, but this does not interfere with the creation of other services of the same genre. By the way, the current service is a part of a special KuperStudio service that will give you access to several systems that should facilitate general game development.

1 Like

The task library is just another way of messing with coroutines, you are still messing with coroutines. The task library just fixes some issues with the older ways of spawning and resuming threads like errors going away.

I understand, I just meant that they are used task methods, but there are no routines in the project, because the ‘Wait’ example is not an asynchronous method, it is constantly waiting in a loop for some connection activation, after activation of the connection it will continue to follow with the code.

We now have our repository on GitHub!

coroutine.yield has funky behaviour with Roblox YieldFunctions. I’d advise against it.

Elaborate? Do you have any examples? I’ve never had it issues with coroutine.yield but I did with coroutine.resume which I haven’t had with task.spawn.

I’m reposting this from OSS (quoting eevera)

Any C-side code that invokes user code then “waits” for the user code to yield back to the C-side code will be broken, because of the way Roblox models this idea: continuations. Continuations are essentially a set of instructions that travels along with the thread and instructs the C-functions what to do when they’re done running. C-side yield functions have special machinery to pass along these continuations when they are invoked. However, coroutine.resume has no concept of continuations, so when you use it to resume a thread, the continuations are effectively discarded. This means that any pending jobs that were meant to run when the user thread finishes running will never actually run, thus potentially leaving those dependent threads stuck in a yielded state forever.
For an example of this behavior in action, consider the require function. It is a yield function and when it invokes the module thread it passes along a continuation task that asks that thread to resume the requiring script when it’s done. This all happens behind the scenes and you probably wouldn’t realize any of this happens, because if you have a wait(2); return nil in that module, it waits for two seconds and then you get nil back from require in the requiring module.
But if you instead use coroutine.yield() to yield the module thread and then resume it later with coroutine.resume , since that function has effectively discarded any pending continuations that the thread had, the require call from the requiring script will never return a value and that script will be stuck forever in a yielded state.
This behavior isn’t just isolated to require , though. Any time any C-side code invokes user code and waits for it, these caveats will apply. The advantage of using BindableEvent:Wait is that it supports continuations, whereas the coroutine library does not.

Again, this is an issue with coroutine.resume, and not coroutine.yield, because coroutine.resume doesn’t have the concept of continuations.

task.spawn does, so it’s safe to use.

1 Like

Para SignalsService, ‘task.desynchronize’ é usado para paralelizar conexões como ‘ConnectParallel’ e ‘repeat until’ para aguardar conexões como ‘Wait’, os sistemas mencionados são bons, mas respondendo a uma de suas perguntas anteriores, eles não são usados.

For SignalsService ‘task.desynchronize’ is used to parallelize connections like ‘ConnectParallel’ and ‘repeat until’ to wait for connections like ‘Wait’, the mentioned systems are good but answering one of your previous questions, they are not used.

Oh, so this relates to resuming in a thread yielded by C code? I can actually see why this would have issues.

i know english


Your :Wait function is based around the Heartbeat event, because task.wait is.
It will not be immediate because you are using task.wait.

image

Your :Wait function also doesn’t return the arguments that were fired, which is something that RBXScriptSignals do.

This implementation should be a copy here, put there solution, it’s the same as GoodSignal.

function Signal:Wait(): (...any)
    if not self.Name then return end

    local thread = coroutine.running()
    local connection
    connection = self:Connect(function(...)
        connection:Disconnect()
        task.spawn(thread, ...)
    end)

    return coroutine.yield()
end

There’s some other issues with your library like it storing connections in a weird manner, which can leak them, you should be storing them on the signal object you have but other than that there’s the fact you seem to be using strings for as like indexes for connections, which is interesting? I haven’t messed with that, and I don’t know what potential issues it can have, but I would guess there are some with hashtable positions. (complicated subject that i don’t even understand all that well)

1 Like

I saw that your profile is part of the Brazilian community and I am also Brazilian.

It might seem like ‘task.wait’ and ‘Heartbeat’ work in the same way, but this is not true, they both have fast processing but ‘task.wait’ is limited. I won’t be able to explain it, but through some simple tests you will be able to see it.

Maybe I forgot to implant this in the library.
I don’t usually use coroutines, but I must confess that your implementation of coroutines for ‘Wait’ is quite interesting.

Indexes are in strings so that there are not multiple connections with the same index, which also makes disconnection easier.
Unfortunately, the service has a big problem with multiple connections being made in parallel as it won’t be able to capture the total number of connections and therefore won’t be able to connect properly.

Invocation Update

Recently the code has been restructured and improved by containing function derivations that bring you a wide working area and also Invocations that try to reproduce the RemoteFunction system by getting a return when invoking a function.

The documentation and other systems of the service have also been updated, see more about the update in the API wing of the service documentation.


Invocation Example

Script - 1

--// Services
local SignalsService = require(game:GetService('ReplicatedStorage'):FindFirstChild('SignalsService'))

--// Signals
local GetVar = SignalsService.new('GetVar')

--// Variables
local Variable = 'Hello World!'

--// Functions
GetVar:OnInvoke(function()
	return Variable
end)

Script - 2

--// Services
local SignalsService = require(game:GetService('ReplicatedStorage'):FindFirstChild('SignalsService'))

--// Signals
local GetVar = SignalsService.new('GetVar')

--// Variables
local Variable

--// Functions
Variable = GetVar:Invoke()
print(Variable)
2 Likes

Just a quick question, does this work for client → server and server → client or just server → server?

The service only works between server → server and client → client.

For the service, there is no need to use multiple scripts, if you want you can use the system just using a single script.

1 Like