#Signal Certifications & Class-Grader

1 messages Β· Page 2 of 1

scarlet pecan
#

I mislabeled DisconnectAllMutations when I suggested it mb, I assume your implementation affects :ChangePriority() & :Disconnect() mutations as well?
If it's just Nested Connections, it may be more accurate to rename to DisconnectAllNested instead

stuck thorn
#

sure some fields (priority, disconnect [depending on the layer]) might change but at max you will only see that the connections aren't connected anymore

scarlet pecan
#

Gotcha 🀝

#

New test, it doesn't test mixed yet (I gotta observe what the new handling of Layer 1 mutations for Nested Connections is like)
Your current version should be able to pass as it is

stuck thorn
#

Slipped with my wording I see but I meant future nested connection

stuck thorn
scarlet pecan
#

You good if I have a copy of it? I'm gonna try to make up a few practical scenarios between Nested & non-nested connections to visualize what it'll be like working with it

stuck thorn
#

Though your implementation is at a cost, firing cost to be exact

scarlet pecan
#

How expensive?

stuck thorn
#

on my machine I failed every fire test except fire_none

scarlet pecan
#

WHAT

#

So the Fire_ tests are past the 2-3 microsecond range???

stuck thorn
#

that's just how weak my pc is lol

#

on roblox servers it averages ~2.2ms

scarlet pecan
#

😨

stuck thorn
#

that's how expensive the proper signal is lol πŸ’”

scarlet pecan
#

GAH DAYUM

#

Forget my "optimization" LMAO

#

Your thread idea is better for speed, I just wanted to save on memory 😭 (3 Bytes is 3 Bytes πŸ€‘)

stuck thorn
#

LOL

#

I JUST REALIZED WHAT I DONE

stuck thorn
#

though, was it worth adding 1ms more to firing speeds just to have "correct" behavior when disconnecting

stuck thorn
stuck thorn
#

this is RIGGED brah

stuck thorn
scarlet pecan
scarlet pecan
stuck thorn
#

ah yes, repeat until good

scarlet pecan
#

very scientific process there's no bias trust me bro 🀝

stuck thorn
#

which is to say, you copied connect code to once

scarlet pecan
#

guh!?

#

I mayhaps lost the plot

stuck thorn
#

Funny how most basic signals are 100 lines yet this almost reaches 600 lines

#

what have I made

scarlet pecan
stuck thorn
#

although what did you save bytes on

#

πŸ€‘

#

okay so it varies, it might be 1.5 or ~3.5 (watch mobile device get like 50+)

scarlet pecan
#

I might pivot the margin of acceptance speed to be percentage-based off of the baseline rather than fixed values so that the Certifications test can be used no matter how crappy the device is πŸ”₯

#

In the meantime if anybody asks, just put it on my soul that it passes the Speed Cert

stuck thorn
stuck thorn
#

but Signal should now be "finished" "finished" as it does what you ask it for + it no longer yields at everything (unless it's fire, but that also has a fail-safe)

#

these are my tests on client (I should see if not using threads while doing queue would be faster)

#

I believe that's where all the big bucks are going to waste

scarlet pecan
stuck thorn
#

yeah okay it did no difference but I know that putting that there was redudant

scarlet pecan
#

That should read less than 2k, not 200k :D

stuck thorn
scarlet pecan
stuck thorn
#

actually that's acceptable now

stuck thorn
#

guess ill post this and boom this totally could be the last version and it'd last to who knows

#

I should go to sleep, it's almost 11 pm

scarlet pecan
stuck thorn
#

Why, this signal already peaks

#

Fanciest wine of them all publicly

patent ore
#

Wtf is happening here 😭

scarlet pecan
#

CHANGELOG

Speed Certifications Change

  • Fixed a CRTICAL bug to do with the Fire_OneYield & Fire_ManyYield tests. They now have a debounce between each sample so they won't create anymore threads.
    • Tweaked sample & connection amounts, less bench samples (1k -> 100) but more connections (100 -> 1k). Results are now a little more accurate to practical workloads and only take 10-15 seconds.
scarlet pecan
scarlet pecan
stuck thorn
#

what is it about

scarlet pecan
stuck thorn
#

now thats the issue with the fact that... WE DONT SEND IT BEFORE FIRING

#

ngl its better off to just have a signal flag about layer2

#

than each connection

scarlet pecan
#

Sounds good as long as it works with DisconnectAll πŸ”₯

stuck thorn
#

it should?

scarlet pecan
stuck thorn
#

in a sense that it happens after fire, but before processing queue

stuck thorn
#

we save performance on... not setting booleans 10k times per second

#

try that approach

scarlet pecan
#

ye it should work, the setting boolean thing was for a problem that no longer exists

stuck thorn
#

Now I just realized

#

this now not gonna work becsuse it'll them all equal 😭

#

wait, nvm

scarlet pecan
stuck thorn
#

but I have a strange deja vu that it will not work

scarlet pecan
stuck thorn
#

you should run the old tests just incase

#

trust

scarlet pecan
#

Will do once the flag implementation is done πŸ‘ (send tomorrow* pls πŸ™)

scarlet pecan
stuck thorn
#

but ig ill see what I can do tomorrow

stuck thorn
#

okay I realized I could use next and previous values to determine it I found a better idea

stuck thorn
#

Okay I believe I got it right, but I can't really do that with :ChangePriority(), unless I add another value for that

#

it should be good now, unless there's some magic (let me know what I should fix)

stuck thorn
stuck thorn
#

I find that weird lol, on roblox servers doing once connection is actually slower, but rest is good enough

scarlet pecan
#

Okay, the FINAL step is to visualize how to deal with nested connections using this Signal (not to look for problems, but to know what it currently looks like when working with it)

stuck thorn
#

It should be handled by default

scarlet pecan
stuck thorn
scarlet pecan
#

Gotcha πŸ”₯

stuck thorn
#

that's kinda how I figured out how to make deferred disconnect or instant disconnect

#

(all roads lead to queue though as then there'd be no guarantee that it'd be in order if I done that INSTANTLY)

stuck thorn
#

amazing

#

So pretty much just leaking threads

scarlet pecan
#

I was honestly fine with the "good enough" speeds that it had before purely because of what it offered, but to think that it actually was on-par with non-Class 3 Signals speeds this entire time despite having a MORE COMPLICATED STRUCTURE with EXTRA FEATURES is absolutely incredible

#

ts is the kind of thing I was talking about of anybody even remotely having a chance to surpass your Signal, I am actually certain now it'll remain the best for a long time

stuck thorn
#

I still believe the only thing which can now screw you over is abusing priority connections

#

(it'll get expensive depending on how much you use)

scarlet pecan
stuck thorn
#

oh truly give or take 1000, worst case scenario it's ~100 micro seconds per new connect / change priority

scarlet pecan
#

not that bad for a few of its use-cases tbh

#

Even with the slower speed for sorting, I'd be fine with it as long as it's used sparingly or in clusters.
Priority clusters are grouping an entire signal's worth of connections as one priority.

Cluster example using signal wrapping:

local TickSignal_Stepper = signalMD.new()

local TickSignal_Damage = signalMD.new()
local TickSignal_Heal = signalMD.new()

local tickCN_Damage = TickSignal_Stepper:Connect(function()
  TickSignal_Damage:Fire()
end, 1) -- Every connection in this Signal is considered as priority == 1 using this connection as a proxy

local tickCN_Heal = TickSignal_Stepper:Connect(function()
  TickSignal_Heal:Fire()
end, 2) -- Every connection in this Signal is considered as priority == 2 using this connection as a proxy

TickSignal_Stepper:FirePriority()
stuck thorn
#

Though I think the better change is that having nested connections doesn't yield threads anymore

#

^ so a free speed-up woo!

scarlet pecan
#

oh bet πŸ”₯

stuck thorn
#

Though I still wonder what bugs could be lurking, at worst it'd be just that some connections connect after DisconnectAllMutations call because there was reentrancy fire blocking said connections

scarlet pecan
#

Gonna test that out 😈

stuck thorn
#

that's a guaranteed behavior, you can't change that lol...

#

because if it's OK, then I guess this might aswell be the V2 as it brings lot of improvements lol

scarlet pecan
#

There's an issue with DisconnectAll, it'll disconnect Nested connections made after it

#

Which conflicts with another observation that DisconnectAll doesn't affect existing Nested connections (this part is expected, the one above ^ isn't)

stuck thorn
#

I thought we kinda went over this no?

scarlet pecan
# stuck thorn I thought we kinda went over this no?

That DisconnectAllMutations should be used for Nested Connections right?
The issue here is that DisconnectAll decides to affect Nested Connections based on ordering (contradictive behavior)
Nested Connection made before -> Unaffected (remains connected even after Fire is complete)
Nested Connection made after -> Disconnected

stuck thorn
#

guess ill see what's up

#

unless it's simple as setting the queue connections Connected to false

#

actualy I think ill bench it myself just for "sake" of speed

scarlet pecan
#

But seeing it disconnect future nested connections in practice, you might need to implement range-clearing instead

stuck thorn
#

interesting that there's a loss of :once connection

scarlet pecan
stuck thorn
stuck thorn
scarlet pecan
#

seems like it πŸ’”

stuck thorn
#

So im taking that as not a bug

#

unless you really want to wait for each :fire() to finish it's chores (even if it yields)

stuck thorn
#

queue on queue, literally

#

another disconnectall method called DisconnectAllInThisRange

scarlet pecan
#

I usually separate the current connections list from the signal if DisconnectAll is called (so that it's empty for the next connection insertion and it becomes the head), but I gotta look at your script to see if there's something specialized to be done

stuck thorn
#

ah yes, let me just like create a segment

#

from index 1 to index n and then continue chain off that

#

oh wait a minute, this should be a "simple" fix

#

but code logic goes to hell for this

#

Actually I forgot what I was thinking about

#

Because im more thinking about, how would the range-clear even work

#

^ or should I say I have no concept of how it'd work

scarlet pecan
#

So that by the time it reaches DISCONNECT_CURRENT, you can do i= 1, (cut-off length of queue) instead of the full length of the queue

stuck thorn
#

insert 2nd connection screwing that cut-off length of queue

scarlet pecan
#

RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGH

stuck thorn
#

like my idea is more of

#

just track what connections were made during firing, if disconnect all was called then store the segment of what it needs to disconnect lol

scarlet pecan
#

so a 2nd queue?

stuck thorn
#

not really, it's be a array at that point lol...

#

with provided "slices"

#

actually dang it, I probably wouldn't be even be able to track that

#

you truly gave me a problem

#

hmmm

stuck thorn
#

nah this shi finna make me cry

stuck thorn
#

Okay I think I got it

#

although now DisconnectAll doesn't feel like DisconnectAll

#

(it just range-clears + currently connected ones)

#

So apparently that never connected to signal like ever

scarlet pecan
#

Issue 1: Nested connections seem to not connect at all
Issue 2: Reentrant Fires don't run
^ May be a race condition issue, nested connections might be only made after Fires are done (asynchronous)

stuck thorn
#

I feel like this problem is gonna be worse than I imagined

scarlet pecan
#

Do you have the prior version saved?

stuck thorn
#

Yeah

#

Wouldn't be me if I didn't create a copy before I do something which breaks signal functionality

stuck thorn
#

So have I dug myself a grave? πŸ’”

scarlet pecan
#

Possibly in the current version, gonna check its code

stuck thorn
#

that fire fires when said connection is not yet ready

scarlet pecan
#

oh gotcha

stuck thorn
#

fuh that, surely deffering the fire will FIX all the issues

#

LOL NO

scarlet pecan
stuck thorn
#

Seems like whatever I made breaks a lot of things, like the last example just leaves a immortal once connetion πŸ’”

#

(im guesisng because signal disconnecting everything in that span)

scarlet pecan
#

There's no DisconnectAll or DisconnectAllMutations in the test so I'm unsure what might be causing it if it is being disconnected, I'm leaning towards it possibly not being added in the first place

stuck thorn
#

im still discussing the old test lol

scarlet pecan
#

oh gotcha

stuck thorn
#

okay nvm I can't figured it out for today

scarlet pecan
#

All good, you've worked hard πŸ”₯

stuck thorn
#

Now my issue is like, do I just run out of frame budget to squeeze in more tasks

#

or is it more about that I have to wait for it to finish

scarlet pecan
# stuck thorn Now my issue is like, do I just run out of frame budget to squeeze in more tasks

The scheduler gives you as much budget as you need to the point it's willing to freeze the frame all to preserve guaranteed ordering before it moves onto the next frame πŸ‘

print(1)
for i = 2, 1e7 - 1 do
  task.spawn(function()
    print(i)
  end)
end
print(1e7)
-- 1
-- 2
-- ...
-- 9999999
-- 10000000 (1e7)

The only way to pass tasks across frames* is if they become asynchronous (yield or wait on something in any way)

task.wait()
workspace.Baseplate:GetPropertyChangedSignal("Name"):Wait()
coroutine.yield()

Race conditions are still possible only if you can't explicitly control the insertion order of tasks, but I believe it's possible here

#

^ Assuming this is what you're talking about

stuck thorn
#

im more abotu how does cn2 never connect, it technically ran

#

and it's still provided what it needs

#

Ill think about it tomorrow (or now if I get enough info about how the whole range-clearing works and then ponder long enough on why cn2 just gives up at last fire)

#

I wonder if it has to do with queue

stuck thorn
#

Oh wait, I could technically just yield the main thread until all fires are done?

#

but that feels cheap, because itd be a bandaid solution? (Ill give it a shot tomorrow)

stuck thorn
#

@scarlet pecan Apparently it's about how im dispatching the waiting fire's lol (so I expect this is a issue with the way I schedule it)

#

fuckass coconut.jpg creepypasta LOL 😭

stuck thorn
#

so it's like... waiting for connections in a queue is a "better" choice 😭

stuck thorn
stuck thorn
scarlet pecan
#

I can slowly but surely feel your sanity crumble before this challenge 😊

#

Gonna make a guide to hopefully help visualize what the end result should look like

stuck thorn
#

though I found something interesting

#

the cn2 does connect, but it doesn't get called during the last reentrancy fire, which I find strange

#

guess I gotta sprinkle some tags

stuck thorn
#

I was so successful that I created code so "clever" that by definition I can't debug it

#

Oh I found the issue

#

apparently refrence to tail gets nil'ed

#

while head doesn't

#

so it's a race condition?

scarlet pecan
# stuck thorn so it's a race condition?

It might be assuming this is what you mean:

  1. [Action A] tail -> nil
  2. [Action B] New Nested connection is inserted
  3. [Action A cont.] head -> nil

^ Therefore, the new Nested connection is removed along with the head, or it does become the head (then overwritten to nil)

stuck thorn
#

Actually ima "select" wher ethe issue is

#

so once we get to that fire, signals tail is nil, but signals head has cn2

#

actually hold up, I changed disconectall logic a bit, ima revert it so it's more clear

scarlet pecan
#

Have you done?

print(cn2.Connected)
signal:Fire()
print(cn2.Connected)

^ To confirm if it's truly being nil'd or if it's just being disconnected by the :DisconnectAll() before the nested Fire runs (it would self-disconnect by the Fire if it did ran, but it isn't in this case)

You could also print every connection being disconnected during the DisconnectAll loop (I assume that's what you just did in the screenshot)

stuck thorn
#

before fire it's true, after fire it's false

scarlet pecan
#

so it is being disconnected by DisconnectAll?

stuck thorn
#

Yeah, but it's still in the linked list

#

I only now realized how buggy it is 😭

#

wow wtf I fixed it

scarlet pecan
stuck thorn
#

I mean that's just old way of DisconnectAll πŸ’”

#

still no range-clearing as I dunno how'd that work yet

scarlet pecan
# stuck thorn still no range-clearing as I dunno how'd that work yet

Probably thanks to the mutation queue you made (ordered by time), so future connections will be ordered after it and won't be insta-disconnected
^ My idea of insta-disconnecting future nested connections within a Fire is dumb as hell in practice lmao, I might've messed up your original logic because of it.

stuck thorn
#

yeah no I think what I just made can be a bandaid fix πŸ’”

scarlet pecan
stuck thorn
#

oh so called "temporary" fix

scarlet pecan
#

100% intended permanent design trust

stuck thorn
#

Promise would be proud

#

dang I broke it again

#

now that both head and tail is nil

#

I believe it was dumb on my part to not make when Wait/Once connection gets called it disconnects instantly

scarlet pecan
#

This is how the end result is supposed to work right?

  • isFiring == false
    • Connections :Connect() :Once() :Wait()
      • Immediate: Inserted to connections list & returned to immediately access properties & methods
    • Disconnect()
      • Immediate: Disconnection
    • DisconnectAll()
      • Immediate: Disconnection
    • :ChangePriority()
      • Immediate: Priority change & sorting
  • isFiring == true
    • Nested Connections :Connect() :Once() :Wait()
      • Immediate: Returned to immediately access properties & methods
      • Deferred/Queued: Insert to the connections list.
    • :Disconnect()
      • Layer 1 (nested connection disconnected in the SAME Fire)
        • Immediate: Disconnection
      • Layer 2 (not nested connection or it's disconnected in a reentrant Fire)
        • Deferred/Queued: Disconnection
    • :DisconnectAll()
      • Deferred/Queued: Disconnect all CURRENT (non-nested & nested) connections at the time it's called
    • :ChangePriority()
      • Deferred/Queued: Priority change & sorting
    • :DisconnectAllMutations()
      • Deferred: Cancels ALL mutations in the same Fire, includes new Nested Connections (they'll be disconnected). (Note: Reentrant Fires are not mutations, they house mutations)
stuck thorn
#

:DisconnectAll() doesn't work like that fully, but rest is accurate

scarlet pecan
stuck thorn
#

uhhh, rips all the tubes out and lets yielded fire threads go

#

Which is to say, I have no idea how you make signals (unless it's simple roblox ones)

scarlet pecan
# stuck thorn uhhh, rips all the tubes out and lets yielded fire threads go

I think DisconnectAllMutations should leave reentrant Fires alone since they're more of mutation boundaries (similar to consecutive Fires but synchronous to just 1) instead of mutations themselves.
Mutations just refers to any changes relating to connections

-- Consecutive Fires
signal:Fire()
signal:Fire()
-- Reentrant Fire
signal:Once(function()
  signal:Fire()
end)
signal:Fire()
stuck thorn
#

so it's to say, to just leave them be and continue their work?

scarlet pecan
#

Yup πŸ‘

stuck thorn
#

okay, done that

scarlet pecan
#

Thx 🀝

stuck thorn
#

oh and type errors (mostly there for debugging)

scarlet pecan
#

I think this is gg πŸ”₯

#

(everything is as expected & correct)

#

Gonna do 1 last test, immediate mass disconnection for Layer 1 Nested connections

stuck thorn
#

unless it's the gloomy DisconnectAll (that just sets every connections Connected in queue to false as I dunno how to deal with this for now lol!!!!)

scarlet pecan
stuck thorn
#

that does give me a idea though...

scarlet pecan
#

But there's currently no way to immediately disconnect Nested connections (Layer 1 disconnection of current connections)

scarlet pecan
# scarlet pecan But there's currently no way to immediately disconnect Nested connections (Layer...

If it's fine, I think DisconnectAllMutations should be both immediate & deferred

  • Immediate: Disconnects all nested connections currently present in the queue & cancels all current mutations (wipe the queue)
  • Deferred: Does the same thing again but for "future mutations" (mutations that happened after :DisconnectAllMutations() was called within the same Fire)
    ^ So that there's a way to immediately disconnect all Nested Connections at once (for Layer 1 disconnections)
stuck thorn
#

yeah it's getting ugly now

stuck thorn
scarlet pecan
scarlet pecan
#

Doing final verifications real quick...

#

I can die happy now :D

stuck thorn
#

Honestly if I could get range-clearing idea working then pretty much it'd be "set"

#

because then I could just reject disconnected connections in the queue

scarlet pecan
stuck thorn
#

you wanted me to add it (the range-clear DisconnectAll)

#

just that it worked 10% of the time

scarlet pecan
stuck thorn
#

but then... disconnectall has a bug

scarlet pecan
#

guh!?

stuck thorn
scarlet pecan
#

oh yeah that's weird, have you checked cn3.Connected? (only cn1.Connected is printed)

stuck thorn
#

It'll say it's disconnected, but in actuality? haha. NO

#

I could probably print signal._head == cn3 and it'd be true

#

actually ill just implement a "disconnectall" which just clears queue to a certain range (if oldest calls it, then say bye bye to like majority of the queue LOL)

stuck thorn
#

So this probably still gonna be experimental, but atleast DisconnectAll should work as "intended" (clearing only connections before its call, doesn't account for when older connection calls it)

#

^ so it also fixes some disconnect all quirks yada yada, adds new one (yada yada, might get fixed)

stuck thorn
#

I wanted to early return it, but it broke things

#

so we're just doing more work and then leaving it by the road side

#

wasting computing time πŸ€‘

scarlet pecan
stuck thorn
#

Yeah I was wondering if you meant disconnect all mutations should disconnect every connection, guess not lmao 😭

scarlet pecan
#

Oh wait one more feature suggestion that just occured to me 😈

#

DisconnectAll could have the same arguments as DisconnectAllMutations for extra versatility
:DisconnectAll( "deferred" | "instant" | "both" )

stuck thorn
#

I think that would just screw up shit by a long shot

scarlet pecan
#

Later after release is fine πŸ™

stuck thorn
#

actually, should disconnectall affect mutations

#

or should they be like their own seperate buddies

#

@scarlet pecan guess since you asked for it then im just gonna do it, it won't be pretty though

stuck thorn
stuck thorn
#

ill just assume that you want to disconnect current connections + mutations before the call

#

dang it, now I have to make another state called both

scarlet pecan
#
  • :DisconnectAll()
    • All Non-nested Connections
    • Current Nested Connections (at the time of being called)
  • :DisconnectAllMutations(), cancels the following, timing depending on the mode:
    • Queued :Connect() :Once() :Wait() <- Disconnects & cancels the insertion to the connections list
    • Queued :Disconnect()
    • Queued :ChangePriority()
    • Queued :DisconnectAll()
#

Actually nvm about :DisconnectAll() having modes, it would be redundant & against its purpose of being deferred in the first place (while in a Fire)

stuck thorn
#

that's kinda what I meant lmao

#

why would it have modes

stuck thorn
scarlet pecan
stuck thorn
#

🀫

scarlet pecan
#

πŸ™

stuck thorn
#

next change request would've been asking me to actually implementing range-clear (:DisconnectAll() working in it's selected connection range), but nice catch

stuck thorn
#

there is no AI for it

#

let me just create a http wrapper to chatGPT and then ask it to be your signal and tax payer

scarlet pecan
#

hmmmm I think it might be better offline so a custom-made model better than GPT 5.2 would be okay ig

#

less than 200 lines would be best ^

#

Aside from that, I think it might be perfect for release if you would like to

stuck thorn
#

we gotta use buffers, we gonna have 1 bit llm's

stuck thorn
scarlet pecan
stuck thorn
#

I mostly meant range-clearing

#

you call :DisconnectAll() at the oldest connection and it's gonna rip your roof open

stuck thorn
#

MUAHAHHAHAHAHAA

stuck thorn
#

actualy yeah ill just implement range-clearing for disconnectall and it'd be all good I think

scarlet pecan
stuck thorn
#

if you implement it good enough, then it's not going to kill you

scarlet pecan
#

I'll make sure you get the best Roblox programmer-themed coffin at your funeral 🀝

stuck thorn
#

LOL

#

IT ERROR'D BUT I STILL PASSED

#

😭

#

guess im getting that casket overall

stuck thorn
#

And I fixed that issue (I believe, unless torture testing can break it) [so this means... it's completed! hell yea!]

#

it fine, for how many features it has it's good enough

#

actually wait I think I screwed up smth

scarlet pecan
stuck thorn
#

tanks you for reporting tis...

#

are these new tests or the ones I missed

scarlet pecan
#

Change the -- true | -- false comments to what you expect the results to be, the file I sent you isn't updated for [7]

#

I can make a proper tester (including niche situations) once we're satisfied with the observed behaviors/results

stuck thorn
#

Dang it, that bug came back

#

wait a minute, that shou;dn'tr be a bug

#

I just now realized what your test does

#

it calls DIsconnectAll

stuck thorn
#

"scope-clear" thing

#

you need to do DisconnectAll in both of them or brutal way is to DisconnectAllMutations on deferred

#

I wonder if that's overkill or not

#

@scarlet pecan but yes, fix for [7] is coming it's way

#

Though a quick question: should disconnectall be as is, or should I seperate that feature as another function called "disconnectPreviousConnections"

stuck thorn
scarlet pecan
#

I lowkey don't know anymore πŸ’ƒ

stuck thorn
#

Asks me for so much features that its fundamentally breaking MaddestCat

#

price for class 3.141 signal πŸ’”

scarlet pecan
#

But honestly, I think there should be as few disconnect methods as possible (ideally sticking with the 3 that we already have, 2 of which are for multiple)
:Disconnect(), :DisconnectAll(), & :DisconnectAllMutations

stuck thorn
#

Though, you would need to call that in said nest layer to do that

#

also thats why we got... 2 disconnect methods

#

truly

stuck thorn
#

[10] now has different behavior (from [7] fix)

scarlet pecan
# stuck thorn ill leave it at that for today, it's 11 pm

I highkey like the current behaviors of this, of Nested Connections disconnecting immediately upon :DisconnectAll() while keeping disconnecting non-nested connections deferred, feels more intuitive & in-line with the orignal mutation-layering concept.

#

There's always that 1 issue tho πŸ’”
(cn1 is made before :DisconnectAll() is called but it's completely unaffected)

stuck thorn
#

Not touched in the queue

#

im guessing you want disconnectall to touch all the mutations too?

#

erghh cough cough, all current*

scarlet pecan
stuck thorn
#

so abolishing the whole range-clear den

#

for a simple for loop in queue which kidnaps new connections

stuck thorn
#

πŸ”₯

stuck thorn
scarlet pecan
# scarlet pecan I highkey like the current behaviors of this, of Nested Connections disconnectin...

Like this, but it can catch nested connections made in older connections too πŸ™

local cn1 = signal:Connect(fn)
signal:Connect(function() -- [1]
  cn2 = signal:Connect(fn) -- Made before :DisconnectAll() is called, caught by it
end)
signal:Connect(function() -- [2]
    cn3 = signal:Connect(fn) -- Made before :DisconnectAll() is called, caught by it
    signal:DisconnectAll()
    print(cn1.Connected) -- true (deferred false)
    print(cn2.Connected) -- false (immediate)
    print(cn3.Connected) -- false (immediate)
end)
signal:Connect(function() -- [3]
  cn4 = signal:Connect(fn) -- Made after :DisconnectAll() is called, so not caught by it
  print(cn4.Connected) -- true
end)
signal:Fire()
print(cn1.Connected) -- false (deferred)
print(cn2.Connected) -- false
print(cn3.Connected) -- false
print(cn4.Connected) -- true (NOT CAUGHT BY DISCONNECTALL, BUT CAN BE BY DISCONNECTALLMUTATIONS)
  • DisconnectAll
    • Deferred: ALL non-nested connections
    • Immediate: All CURRENT/PREVIOUS nested connections
      ^ Respects mutation layering, so it's mostly intuitive to use.
stuck thorn
#

oh gotcha, so still... just basic for loop thru a queue

#

atleast firing speed improvement πŸ€‘

#

so yeah, ill change it tomorrow

scarlet pecan
# stuck thorn oh gotcha, so still... just basic for loop thru a queue

Loop through it immediately upon it being called (to disconnect current nested connections)
Then in the mutations processing step (after connections are ran), disconnect all non-nested connections.

  1. Fire [START]
  2. Run connections
  • Create nested-connection A
  • Create nested-connection B
  • :DisconnectAll()
    • Immediately disconnect all current/previous nested-connections in queue (A & B)
  • Create nested-connection C (unaffected by :DisconnectAll())
  1. Process mutations
  • DisconnectAll() has been called (flag raised), disconnect all non-nested connections, and don't disconnect any further nested-connections made after DisconnectAll() was called
  1. Process reentrant Fire(s)
  2. Fire [END]
scarlet pecan
stuck thorn
#

though, to speed up the whole thing

#

ill just keep the index of where it stopped

stuck thorn
#

@scarlet pecan okay guess you can put it under stress again

#

(and I changed some things, ie. some mutations early return if the connection is disconnected)

stuck thorn
#

^ Though, it will not be shattering any speeds as most of the things aren't inlined

scarlet pecan
#

Made the observations into a proper test (get rid of the other tests)

Only tests needed:

  • Signal Certifications
  • Behaviors Test (download here)
scarlet pecan
# stuck thorn https://tenor.com/view/jetstream-sam-metal-gear-rising-jetsream-sam-metal-gear-r...

One final suggestion (fr this time.)

As a compatibility feature for the developers that want to use this Signal but still rely on forced immediate disconnection (for existing games or out of preference), perhaps forceImmediate (boolean) as an argument could be used in :Disconnect() and :DisconnectAll(). Even if it's an inferior behavior, it still has a few niche uses (less work to do them if this is implemented).

forceImmediate == nil | false, default/dynamic mode (the current behaviors)

local cn1 = signal:Connect(fn)
cn1:Disconnect() -- Immediate
signal:DisconnectAll() -- Immediate

signal:Connect(function()
  cn1:Disconnect() -- If Nested & Layer 1? Immediate | Otherwise Deferred
  signal:DisconnectAll() -- QUEUE (Nested & Layer 1): Immediate + EXISTING: Deferred
end)
signal:Fire()

forceImmediate == true, forces immediate-disconnection no matter what

local cn1 = signal:Connect(fn)
cn1:Disconnect(true) -- Forced Immediate
signal:DisconnectAll(true) -- Forced Immediate

signal:Connect(function()
  cn1:Disconnect(true) -- Forced Immediate
  signal:DisconnectAll(true) -- Forced Immediate
end)
signal:Fire()

^ The argument if true basically just skips the isFiring/Nested/Layering check

stuck thorn
#

I feel like this could break some things in signal when abused, but ig ill try

scarlet pecan
stuck thorn
#

So I guess if they want "immediate" disconnectall, then it disconnects all nested + current connections RIGHT NOW

scarlet pecan
stuck thorn
#

so some like this

#

actually I just realized how redundant that disconnect is in not forceImmediate one

#

better

scarlet pecan
#

Now for the final torture test 😈

stuck thorn
#

it's called test because it added 2 1 (because both ask for same thing) things

scarlet pecan
#

Passed so far πŸ‘
Testing the new feature now

stuck thorn
#

decided to change unneeded disconnect() in code for connection.Connected = false, behavior should be the same

scarlet pecan
stuck thorn
#

This is fishy

#

OH RIGHT I REMEMBER

#

even if it is disconnectall, it keeps links

#

so I have to create another disconnectall which multilates them

scarlet pecan
#

I forgot to test :Disconnect() too mb, sending the file in a sec

stuck thorn
#

guess we're boolean blocking that with isfiring

scarlet pecan
stuck thorn
#

holy I just realized how much of a flop this is 😭

#

okay no, this feature is just UGLY

#

which is to say: it just refuses what I say and does whatever it wants lmao

#

O right, I think I need my own logic for this...

#

yeah okay nvm

#

with how we do linked list, there's bound to 1 get loose and chain to the disconnected ones

#

@scarlet pecan

#

oops

stuck thorn
stuck thorn
scarlet pecan
stuck thorn
#

because it unhooks itself from the linked list, but it still lets it bounce to the designated end

#

(ironically that being the issue why your favorite :Disconnect(true) didn't work LOL)

#

it literally bounced from that :Once() connection 😭

#

so no, it shouldn't cause memory leaks because it'll get GC'ed

scarlet pecan
#

I see πŸ”₯

#

With that feature implemented, there's literally nothing else I can think of that I or others would want

stuck thorn
#

I was gonna break down from this feature but then I went

#

"do the dumb way"

#

HELL YEA BRANCH PREDICTION W

scarlet pecan
stuck thorn
#

so I guess I can now sleep peacefully because me tinkering is done

#

But it was fun, can't lie

scarlet pecan
#

I'm gonna update the file on your Signal on the devforum post if that's fine (& include more documentation)

stuck thorn
#

yeah it's fine

#

even if github support still didn't respond back after initial 2 day wait + response

#

I think they got me on the ops bro

scarlet pecan
stuck thorn
scarlet pecan
#

oh RIP

stuck thorn
scarlet pecan
#

god forbid if someone mentions that their height is of 6 foot 9 inches

stuck thorn
#

guess I can now rename SomeSignalTest to SomeSignal in me files

#

and 2 old backups, truly

scarlet pecan
stuck thorn
#

I guess if you want to mimic ":DisconnectAll()" from roblox signals then

:DisconnectAll(true)
:DisconnectAllMutations("deferred")
scarlet pecan
#

whoops forgot about DisconnectAllMutations

scarlet pecan
#

^ Added

stuck thorn
#

@scarlet pecan Uhhh I didn't make it nil-able

#

unless you want me to do that.... Nvm

#

actually I can

scarlet pecan
# stuck thorn actually I can

No worries, I'll fix the documentation
Seems like "immediate" is the default, this makes more sense tbh considering :DisconnectAll() and :DisconnectAllMutations() are both immediate in layer 1.

stuck thorn
#

that wasn't intended but okay 😭

#

so a feature it is

scarlet pecan
#

Should I specify the default as deferred, immediate, or both?

stuck thorn
#

I think this one should be deferred by default (because the name implies, Disconnect all mutations)

scarlet pecan
#

Sounds good 🀝

stuck thorn
scarlet pecan
rough rivet
#

is this zignal 2

scarlet pecan
# rough rivet is this zignal 2

Nah nothing will replace Zignal as the best BindableEvents replacement in its generation.

BUT,

SomeSignal is more of an evolution past BindableEvents & previous Signals, for superior versatile, consistent, & backwards compatible functionality and extra features.
Pretty much a long-awaited BindableEvents 2, a new generation and best among all generations.

Feel free to check it out (features & documentation at the bottom of the topic in the Class 3 dropdown section)
https://devforum.roblox.com/t/signal-certifications-classes-guide/4263792

rough rivet
#

i wonder if there are signals i can define like packets

#

similar to blinks networking

#

that would... come in handy for autocomplete

#

and it'd be all inlined too for better performance I think

scarlet pecan
#

That would be a pretty cool feature ngl, @stuck thorn what do you think?

stuck thorn
#

I dont get it

rough rivet
#

wait that might actually be the fastest a signal module could be

#

blink beats out other networking modules in terms of performance because all the events are defined beforehand

#

because you use a plugin that generates the code

#

i wonder if signals would benefit in the same way

scarlet pecan
rough rivet
#

but it still uses signals under the hood but those signals are remote events

#

if you get what i mean

stuck thorn
rough rivet
#

I'll hop on my pc in a sec and show an example

stuck thorn
#

No

rough rivet
#

this might be revolutionary

scarlet pecan
# rough rivet have you ever used blink?
#

I haven't yet

stuck thorn
rough rivet
#

I'm getting on my pc

#

this will solve world hunger

#

i know it

stuck thorn
#

at best you can just INLINE every signal method so ti doesnt waste time

rough rivet
#

SHUT UP!!! this will cure cancer

stuck thorn
#

You shush

rough rivet
#

sorry papa

stuck thorn
#

This only works BEST for network library

#

because you know whats GOING TO HAPPEN

scarlet pecan
#

Checking it out under the hood rn to see if there really are any benefits, on the surface it only seems good for networking (autofill, data serialization/deserialization for less CPU usage & lower bandwidth, and security).
The only thing that might be viable for Signals are autofill and security (so exploiters can't Fire false signals)

rough rivet
#

j- j.... JUST GIVE ME A CHANCE HERE

#

I'll send an example of a blink packet

stuck thorn
#

you know who else can compete....

#

making a remote wrapper in which you have to do your own encode/decode

#

truly

rough rivet
#

now that would tickle my pickle

#

this is taking so long

#

my pc is exploding

#

the idea is too good

#

UGH

#

okay there

#

these are some example packets

#

okay fuck it's hard to tell what i mean

#

basically just the "Data" fields are important

#

the text editor on the left is the plugin

#

and the code in the actual editor is the generated code

#

and now in a normal script i do something like

local Blink = require(path.to.blink.Client)

and i get autocomplete and the types etc.

scarlet pecan
#

Basically this?

signal:Connect(function(hasCheese: boolean, hasCrackers: boolean)

end)
signal:Fire() -- ( hasCheese: boolean, hasCrackers: boolean )
rough rivet
#

Blink.Character_Reset:Fire()

rough rivet
scarlet pecan
#

I think something similar to Packet's type specification could be done, with custom/static types also being possible.
We can't do Blinker's plugin approach because it does the heavy-lifting of hardcoding it for you (it requires a plugin or running code in command bar to do)

Encoding/decoding through buffers isn't practical for Signals, data compression is only really needed for bandwidth reduction or storage.

rough rivet
#

blink uses buffers and other absolutely wacky crazy shit

rough rivet
#

here's the entire generated blink file

#

821 lines of pure goodness

#

wait what would happen if you used a single bindableevent and put all the fired connections and it's parameters into it once
similar to how blink does it's remote events

#

instead of a bindable for each event

#

or making it code based

#

and having only a single entry and output point without even using bindable events

scarlet pecan
#

oh batching

rough rivet
#

exactly

#

and also using buffers and stuff like that

scarlet pecan
#

Batching isn't necessary for Signals because there's no network latency

#

You could create a custom batcher using Signals though if you really wanted

rough rivet
#

so many possibilities

scarlet pecan
#

Engine instructions are surprisingly fast as hell

rough rivet
#

that answers my question.
jarvis, start batching the signals for me

#

if someone is mentally ill enough to implement the buffers system etc. then maybe this could even beat out zignal

scarlet pecan
#

SomeSignal already beats out Zignal 😭

rough rivet
#

o- oh

#

well then somesignal is the next one on the chopping block

stuck thorn
#

Buffering slower than setting RAW values

#

πŸ€‘

#

Anyway, going to sleep

scarlet pecan
rough rivet
#

night night!

#

i guess we'll never see a blazingly fast idl compiler written in luau for roblox signals...

scarlet pecan
# rough rivet i guess we'll never see a blazingly fast idl compiler written in luau for roblox...

I mean there could be in the same way that the Blink Plugin works, but the only usage it'll provide over using the base module is more convenient argument typing...
Argument typing as you suggested is still a good idea though, but it'll probably only be possible in the way that Packet does instead (without a plugin).

Hardcoded security (same way that Blinker does) is also a good idea if it really is a big enough problem that developers face against exploiters. I know network manipulation & fraud through exploits is common, but I don't know how often it happens to BindableEvents/Signals.

rough rivet
#

types would be nice yeah

stuck thorn
#

@scarlet pecan hmmm, I think there's that 1 mistake I haven't noticed

#

Decided 1 thing was a "bug": forceImmediate on DisconnectAll not working when DisconnectAllMutations was called before DisconnectAll with forceImmediate set to true

#

because I think this might have a use case for when you want to stop ALL current connections while at the same time stop all mutations

#

actually on another note, I should actually make DisconnectAll and Disconnect fit the type of DisconnectAllMutations lol, which is to say ( "default" | "immediate" )? πŸ’”

stuck thorn
#

done it, I didn't like booleans that's all

stuck thorn
#

Baa okay, I should've probably mentioned in DisconnectAllMutations that it's deferred

scarlet pecan
# stuck thorn actually on another note, I should actually make `DisconnectAll` and `Disconnect...

My neighbor woke me up with loud-ahh alarms
I've just realised I never tested how the queue system deals with multiple signals (from SomeSignal).
Because of that, you good to add deferred and both as well?

local cn1 = signal_A:Connect(fn)
signal_B:Connect(function()
  cn1:ChangePriority(10)
  local cn2 = signal_A:Connect(fn)
  local cn3 = signal_B:Connect(fn) signal_B:DisconnectAllMutations("both") -- This will disconnect all current & future Nested Connections while also cancelling all mutations, regardless of what signal the mutations were for
  signal_B:DisconnectAll("both") -- This will immediately disconnect all current connections and defer-disconnect future nested connections of this signal only.
  print(cn2.Connected) -- (DisconnectAllMutations -> false) | (disconnectAll of signal_B -> true)
  print(cn3.Connected) -- false
  local cn4 = signal_B:Connect(fn) -- future nested connection
  print(cn4.Connected) -- true, but deferred false
end)

Typed ts on mobile πŸ’ͺ

#

Idk if default should be renamed to smart to prevent devs from assuming one of the other modes might be the default if they haven't read the documentation

stuck thorn
#

ill just name it default as... that's the default behavior

scarlet pecan
#

Sounds good πŸ‘

stuck thorn
#

if you want to mimic the behavior, it wouln't hurt to just call the method on both signals

scarlet pecan
stuck thorn
#

you know what would that mean?

#

shared queue

scarlet pecan
#

The queue would only exist in signal_B no? (Of mutations done in sognal_B's fire)

#

Oh wait

#

Oh

#

Nvm I see yhe problem

#

One solution I see is to set a global flag for the signal module of whichever signal is currently Firing, to then use that Signal's queue for all mutations

#

^ this is safe in CPU serial threading only (no CPU parallel threading allowed !)

stuck thorn
#

I think I won't be adding that feature

#

it's gonna be more of a "do it yourself" solution πŸ•ΆοΈ

scarlet pecan
#

All good, I'll give it a try later

stuck thorn
#

if you want to do that, then you would to manually overwrite _queue with a table of same address, then how queue is dispatched

scarlet pecan
#

Nothing about the queue will be changed, it'll just affect if mutations will use their own signal's queue or the queue of a signal currently Firing (this is possible because of Fire queueing)

stuck thorn
#

You said

signal_B:DisconnectAllMutations("both") -- This will disconnect all current & future Nested Connections while also cancelling all mutations, regardless of what signal the mutations were for
scarlet pecan
stuck thorn
#

if you wanted it to affect signal_A, both would need to share the same _queue table

scarlet pecan
#

OH

scarlet pecan
stuck thorn
#

ah yes, custom function of sewSignalsTogether()

#

you would probably need to change how disconnectallmutations does it (COUGH COUGH) and then queue dispatch in both fire and priorityfire

scarlet pecan
#

It's more like this (applied to all signal functions/methods that insert to a queue)

-- All within signal module
local firingSignal: Signal

SignalMeta.Connect = function(self: Signal, fn, priority)
  local cn = connectfunction(...) -- Just assume this is the created connection not inserted yet
  if firingSignal ~= self then
    table.insert(firingSignal._queue, cn) -- I forgot what your insertion looked like, assume this is the cn's insertion function/process being queued
  end
end

I'll look into DisconnectAll, I just remembered it raises flags I gotta consider (for future nested connections of its own signal)

stuck thorn
#

Though if I think about it

#

you just turned DisconnectAll into DisconnectAllMutations mostly

#

just that it clears the non-nested connections while at it

scarlet pecan
#

Precisely

stuck thorn
#

suprised that you now caught on about how about 2+ signals :thinking:

#

Doubt im gonna find a person who gonna need a super specific latte queue dispatch disconnectall connect wait disconnect in their code 😭

scarlet pecan
#

Me: πŸ₯€

stuck thorn
#

what would you need that behavior though

scarlet pecan
#

Ye if signals are gonna chain together, one use-case is Firing a signal to shoot a bullet, then a connection in that will automatically Fire the signal for reloading if the mag is empty, possibly creating a new (temporary) connection in the process
Idk what that temp connection would be honestly (I'm not developing a gun system rn), but it would be nice to have it as an option

stuck thorn
# scarlet pecan My neighbor woke me up with loud-ahh alarms I've just realised I never tested h...
local cn1 = signal_A:Connect(fn)
signal_B:Connect(function()
  cn1:ChangePriority(10)
  local cn2 = signal_A:Connect(fn)
  local cn3 = signal_B:Connect(fn)
  -- check this out
  signal_A:DisconnectAllMutations("instantly")
  signal_B:DisconnectAllMutations("deferred")
  signal_B:DisconnectAll("instantly")
  print(cn2.Connected) -- false
  print(cn3.Connected) -- false
  local cn4 = signal_B:Connect(fn) -- future nested connection
  print(cn4.Connected) -- true, but deferred false
end)

I decided to see how I would write it πŸ’”

#

seems like the new features are paying OFF πŸ€‘

stuck thorn
scarlet pecan
#

Wait what if signal_A called for DisconnectAll in signal_B?

#

^ putting all niche applications aside, this one's common & important

stuck thorn
#

actuall nvm I see my issue

#

because if signal_A is not firing, then cn2 is instantly connected (so is cn1 being 10 priority)

#

made this signal, still doesn't know what's going on

#

this feature is ass πŸ‘Ž , blocked from suggesting anything ever again

scarlet pecan
#

I'm gonna come back to this later in the morning 😈

stuck thorn
#

I think MaddestCat fell asleep on the wheel

rustic wave
stuck thorn
#

Can't believe MaddestCat would task.defer(Signal.Fire, Signal, "1")

scarlet pecan
#

mb lmao, working on it now

scarlet pecan
#

SomeSignal Bug fixes

  • :DisconnectAllMutations()'s deferred and both modes forgot to account for nested connection mutations made after it's called (to disconnect them)
    • Fixed by disconnecting them in the Fire function if (state == DISCONNECT_MUTATIONS) is true.
  • :DisconnectAllMutations() did not cancel mutations of other signals within the same Fire.
  • Fixed by implementing sharedQueue
    • mutation() calls now pass connection._Signal (the signal it was made with) instead of self (the firing Signal)
    • :DisconnectAll() (regarding queued connections) now disconnects queued connections from its own Signal instead of indiscriminately like before.
  • Mutations queue didn't clear if (state == DISCONNECT_MUTATIONS) is true after all connections are ran.
    • Fixed by clearing the queue table no matter what
  • Consecutive reentrant Fire had incorrect timing
    • Fixed by moving isFiring = true to the start of the Fire functions, and isFiring = false to before resuming the next Fire.
      Example of what the consecutive reentrant Fire timing looked like before the fix:
signal:Connect(function(fireId:number)
  print(fireId)
end)
signal:Once(function()
  signal:Fire(2) -- 2 (End -> resume nil)
  signal:Fire(3) -- [N/A], this won't run in this Fire until after Fire 4.
end)
signal:Fire(1) -- 1 (End -> resume Fire 2)
print("------")
signal:Fire(4) -- 4 (End -> resume Fire 3)
--[[
1
2
-----
4
3
]]
#

New behaviors test, now includes:

  • [11] now also tests for future nested connections (deferred & both) mode
  • [17] Multi-Signal mutations handling
  • [18] Consecutive reentrant Fire
#

Before Speed (old version)

#

New Speed (new version)

#

^ Almost the same in terms of practicality

#

@stuck thorn any issues with it?

stuck thorn
#

Ill check a bit later

stuck thorn
#

changes look good to me, other than some coding style inconsistencies

scarlet pecan
stuck thorn
#

Im still wondering what was wrong about :Fire()

#

ig you aren't suppose to invoke fire in the same connection twice?

#

actually it'd be faste rif I just tested it with before fixes signal

#

okay makes sense πŸ’”

scarlet pecan
# stuck thorn ig you aren't suppose to invoke fire in the same connection twice?

Here's what it was doing before:

  1. Fire 1 starts
  2. Reentrant Fire 2 sees isFiring == true and is fire-queued
  3. Fire 1 finishes running all connections & mutation processing, resumes the next Fire in firingQueue
  4. Fire 2 starts
  5. Fire 2 sees nothing next in the firingQueue
  6. Fire 2 ends
  7. The thread of the 'Once' connection continues after Fire 2 is completed, reentrant Fire 3 sees isFiring == true and is fire-queued
  8. Fire 1 ends
  9. Fire 4 starts
  10. Fire 4 resumes the next Fire in firingQueue
  11. Fire 3 starts
  12. Fire 3 sees nothing next in the firingQueue
  13. Fire 3 ends
  14. Fire 4 ends
signal:Connect(function(fireId:number)
  print(fireId)
end)
signal:Once(function()
  signal:Fire(2) -- 2 (End -> resume nil)
  signal:Fire(3) -- [N/A], this won't run in this Fire until after Fire 4.
end)
signal:Fire(1) -- 1 (End -> resume Fire 2)
print("------")
signal:Fire(4) -- 4 (End -> resume Fire 3)
--[[
1
2
-----
4
3
]]
stuck thorn
#

Im guessing now if you frick around with 3 signals, this will get nasty quick lmao

scarlet pecan
#

Indeed lmao, but it hopefully should be fixed now

stuck thorn
#

because it supports mangling with other signals

stuck thorn
#

peak work

scarlet pecan
#

Thx my man 🀝

stuck thorn
#

it should work the same I believe

scarlet pecan
stuck thorn
#

ah yes, put everything on shared tab

#

are you sure that it won't be headache to deal with later on?

scarlet pecan
#

All you gotta replace is signal.__firingQueue with firingQueue πŸ™

stuck thorn
#

ik

scarlet pecan
stuck thorn
#

no I mostly mean by like

scarlet pecan
#

Without it, Fires will be skipped entirely if they're queued in a different signal

signal_2:Connect(function(fireId: number)
  print(fireId) -- This won't print because signal_2's Fire is not in the same _firingQueue as signal_1
end)
signal_1:Connect(function(fireId: number)
  print(fireId) -- 1
  signal_2:Fire(2) -- Queued to signal_2's _firingQueue
end)
signal_1:Fire(1) -- This'll only check signal_1's _firingQueue for the next Fire
--[[

Output:
1

]]
stuck thorn
#

you got 20 signals, 1 calls disconnectallmutations

#

??? watch the city BURN.

scarlet pecan
stuck thorn
#

rite I forgot that I even wrote that logic

#

brah

stuck thorn
#

this only matters if you want it RIGHT NOW or BOTH

scarlet pecan
#

yee even with deferred | both it'll still work πŸ‘ (still happens before the next Fire)

#

You have a robust as hell signal design πŸ”₯

stuck thorn
#

actually wait... I forgot that ONE bit

scarlet pecan
stuck thorn
#

now it has that fancy sharedFiringQueue

scarlet pecan
#

Adding the test real quick

#

πŸ€‘

stuck thorn
#

I actually just now realized

#

DisconnectAll gon kill the sharedQueue

scarlet pecan
stuck thorn
#

oh

#

okay now I see

#

Nvm this might be concerning

#

but on second glance... NO

#

(more code cleaning)

scarlet pecan
#

It's the fastest possible way to range-clear/range-move

stuck thorn
#

I know since table.clear() makes the table retain the allocated memory

#

thus, speeding up the future insertions

#

So I guess it's good now

scarlet pecan
# stuck thorn Nvm this might be concerning

Range-Move implementation if you're interested:

local transferTable = {}
local ogTable = {}
for i = 1, 100 do
    table.insert(ogTable, i)
end

local minMove, maxMove = 20, 58

table.move(ogTable, minMove, maxMove, 1, transferTable)
table.clear(ogTable)
table.move(transferTable, 1, #transferTable, 1, ogTable)
table.clear(transferTable)

print("-------------------------------")
print("Move Length:", (maxMove - minMove) + 1)
print(#ogTable, ogTable, #transferTable, transferTable)
print("-------------------------------")

Range-Clear is the same thing but outside of min & max instead of between (doing it twice)

stuck thorn
#

because it's provided by ROBLOX it must mean it's FASTER than our implentation πŸ”₯

#

SO THIS MEANS... WE CRACKED THE KFC RECIPE??? πŸ€‘ (made "correct" signal)

scarlet pecan
#

There's literally nothing else to do for it, you've implemented EVERYTHING a developer would EVER need in a Signal.
-# (except custom argument typing for :Fire() but let's have that discussion another day)

stuck thorn
#

what the hell is custom argument typing................

#

I think it'd be impossible with the types you are given lmao

scarlet pecan
stuck thorn
#

or Packet.any if it's too complex

#

Packet("handThemPacket", Packet.F64, Packet.String) unda the hood it's Packet.Packet<number, string>

#

Just that it does the buffering differently

stuck thorn
#

I only layed the foundation (+ with you tests help)

stuck thorn
#

rahhh another boolean not wrapped in (), but ill that one slide

stuck thorn
#

im guessing you're going to have fun rewriting this now since there were ""major"" changes

scarlet pecan
#

Will do πŸ‘

stuck thorn
#

Hmmm, if SignalB is firing and then calls SignalA disconnectall, SignalA will have disconnect flag raised but will not disconnect current connections

#

oh ofc, if you immediate mode, then it'll work

stuck thorn
#

^But ill fix that via, than isFiring stores which signal it is

stuck thorn
#

Though if you are calling from B method DisconnectAllMutations while it's A which is firing, then DisconnectAllMutations will default to immediate behavior

stuck thorn
#

10 hours later

scarlet pecan
stuck thorn
#

I mean they are doing that

#

first was 2 days and then I got this after I replied

#

5 days later πŸ˜‡

scarlet pecan
stuck thorn
#

so now Signal shouldn't have any "bugs" im aware of

scarlet pecan
#

None more I could think of as well πŸ”₯

#

Only way to really know for sure is with more people using it in various real-world situations

stuck thorn
#

Though, when would you use signals for I still have no idea

scarlet pecan
# stuck thorn Though, when would you use signals for I still have no idea
  1. General synchronized events between scripts/modules
  2. Custom Objects (metatable/table objects)

A few examples I can think of:

  • Major use-case: Network replication send/receive events for scoped data (similar to [instance]:GetPropertyChangedSignal())
  • Systems that create objects
    • NPC system (NPC objects)
    • Gun system (gun objects)
    • Vehicle system (vehicle objects)
    • Interpolation system, basically TweenPlus (interpolation objects)
  • Tick/Time stepper (custom time stepping)
  • Combat system
  • Weather system
  • Minigame system
#

Signals are useful as hell for projects making complex stuff from scratch

stuck thorn
#

Okay, thank you

scarlet pecan
#

np 🀝

stuck thorn
#

I was gonna jokingly say, time stepper?

#

Under the hood : RUNSERVICE HEARTBEAT

scarlet pecan
stuck thorn
#

Totally useful for TD games

#

Though, instead of stepping the whole thing, you only make DT bigger, which is ehhhhh

#

okay until certain speed up

#

^ which is to say Suphi way of doing this (ticks) does have a sense

scarlet pecan
# stuck thorn okay until certain speed up

Rather than delta-ticks, you can always force each and every tick to run one after another for accuracy 😈
(As long as network replication is forced into batches so you don't pull a DOS from the server-side lmao)

#

100_000 times speed, make that server CPU WORK 100% of its worth πŸ€‘

stuck thorn
#

*Makes roblox electricity bills higher

#

This Signal good, but once you got 58 signals with 200 connections each, those transfer costs gonna be diabolical LOL

#

I mean if it ever comes to that, then

#
if (#transferTable > 0) then
-- table.move(transferTable, 1, #transferTable, 1, sharedQueue)
-- table.clear(transferTable)
  sharedQueue, transferTable = transferTable, sharedQueue
end
stuck thorn
scarlet pecan
#

Updating the download on the devforum once I get home πŸ”₯

stuck thorn
#

I am indeed the compiler which bytecode was looking for

rough rivet
#

so wait i forgot

#

is this better than zignal

scarlet pecan
rough rivet
#

alright fire

#

thanks guys love you ❀️

scarlet pecan
# stuck thorn Okay decided to implement that because seems like a free speed boost later on

This is the latest version
Also check out the devforum for some documentation (class 3 drop-down at the bottom of the post), and feel free to ask us anything anytime πŸ‘
https://devforum.roblox.com/t/signal-certifications-classes-guide/4263792

rough rivet
#

do you guys have a github?

#

also your response time is incredible

#

greatly appreciated

scarlet pecan
#

(he got banned for his username lmao)

rough rivet
#

😭

#

wait

#

:DisconnectAllMutations()

#

it disconnects connections under connections

#

right?

#

atleast that's how i understood it from the documentation

#

and the function comment

scarlet pecan
# rough rivet :DisconnectAllMutations()

It disconnects any signal-related action done inside of a Fire
The following are mutations:
:Connect, :Once, :Wait, :ChangePriority, :Disconnect, and :DisconnectAll

signal:Connect(fn)
signal:DisconnectAll()
signal:DisconnectAllMutations() -- Useless because it's not inside a Fire, does nothing

local cn1 = signal:Connect(function()
  local cn2 = signal:Connect(fn)
  signal:DisconnectAll() -- Immediately disconnects cn2 because it's a layer 1 disconnection (impossible to cancel), but cn1 is a layer 2 disconnection so it's deferred (after all connections are ran)
  signal:DisconnectAllMutations() -- Inside a Fire, disconnects cn2 (if it wasn't already) & cancels DisconnectAll so cn1 remains connected.
end)
rough rivet
#

oh so if i did DisconnectAllMutations it would leave cn1?
but if i did DisconnectAll it would disconnect cn1 too?

scarlet pecan
#

Yup

rough rivet
#

oooooooooooooh

#

okay okay

#

i know mutations is probably the best name for it generally but

#

what if it was :DisconnectAllNested instead?

#

i like this function description

#

this one isn't quite as obvious though

#

i might just be a little dense though

scarlet pecan
#

Brb srry

rough rivet
rough rivet
#

even if the function description were updated by itself that would be a lot more clear

#

"Disconnects all nested connections and cancels any queued connections, deferred by default, leaves the main signal intact"

#

idk something like that

#

also happy birthday @stuck thorn

#

if it is your birthday today...

rough rivet
#

wait actually the current description might be fine...

#

i'm just... stupid...

scarlet pecan
# rough rivet i'm just... stupid...

All good, it's probably a bit tricky for most people to intuitively wrap their heads around this the first time. It took 6inch9inch & I several days to independently think of some of the concepts & determine that they're the best fit for Roblox development so however long you'll take, it'll be far faster than my understanding time lol.

If there's demand, I can make a suphi-kaner style video of how to use SomeSignal, explaining some of the concepts behind it, its features, several examples of it in practice, and usage comparisons between SomeSignal and other Signals/BindableEvents (to show exactly why it's superior for versatility & convenience)

stuck thorn
#

And then you going to hit Suphi Kaner bugs

stuck thorn
scarlet pecan
stuck thorn
#

Thank you, but you know what portal teaches about the cake....

stuck thorn
#

Good to see someone else other than me and maddestcat trying to use this signal

stuck thorn
# rough rivet

It could get better wording, but I still think it's good enough

#

Because I wouldn't really say it Disconnects per say, it just cancels all queued mutations

#

Actuall yeah you have a point

scarlet pecan
# stuck thorn Actuall yeah you have a point

Any of these good?
:CancelAllMutations() -> Might be the most accurate because it also disconnects nested connections (the other ones only imply deferred mutations only)
:CancelDeferredMutations()
:CancelQueuedMutations()

stuck thorn
#

CancelAllMutations("deferred" | "immediate" | "both")

#

truly

#

and it also has a more ""carefully"" worded comment

#

Feeling bad for the guy who wants to DisconnectAll without killing his nested connections

scarlet pecan
stuck thorn
#

though should I give people option to defer disconnect nested connections

#

lol

scarlet pecan
#

True πŸ”₯

#

Could there be a 4th while we're at it? (For nested connections only without cancelling mutations)

stuck thorn
#

I am doing that

#

hmmm, should I make it that if you call for immediate, it just ignores the CancelAllMutations flag

scarlet pecan
stuck thorn
#

and we got....

signal:CancelAllMutations("immediate")
signal:DisconnectAll("immediate") -- Ignores Cancel flag
#

so im just gonna fix that real quick....

scarlet pecan
#

Thx πŸ”₯

stuck thorn
#

hmmm I see another optimization I can do...

#

@scarlet pecan Apparently you did a transferTable which got... canceled right after

stuck thorn
#

Hmm should I make it that if you call CancelAllMutation it can't call itself until the previous CancelAllMutation is done

#

truly best idea

#

Actually nvm, that I shouldn't do

#

Though, if you do DisconnectNestedConnections on deferred and then DisconnectCurrentConnections then DisconnectCurrentConnections will be the disconnectAll mode

#

I could... make it so callign them both causes a synergy

#

but that's DisconnectAll in disguise

scarlet pecan
#

Giving the new methods a try in a few minutes + will update the devforum download & documentation

rough rivet
#

also each newer version that gets sent here isn't just like showcase code right?

rough rivet
#

should i replace my existing version with this one?

scarlet pecan
scarlet pecan
#

Gonna do it now

scarlet pecan
#

cn1 -> Current connection (created outside of Fire)
cn2 -> Nested connection created before disconnection is called
cn3 -> Nested connection created after disconnection is called

stuck thorn
#

Ill probably add better comments

#

because disconnect nested connections will be immediate if the signal which called it is NOT the signal firing

#
  • probably make it so calling disconnect nested connections and current connections on deferred makes it do both
#

as a cool old "DisconnectAll" which everyone remembers

scarlet pecan
# stuck thorn Ill probably add better comments

Considering that the "immediate" mode for DisconnectAll now bypasses :CancelAllMutations() for the sake of consistency, should the same be applied to all other mutations under the same reasoning of more consistency?

cn1:Disconnect("immediate") -- Bypass?
signal:DisconnectCurrentConnections("immediate") -- Bypass?
signal:DisconnectNestedConnections("immediate") -- Bypass?

^ And in that case, should there be a new :CancelDeferredMutations() method for an extra layer of versatility?
Similar to why DisconnectCurrentMutations & DisconnectNestedConnections now exists

signal:CancelDeferredMutations() -- Deferred mutations will be cancelled, but "immediate" mode mutations will be able to bypass this
signal:CancellAllMutations() -- ALL mutations will be cancelled no matter what

^ It'll require implementing one extra flag that mutations will need to check.

local canBypassMutationCancel = true -- Remains true until :CancelAllMutations() is called, of if new "immediate" mutations can bypass the cancel.
stuck thorn
#

sorry not sorry

scarlet pecan
stuck thorn
#

immediate DisconnectAll no longer bypasses CancelAllMutations

scarlet pecan
#

What do you think of the :CancelDeferredMutations() idea though?

stuck thorn
#

Extra complexity

scarlet pecan
#

Precisely πŸ€‘

stuck thorn
#

as it got the things youd want

stuck thorn
#

~700 lines signal

#

while Zignal fits in ~100

scarlet pecan
stuck thorn
#

I dont think its possible unless I bend the rules a bit, but then you a bit of a picke

#

we need... another boolean flag

#

Actually I could... but ill think about it

scarlet pecan
scarlet pecan
rough rivet
stuck thorn
#

but I could just add another if statement in the disconnect function (helper one) and boom

#

problem solved

stuck thorn
#

Tank you πŸ‘

stuck thorn
#

finally done that DisconnectNestedConnections on deferred and DisconnectCurrentConnections join together to disconnect everything

stuck thorn
#

Actaully I should change how CancelAllMutations work, no matter from which signal you call it, it affects the firingSignal

stuck thorn
#

(because under the hood disconnectall does a DISCONNECT_CURRENT)

#

notes should be taken that this only works on firing signal (it will have differnet behavior on a non firing signal)

rough rivet
#

could you make this a package? so it automatically updates?

stuck thorn
#

Hmmm, actually yeah I can lol

stuck thorn
#

nvm I see why nobody can't even do packages lol

#

guess it's gonna be... YOU job to make that package YOUSELF

scarlet pecan
stuck thorn
#

update it once when you feel bored to see what it might break

scarlet pecan
scarlet pecan
scarlet pecan
stuck thorn
stuck thorn
#

πŸ—£οΈ

scarlet pecan
# stuck thorn Its only like that because how am I suppose to know which one truly wants it def...

Use a table for the flag that ⁨⁨⁨⁨⁨⁨⁨⁨⁨:DisconnectNestedConnections()⁩⁩⁩⁩⁩⁩⁩⁩⁩ uses, to store each signal as an index with a ⁨⁨⁨⁨⁨⁨⁨⁨⁨true | nil⁩⁩⁩⁩⁩⁩⁩⁩⁩ value if ⁨⁨⁨⁨[signal]:DisconnectNestedConnections("deferred")⁩⁩⁩⁩ is called.
^ BUT make it a new flag if it'll break the behavior of other existing signal methods
⁨⁨⁨⁨⁨⁨⁨⁨```lua
local disconnectNestedQueue = {}
disconnectNestedQueue[signal] = true

stuck thorn
#

Well then, another "queue" it is

scarlet pecan
#

Sounds good πŸ”₯

stuck thorn
#

so this gonna evaporate the whole _disconnectallstate on signals

#

amazing

#

Though a design choice now

#

should I make it so calling CancelAllMutations after CancelAllMutations just doesn't happen

scarlet pecan
# stuck thorn should I make it so calling `CancelAllMutations` after `CancelAllMutations` just...

Nope unless the mode is ⁨⁨⁨⁨⁨⁨"deferred"⁩⁩⁩⁩⁩⁩ or ⁨⁨⁨⁨⁨⁨"both"⁩⁩⁩⁩⁩ on the first time it's called⁩, but even then I think it's better to leave it for the sake of debuggability.
^ If you do plan on adding it, include a ⁨⁨⁨⁨warn()⁩⁩⁩⁩ that CancelAllMutations is already deferred pls.
⁨⁨⁨⁨⁨⁨```lua
signal:Connect(function()
cn1 = signal:Connect(fn)
signal:CancelAllMutations("immediate")
print(cn1.Connected) -- false

cn2 = signal:Connect(fn)
signal:CancelAllMutations("immediate")
print(cn2.Connected) -- false
end)

signal:Connect(function()
cn1 = signal:Connect(fn)
signal:CancelAllMutations("both")
print(cn1.Connected) -- false

cn2 = signal:Connect(fn)
print(cn2.Connected) -- true (deferred false)
signal:CancelAllMutations("immediate") -- If warn() is added: "CancelAllMutations is already deferred, no need for an immediate cancel afterwards."
print(cn2.Connected) -- false
end)

stuck thorn
#

guess im not doing that

scarlet pecan
#

I just realized I never properly tested what would happen if ⁨CancelAllMutations⁩ is called by non-firing signals, doing that real quick

stuck thorn
#

I changed that real quickly

stuck thorn
#

actually I just realized something

#

LOL

scarlet pecan
stuck thorn
#

Im calling this after I set cancel queue

#

so im effectively canceling the cancel queue

scarlet pecan
# stuck thorn so im effectively canceling the cancel queue

πŸ€‘
What does this mean exactly btw?
This?
⁨⁨```lua
signal:Connect(function()
cn1 = signal:Connect(fn)
signal:CancelAllMutations("deferred")
signal:CancelAllMutations("immediate") -- Cancels the one above & no mutations are cancelled?
print(cn1.Connected) -- true?
end)

#

Or is that just cleanup after the Fire is finished?

stuck thorn
#
signal:CancelAllMutations("both") -- cancels itself
scarlet pecan
stuck thorn
#

actualy wrong example ven

#

So in both mode it firstly set itself on CANCEL_QUEUE but then moments later clears the sharedDisconnectAllStates table, thus canceling itself out

scarlet pecan
stuck thorn
#

I fixed that in 5 seconds okay

stuck thorn
scarlet pecan
#

gotcha

stuck thorn
#

Though once again, I think this going to slow Signal firing speeds down once you got 28123 signals wanting their connections disconnected and needing to loop through the entire queue

#

πŸ’”

#

Actually hah, no (because I no longer need to insert mutations)

#

because cmon, this was just stupid LOL

scarlet pecan
#

Peak performance trust

#

Updated the SomeSignal's devforum download and added the creator store link πŸ‘

#

Final Speed

SomeSignal

#

Zignal

#

I'd say it's very good for the immense amount of features it offers for versatility & convenience

#

This is like if Eddie Hall or Tom Stoltman is able to nearly catch up to Usain Bolt in a sprint

stuck thorn
#

I think changing the disconnect thingy was a good thing for me

#

just simplified the whole disconnect functions by a heck ton

#

so is this now like ""finished""

scarlet pecan
#

Gonna do all the tests in ⁨⁨:PriorityFire()⁩⁩ now 😈

#

Yup everything works πŸ‘
(Simply did Ctrl+F ⁨:Fire⁩ -> ⁨:PriorityFire⁩)

#

SomeSignal should be 100% fine to officially announce on the devforum now