Advertisement
KashTheKingYT

Signal

Mar 4th, 2023
825
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.17 KB | None | 0 0
  1. --[=[
  2.     A class which holds data and methods for ScriptSignals.
  3.  
  4.     @class ScriptSignal
  5. ]=]
  6. local ScriptSignal = {}
  7. ScriptSignal.__index = ScriptSignal
  8.  
  9. --[=[
  10.     A class which holds data and methods for ScriptConnections.
  11.  
  12.     @class ScriptConnection
  13. ]=]
  14. local ScriptConnection = {}
  15. ScriptConnection.__index = ScriptConnection
  16.  
  17. --[=[
  18.     A boolean which determines if a ScriptConnection is active or not.
  19.  
  20.     @prop Connected boolean
  21.     @within ScriptConnection
  22.  
  23.     @readonly
  24.     @ignore
  25. ]=]
  26.  
  27.  
  28. export type Class = typeof( setmetatable({
  29.     _active = true,
  30.     _head = nil :: ScriptConnectionNode?
  31. }, ScriptSignal) )
  32.  
  33. export type ScriptConnection = typeof( setmetatable({
  34.     Connected = true,
  35.     _node = nil :: ScriptConnectionNode?
  36. }, ScriptConnection) )
  37.  
  38. type ScriptConnectionNode = {
  39.     _signal: Class,
  40.     _connection: ScriptConnection?,
  41.     _handler: (...any) -> (),
  42.  
  43.     _next: ScriptConnectionNode?,
  44.     _prev: ScriptConnectionNode?
  45. }
  46.  
  47.  
  48. local FreeThread: thread? = nil
  49.  
  50. local function RunHandlerInFreeThread(handler, ...)
  51.     local thread = FreeThread :: thread
  52.     FreeThread = nil
  53.  
  54.     handler(...)
  55.  
  56.     FreeThread = thread
  57. end
  58.  
  59. local function CreateFreeThread()
  60.     FreeThread = coroutine.running()
  61.  
  62.     while true do
  63.         RunHandlerInFreeThread( coroutine.yield() )
  64.     end
  65. end
  66.  
  67. --[=[
  68.     Creates a ScriptSignal object.
  69.  
  70.     @return ScriptSignal
  71.     @ignore
  72. ]=]
  73. function ScriptSignal.new(): Class
  74.     return setmetatable({
  75.         _active = true,
  76.         _head = nil
  77.     }, ScriptSignal)
  78. end
  79.  
  80. --[=[
  81.     Returns a boolean determining if the object is a ScriptSignal.
  82.  
  83.     ```lua
  84.     local janitor = Janitor.new()
  85.     local signal = ScriptSignal.new()
  86.  
  87.     ScriptSignal.Is(signal) -> true
  88.     ScriptSignal.Is(janitor) -> false
  89.     ```
  90.  
  91.     @param object any
  92.     @return boolean
  93.     @ignore
  94. ]=]
  95. function ScriptSignal.Is(object): boolean
  96.     return typeof(object) == 'table'
  97.         and getmetatable(object) == ScriptSignal
  98. end
  99.  
  100. --[=[
  101.     Returns a boolean determing if a ScriptSignal object is active.
  102.  
  103.     ```lua
  104.     ScriptSignal:IsActive() -> true
  105.     ScriptSignal:Destroy()
  106.     ScriptSignal:IsActive() -> false
  107.     ```
  108.  
  109.     @return boolean
  110.     @ignore
  111. ]=]
  112. function ScriptSignal:IsActive(): boolean
  113.     return self._active == true
  114. end
  115.  
  116. --[=[
  117.     Connects a handler to a ScriptSignal object.
  118.  
  119.     ```lua
  120.     ScriptSignal:Connect(function(text)
  121.         print(text)
  122.     end)
  123.  
  124.     ScriptSignal:Fire("Something")
  125.     ScriptSignal:Fire("Something else")
  126.  
  127.     -- "Something" and then "Something else" are printed
  128.     ```
  129.  
  130.     @param handler (...: any) -> ()
  131.     @return ScriptConnection
  132.     @ignore
  133. ]=]
  134. function ScriptSignal:Connect(
  135.     handler: (...any) -> ()
  136. ): ScriptConnection
  137.  
  138.     assert(
  139.         typeof(handler) == 'function',
  140.         "Must be function"
  141.     )
  142.  
  143.     if self._active ~= true then
  144.         return setmetatable({
  145.             Connected = false,
  146.             _node = nil
  147.         }, ScriptConnection)
  148.     end
  149.  
  150.     local _head: ScriptConnectionNode? = self._head
  151.  
  152.     local node: ScriptConnectionNode = {
  153.         _signal = self :: Class,
  154.         _connection = nil,
  155.         _handler = handler,
  156.  
  157.         _next = _head,
  158.         _prev = nil
  159.     }
  160.  
  161.     if _head ~= nil then
  162.         _head._prev = node
  163.     end
  164.  
  165.     self._head = node
  166.  
  167.     local connection = setmetatable({
  168.         Connected = true,
  169.         _node = node
  170.     }, ScriptConnection)
  171.  
  172.     node._connection = connection
  173.  
  174.     return connection :: ScriptConnection
  175. end
  176.  
  177. --[=[
  178.     Connects a handler to a ScriptSignal object, but only allows that
  179.     connection to run once. Any `:Fire` calls called afterwards won't trigger anything.
  180.  
  181.     ```lua
  182.     ScriptSignal:ConnectOnce(function()
  183.         print("Connection fired")
  184.     end)
  185.  
  186.     ScriptSignal:Fire()
  187.     ScriptSignal:Fire()
  188.  
  189.     -- "Connection fired" is only fired once
  190.     ```
  191.  
  192.     @param handler (...: any) -> ()
  193.     @ignore
  194. ]=]
  195. function ScriptSignal:ConnectOnce(
  196.     handler: (...any) -> ()
  197. )
  198.     assert(
  199.         typeof(handler) == 'function',
  200.         "Must be function"
  201.     )
  202.  
  203.     local connection
  204.     connection = self:Connect(function(...)
  205.         connection:Disconnect()
  206.         handler(...)
  207.     end)
  208. end
  209.  
  210. --[=[
  211.     Yields the thread until a `:Fire` call occurs, returns what the signal was fired with.
  212.  
  213.     ```lua
  214.     task.spawn(function()
  215.         print(
  216.             ScriptSignal:Wait()
  217.         )
  218.     end)
  219.  
  220.     ScriptSignal:Fire("Arg", nil, 1, 2, 3, nil)
  221.     -- "Arg", nil, 1, 2, 3, nil are printed
  222.     ```
  223.  
  224.     @yields
  225.     @return ...any
  226.     @ignore
  227. ]=]
  228. function ScriptSignal:Wait(): (...any)
  229.     local thread do
  230.         thread = coroutine.running()
  231.  
  232.         local connection
  233.         connection = self:Connect(function(...)
  234.             connection:Disconnect()
  235.             task.spawn(thread, ...)
  236.         end)
  237.     end
  238.  
  239.     return coroutine.yield()
  240. end
  241.  
  242. --[=[
  243.     Fires a ScriptSignal object with the arguments passed.
  244.  
  245.     ```lua
  246.     ScriptSignal:Connect(function(text)
  247.         print(text)
  248.     end)
  249.  
  250.     ScriptSignal:Fire("Some Text...")
  251.  
  252.     -- "Some Text..." is printed twice
  253.     ```
  254.  
  255.     @param ... any
  256.     @ignore
  257. ]=]
  258. function ScriptSignal:Fire(...: any)
  259.     local node: ScriptConnectionNode? = self._head
  260.     while node ~= nil do
  261.         if node._connection ~= nil then
  262.             if FreeThread == nil then
  263.                 task.spawn(CreateFreeThread)
  264.             end
  265.  
  266.             task.spawn(
  267.                 FreeThread :: thread,
  268.                 node._handler, ...
  269.             )
  270.         end
  271.  
  272.         node = node._next
  273.     end
  274. end
  275.  
  276. --[=[
  277.     Disconnects all connections from a ScriptSignal object without making it unusable.
  278.  
  279.     ```lua
  280.     local connection = ScriptSignal:Connect(function() end)
  281.  
  282.     connection.Connected -> true
  283.     ScriptSignal:DisconnectAll()
  284.     connection.Connected -> false
  285.     ```
  286.  
  287.     @ignore
  288. ]=]
  289. function ScriptSignal:DisconnectAll()
  290.     local node: ScriptConnectionNode? = self._head
  291.     while node ~= nil do
  292.         local _connection = node._connection
  293.  
  294.         if _connection ~= nil then
  295.             _connection.Connected = false
  296.             _connection._node = nil
  297.             node._connection = nil
  298.         end
  299.  
  300.         node = node._next
  301.     end
  302.  
  303.     self._head = nil
  304. end
  305.  
  306. --[=[
  307.     Destroys a ScriptSignal object, disconnecting all connections and making it unusable.
  308.  
  309.     ```lua
  310.     ScriptSignal:Destroy()
  311.  
  312.     local connection = ScriptSignal:Connect(function() end)
  313.     connection.Connected -> false
  314.     ```
  315.  
  316.     @ignore
  317. ]=]
  318. function ScriptSignal:Destroy()
  319.     if self._active ~= true then
  320.         return
  321.     end
  322.  
  323.     self:DisconnectAll()
  324.     self._active = false
  325. end
  326.  
  327. --[=[
  328.     Disconnects a connection, any `:Fire` calls from now on will not
  329.     invoke this connection's handler.
  330.  
  331.     ```lua
  332.     local connection = ScriptSignal:Connect(function() end)
  333.  
  334.     connection.Connected -> true
  335.     connection:Disconnect()
  336.     connection.Connected -> false
  337.     ```
  338.  
  339.     @ignore
  340. ]=]
  341. function ScriptConnection:Disconnect()
  342.     if self.Connected ~= true then
  343.         return
  344.     end
  345.  
  346.     self.Connected = false
  347.  
  348.     local _node: ScriptConnectionNode = self._node
  349.     local _prev = _node._prev
  350.     local _next = _node._next
  351.  
  352.     if _next ~= nil then
  353.         _next._prev = _prev
  354.     end
  355.  
  356.     if _prev ~= nil then
  357.         _prev._next = _next
  358.     else
  359.         -- _node == _signal._head
  360.  
  361.         _node._signal._head = _next
  362.     end
  363.  
  364.     _node._connection = nil
  365.     self._node = nil
  366. end
  367.  
  368. -- Compatibility methods for TopbarPlus
  369. ScriptConnection.destroy = ScriptConnection.Disconnect
  370. ScriptConnection.Destroy = ScriptConnection.Disconnect
  371. ScriptConnection.disconnect = ScriptConnection.Disconnect
  372. ScriptSignal.destroy = ScriptSignal.Destroy
  373. ScriptSignal.Disconnect = ScriptSignal.Destroy
  374. ScriptSignal.disconnect = ScriptSignal.Destroy
  375.  
  376. ScriptSignal.connect = ScriptSignal.Connect
  377. ScriptSignal.wait = ScriptSignal.Wait
  378. ScriptSignal.fire = ScriptSignal.Fire
  379.  
  380. _G.Signal = ScriptSignal
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement