Module: https://www.roblox.com/library/2985743347/event
I haven’t had any issues with it, but if you have any, please mention them.
If there is anything I can improve on, please let me know.
I’ve copied and pasted the code from Halalaluyafail3’s ModuleScript below for easier viewing.
Code
return {
new = function()
local Event = {}
local ConnectedFunctions = {}
local YieldedThreads = {}
local HiddenFunctions = {}
local HiddenDefaults = {}
local Current
local EventIndex = {}
local mt = {}
function EventIndex:Connect(func,...)
if type(func) == "function" then
local connection = {}
local connectionindex = {Connected=true}
local mt2 = {}
function connectionindex:Disconnect()
if connectionindex.Connected then
connectionindex.Connected = false
HiddenFunctions[connection] = nil
HiddenDefaults[connection] = nil
for i=1,#ConnectedFunctions do
if ConnectedFunctions[i] == connection then
table.remove(ConnectedFunctions,i)
break
end
end
connectionindex.Disconnect = nil
connectionindex = nil
connection = nil
mt2 = nil
end
end
mt2.__index = connectionindex
mt2.__newindex = function()
error("Attempt to modify a non existant/readonly value")
end
mt2.__metatable = "This metatable is hidden"
setmetatable(connection,mt2)
table.insert(ConnectedFunctions,connection)
HiddenFunctions[connection] = func
HiddenDefaults[connection] = {...}
return connection
else
error("Can only connect functions")
end
end
function EventIndex:Wait()
table.insert(YieldedThreads,coroutine.running())
coroutine.yield()
return unpack(Current)
end
function EventIndex:Fire(...)
Current = {...}
for i=#YieldedThreads,1,-1 do
coroutine.resume(YieldedThreads[i])
table.remove(YieldedThreads,i)
end
for i=1,#ConnectedFunctions do
local con = ConnectedFunctions[i]
local f = HiddenFunctions[con]
local d = HiddenDefaults[con]
local tab = {}
if #d == 0 then
tab = {...}
else
tab = {unpack(d),...}
end
coroutine.resume(coroutine.create(function()
local success,msg = pcall(f,unpack(tab))
if not success then
local timestamp = os.date("*t")
print(timestamp.hour..":"..timestamp.min..":"..timestamp.sec.." - "..tostring(msg))
end
end))
end
Current = nil
end
function EventIndex:Destroy()
EventIndex.Fire = nil
EventIndex.Wait = nil
EventIndex.Connect = nil
for i=1,#ConnectedFunctions do
HiddenFunctions[ConnectedFunctions[i]] = nil
HiddenDefaults[ConnectedFunctions[i]] = nil
ConnectedFunctions[i] = nil
end
Current = {"Destroy called on event"}
for i=1,#YieldedThreads do
coroutine.resume(YieldedThreads[i])
YieldedThreads[i] = nil
end
Event = nil
ConnectedFunctions = nil
YieldedThreads = nil
HiddenFunctions = nil
HiddenDefaults = nil
Current = nil
EventIndex = nil
mt = nil
end
mt.__index = EventIndex
mt.__newindex = function()
error("Attempt to modify a non existant/readonly value")
end
mt.__metatable = "This metatable is hidden"
setmetatable(Event,mt)
return Event
end
}
2 Likes
I’ve improved this script a great deal. (threads re used whenever possible, O(1) disconnection, etc.)
Code
local TOKEN = newproxy and newproxy() or {}
local warn = warn or print
local function call(f,...)
f(...)
end
local function resumefunc(f,...)
f(...)
while true do
f(coroutine.yield(TOKEN))
end
end
local function CONNECT(event,f)
local nxt = event[2]
local connection = {f,nil,nxt,nil,true,event,debug.traceback("| Connection Point: ",2),false,false}
if nxt then
nxt[4] = connection
end
event[2] = connection
return connection
end
local function DISCONNECT(connection)
if connection[5] then
if connection[8] then
connection[9] = true
return
end
local event,nxt,prev = connection[6],connection[3],connection[4]
if not prev then
if nxt then
nxt[4] = nil
end
event[2],connection[3],connection[5] = nxt,nil,false
elseif nxt then
prev[3],nxt[4],connection[3],connection[4],connection[5] = connection[3],connection[4],nil,nil,false
else
prev[3],connection[4],connection[5] = nil,nil,false
end
end
end
local function RECONNECT(connection)
if not connection[5] then
local event = connection[6]
local nxt = event[2]
if nxt then
nxt[4] = connection
end
event[2],connection[3],connection[5] = connection,nxt,true
elseif connection[9] then
connection[9] = false
end
end
local function ISCONNECTED(connection)
return connection[5] and not connection[9]
end
local function NEWEVENT()
return {{},nil,false}
end
local function WAIT(event)
local threads = event[1]
threads[#threads+1] = coroutine.running()
return coroutine.yield()
end
local function FIRE(event,...)
local isntfiring = not event[3]
if isntfiring then
event[3] = true
end
local firepoint,threads = debug.traceback("| Fire Point: ",2),event[1]
local con = event[2]
while con do
if con[5] and not con[9] then
if con[8] then
local thread = coroutine.create(call)
local success,err = coroutine.resume(thread,con[1],...)
if not success then
warn(string.format("| Error Message (Connection):\n%s\n%s\n%s\n%s\n| End",tostring(err),debug.traceback(thread,"| Traceback:"),firepoint,con[7]))
end
else
con[8] = true
local thread = con[2]
if thread then
local status = coroutine.status(thread)
if status == "suspended" then
local success,err = coroutine.resume(thread,...)
if not success then
con[2] = nil
warn(string.format("| Error Message (Connection):\n%s\n%s\n%s\n%s\n| End",tostring(err),debug.traceback(thread,"| Traceback:"),firepoint,con[7]))
elseif err ~= TOKEN then
con[2] = nil
end
elseif status == "running" or status == "normal" then
thread = coroutine.create(call)
local success,err = coroutine.resume(thread,con[1],...)
if not success then
warn(string.format("| Error Message (Connection):\n%s\n%s\n%s\n%s\n| End",tostring(err),debug.traceback(thread,"| Traceback:"),firepoint,con[7]))
end
else
thread = coroutine.create(resumefunc)
con[2] = thread
local success,err = coroutine.resume(thread,con[1],...)
if not success then
con[2] = nil
warn(string.format("| Error Message (Connection):\n%s\n%s\n%s\n%s\n| End",tostring(err),debug.traceback(thread,"| Traceback:"),firepoint,con[7]))
elseif err ~= TOKEN then
con[2] = nil
end
end
else
thread = coroutine.create(resumefunc)
con[2] = thread
local success,err = coroutine.resume(thread,con[1],...)
if not success then
con[2] = nil
warn(string.format("| Error Message (Connection):\n%s\n%s\n%s\n%s\n| End",tostring(err),debug.traceback(thread,"| Traceback:"),firepoint,con[7]))
elseif err ~= TOKEN then
con[2] = nil
end
end
con[8] = false
if con[9] then
con[9] = false
local oldcon = con
con = con[3]
DISCONNECT(oldcon)
else
con = con[3]
end
end
else
con = con[3]
end
end
for i=#threads,1,-1 do
local thread = threads[i]
local success,err = coroutine.resume(thread,...)
if not success then
warn(string.format("| Error Message (Wait Resume):\n%s\n%s\n%s\n| End",tostring(err),debug.traceback(thread,"| Traceback:"),firepoint))
end
table.remove(threads,i)
end
if isntfiring then
event[3] = false
end
end
Example use
local event = NEWEVENT()
local connection = CONNECT(event,print)
FIRE(event,1,2,3) --> 1 2 3
FIRE(event,nil) --> nil
DISCONNECT(connection)
FIRE(event,4) -- nothing prints
I assume there is still a lot I can improve in this script, so feel free to mention what I can improve.
4 Likes