quad/src/signal.lua

177 lines
4.1 KiB
Lua

local module = {}
local running = coroutine.running
local resume = coroutine.resume
local yield = coroutine.yield
local insert = table.insert
local remove = table.remove
function module.init(shared)
---@class quad_module_signal
local new = {}
local warn = shared.warn
-- roblox connections disconnecter
local disconnecters = {}
new.disconnecters = disconnecters
local disconnecterClass = {__type = "quad_disconnecter"}
disconnecterClass.__index = disconnecterClass
function disconnecterClass:Add(connection)
insert(self,connection)
end
function disconnecterClass:Disconnect()
for i,v in pairs(self) do
pcall(v.Disconnect,v)
self[i] = nil
end
end
function disconnecterClass:Destroy()
local id = self.id
if id then
disconnecters[id] = nil
end
for i,v in pairs(self) do
pcall(v.Disconnect,v)
self[i] = nil
end
setmetatable(self,nil)
end
function disconnecterClass.New(id)
if id then
local old = disconnecters[id];
if old then
return old;
end
end
local this = setmetatable({id = id},disconnecterClass);
if id then
disconnecters[id] = this;
end
return this;
end
new.Disconnecter = disconnecterClass
local connection = {__type = "quad_connection"}
connection.__index = connection
function connection.New(signal,func)
return setmetatable({signal = signal,func = func},connection)
end
function connection:Disconnect(slient)
local signal = self.signal
local onceConnection = signal.onceConnection
local connections = signal.connection
for i,v in pairs(connections) do
if v.connection == self then
remove(connections,i)
return
end
end
for i,v in pairs(onceConnection) do
if v.connection == self then
remove(onceConnection,i)
return
end
end
if not slient then
warn(("Connection %s is not found from signal %s, but tried :Disconnect(). Maybe disconnected aleady?"):format(tostring(self),tostring(signal)))
end
end
function connection:Destroy()
return self:Disconnect(true)
end
new.Connection = connection
local signals = {}
local signal = {__type = "quad_bindable"}
signal.__index = signal
function signal:Fire(...)
local waitting = self.waitting
local onceConnection = self.onceConnection
self.waitting = {}
self.onceConnection = {}
for _,v in pairs(waitting) do
task.spawn(resume,v,...)
end
for _,v in pairs(self.connection) do
task.spawn(v.func,...)
end
for _,v in pairs(onceConnection) do
task.spawn(v.func,...)
end
end
function signal:DisconnectAll()
local waitting = self.waitting
self.waitting = {}
self.onceConnection = {}
self.connection = {}
for _,v in pairs(waitting) do
task.spawn(resume,v,nil)
end
end
function signal:Wait()
insert(self.waitting,running())
return yield()
end
function signal:CheckConnected(func)
local onceConnection = self.onceConnection
local connections = self.connection
for _,v in pairs(connections) do
if v.func == func then
return true
end
end
for _,v in pairs(onceConnection) do
if v.func == func then
return true
end
end
return false
end
function signal:Connect(func)
if self:CheckConnected(func) then
warn(("[Quad] Function %s Connected already on signal %s"):format(tostring(func),tostring(self)))
end
local thisConnection = connection.New(self,func)
insert(self.connection,{func=func,connection=thisConnection})
return thisConnection
end
function signal:Once(func)
if self:CheckConnected(func) then
warn(("[Quad] Function %s Connected already on signal %s"):format(tostring(func),tostring(self)))
end
local thisConnection = connection.New(self,func)
insert(self.onceConnection,{func=func,connection=thisConnection})
return thisConnection
end
function signal:Destroy()
local id = self.id
if id then
signals[id] = nil
end
self:DisconnectAll()
setmetatable(self,nil)
end
function signal.New(id)
if id and signals[id] then
return signals[id]
end
local this = {id=id,waitting={},onceConnection={},connection={}}
setmetatable(this,signal)
if id then
signals[id] = this
end
return this
end
new.Bindable = signal
return new
end
return module