Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --- Lua-side duplication of the API of events on Roblox objects.
- -- Signals are needed for to ensure that for local events objects are passed by
- -- reference rather than by value where possible, as the BindableEvent objects
- -- always pass signal arguments by value, meaning tables will be deep copied.
- -- Roblox's deep copy method parses to a non-lua table compatable format.
- -- @classmod Signal
- local HttpService = game:GetService("HttpService")
- local ENABLE_TRACEBACK = false
- local Signal = {}
- Signal.__index = Signal
- Signal.ClassName = "Signal"
- --- Constructs a new signal.
- -- @constructor Signal.new()
- -- @treturn Signal
- function Signal.new()
- local self = setmetatable({}, Signal)
- self._bindableEvent = Instance.new("BindableEvent")
- self._argMap = {}
- self._source = ENABLE_TRACEBACK and debug.traceback() or ""
- -- Events in Roblox execute in reverse order as they are stored in a linked list and
- -- new connections are added at the head. This event will be at the tail of the list to
- -- clean up memory.
- self._bindableEvent.Event:Connect(function(key)
- self._argMap[key] = nil
- -- We've been destroyed here and there's nothing left in flight.
- -- Let's remove the argmap too.
- -- This code may be slower than leaving this table allocated.
- if (not self._bindableEvent) and (not next(self._argMap)) then
- self._argMap = nil
- end
- end)
- return self
- end
- --- Fire the event with the given arguments. All handlers will be invoked. Handlers follow
- -- Roblox signal conventions.
- -- @param ... Variable arguments to pass to handler
- -- @treturn nil
- function Signal:Fire(...)
- if not self._bindableEvent then
- warn(("Signal is already destroyed. %s"):format(self._source))
- return
- end
- local args = table.pack(...)
- -- TODO: Replace with a less memory/computationally expensive key generation scheme
- local key = HttpService:GenerateGUID(false)
- self._argMap[key] = args
- -- Queues each handler onto the queue.
- self._bindableEvent:Fire(key)
- end
- --- Connect a new handler to the event. Returns a connection object that can be disconnected.
- -- @tparam function handler Function handler called with arguments passed when `:Fire(...)` is called
- -- @treturn Connection Connection object that can be disconnected
- function Signal:Connect(handler)
- if not (type(handler) == "function") then
- error(("connect(%s)"):format(typeof(handler)), 2)
- end
- return self._bindableEvent.Event:Connect(function(key)
- -- note we could queue multiple events here, but we'll do this just as Roblox events expect
- -- to behave.
- local args = self._argMap[key]
- if args then
- handler(table.unpack(args, 1, args.n))
- else
- error("Missing arg data, probably due to reentrance.")
- end
- end)
- end
- --- Wait for fire to be called, and return the arguments it was given.
- -- @treturn ... Variable arguments from connection
- function Signal:Wait()
- local key = self._bindableEvent.Event:Wait()
- local args = self._argMap[key]
- if args then
- return table.unpack(args, 1, args.n)
- else
- error("Missing arg data, probably due to reentrance.")
- return nil
- end
- end
- --- Disconnects all connected events to the signal. Voids the signal as unusable.
- -- @treturn nil
- function Signal:Destroy()
- if self._bindableEvent then
- -- This should disconnect all events, but in-flight events should still be
- -- executed.
- self._bindableEvent:Destroy()
- self._bindableEvent = nil
- end
- -- Do not remove the argmap. It will be cleaned up by the cleanup connection.
- setmetatable(self, nil)
- end
- return Signal
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement