Advertisement
Perth

Untitled

Sep 26th, 2021
5,328
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 3.53 KB | None | 0 0
  1. --- Lua-side duplication of the API of events on Roblox objects.
  2. -- Signals are needed for to ensure that for local events objects are passed by
  3. -- reference rather than by value where possible, as the BindableEvent objects
  4. -- always pass signal arguments by value, meaning tables will be deep copied.
  5. -- Roblox's deep copy method parses to a non-lua table compatable format.
  6. -- @classmod Signal
  7.  
  8. local HttpService = game:GetService("HttpService")
  9.  
  10. local ENABLE_TRACEBACK = false
  11.  
  12. local Signal = {}
  13. Signal.__index = Signal
  14. Signal.ClassName = "Signal"
  15.  
  16. --- Constructs a new signal.
  17. -- @constructor Signal.new()
  18. -- @treturn Signal
  19. function Signal.new()
  20.     local self = setmetatable({}, Signal)
  21.  
  22.     self._bindableEvent = Instance.new("BindableEvent")
  23.     self._argMap = {}
  24.     self._source = ENABLE_TRACEBACK and debug.traceback() or ""
  25.  
  26.     -- Events in Roblox execute in reverse order as they are stored in a linked list and
  27.     -- new connections are added at the head. This event will be at the tail of the list to
  28.     -- clean up memory.
  29.     self._bindableEvent.Event:Connect(function(key)
  30.         self._argMap[key] = nil
  31.  
  32.         -- We've been destroyed here and there's nothing left in flight.
  33.         -- Let's remove the argmap too.
  34.         -- This code may be slower than leaving this table allocated.
  35.         if (not self._bindableEvent) and (not next(self._argMap)) then
  36.             self._argMap = nil
  37.         end
  38.     end)
  39.  
  40.     return self
  41. end
  42.  
  43. --- Fire the event with the given arguments. All handlers will be invoked. Handlers follow
  44. -- Roblox signal conventions.
  45. -- @param ... Variable arguments to pass to handler
  46. -- @treturn nil
  47. function Signal:Fire(...)
  48.     if not self._bindableEvent then
  49.         warn(("Signal is already destroyed. %s"):format(self._source))
  50.         return
  51.     end
  52.  
  53.     local args = table.pack(...)
  54.  
  55.     -- TODO: Replace with a less memory/computationally expensive key generation scheme
  56.     local key = HttpService:GenerateGUID(false)
  57.     self._argMap[key] = args
  58.  
  59.     -- Queues each handler onto the queue.
  60.     self._bindableEvent:Fire(key)
  61. end
  62.  
  63. --- Connect a new handler to the event. Returns a connection object that can be disconnected.
  64. -- @tparam function handler Function handler called with arguments passed when `:Fire(...)` is called
  65. -- @treturn Connection Connection object that can be disconnected
  66. function Signal:Connect(handler)
  67.     if not (type(handler) == "function") then
  68.         error(("connect(%s)"):format(typeof(handler)), 2)
  69.     end
  70.  
  71.     return self._bindableEvent.Event:Connect(function(key)
  72.         -- note we could queue multiple events here, but we'll do this just as Roblox events expect
  73.         -- to behave.
  74.  
  75.         local args = self._argMap[key]
  76.         if args then
  77.             handler(table.unpack(args, 1, args.n))
  78.         else
  79.             error("Missing arg data, probably due to reentrance.")
  80.         end
  81.     end)
  82. end
  83.  
  84. --- Wait for fire to be called, and return the arguments it was given.
  85. -- @treturn ... Variable arguments from connection
  86. function Signal:Wait()
  87.     local key = self._bindableEvent.Event:Wait()
  88.     local args = self._argMap[key]
  89.     if args then
  90.         return table.unpack(args, 1, args.n)
  91.     else
  92.         error("Missing arg data, probably due to reentrance.")
  93.         return nil
  94.     end
  95. end
  96.  
  97. --- Disconnects all connected events to the signal. Voids the signal as unusable.
  98. -- @treturn nil
  99. function Signal:Destroy()
  100.     if self._bindableEvent then
  101.         -- This should disconnect all events, but in-flight events should still be
  102.         -- executed.
  103.  
  104.         self._bindableEvent:Destroy()
  105.         self._bindableEvent = nil
  106.     end
  107.  
  108.     -- Do not remove the argmap. It will be cleaned up by the cleanup connection.
  109.  
  110.     setmetatable(self, nil)
  111. end
  112.  
  113. return Signal
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement