2013/Libraries/Red/Net/Event.luau

414 lines
9.1 KiB
Plaintext

--!strict
return function(IsServer: boolean)
local RunService = game:GetService "RunService"
local RedEvent = require "../RedEvent"(IsServer)
local Remote = RedEvent.Remote
local Serdes = require "./Serdes"(IsServer)
local Spawn = require "../Util/Spawn"
local Promise = require "../Util/Promise"
local Clock = require "../Util/Clock"
local Event = {}
local nil_symbol = { __nil = true }
Event.Callbacks = {} :: { [string]: ((...any) -> ...any)? }
Event.Outgoing = {} :: any
if not IsServer then
Event.ActiveCalls = {}
end
function Event.Listen()
-- debug.setmemorycategory "Red.Listen"
if not IsServer then
Remote.OnClientEvent:connect(
function(SingleFire, MultipleFire, IncomingCall)
-- debug.profilebegin "Red.Listen.Incoming"
-- replace nil symbols with nil
if SingleFire.__nil then
SingleFire = nil
end
if MultipleFire.__nil then
MultipleFire = nil
end
if IncomingCall.__nil then
IncomingCall = nil
end
if SingleFire then
-- debug.profilebegin "Red.Listen.Incoming.SingleFire"
for EventId, Call in pairs(SingleFire) do
local Callback = Event.Callbacks[EventId]
local c = 0
repeat -- bruh
RunService.Stepped:wait()
Callback = Event.Callbacks[EventId]
c += 1
until Callback or c > 500 -- random
if Callback then
if type(Call) == "table" then
Spawn(Callback, unpack(Call))
else
Spawn(Callback, Call)
end
else
print "[Red]: Callback not found!"
end
end
-- debug.profileend()
end
if MultipleFire then
-- debug.profilebegin "Red.Listen.Incoming.Fire"
for EventId, Calls in pairs(MultipleFire) do
local Callback = Event.Callbacks[EventId]
if Callback then
for _, Call in ipairs(Calls) do
if type(Call) == "table" then
Spawn(Callback, unpack(Call))
else
Spawn(Callback, Call)
end
end
end
end
-- debug.profileend()
end
if IncomingCall then
-- debug.profilebegin "Red.Listen.Incoming.Call"
for _, Call in pairs(IncomingCall) do
local CallId = table.remove(Call, 1)
local Success = table.remove(Call, 1)
if Event.ActiveCalls[CallId] then
if Success then
Event.ActiveCalls[CallId].Resolve(
unpack(Call)
)
else
Event.ActiveCalls[CallId].Reject(
unpack(Call)
)
end
Event.ActiveCalls[CallId] = nil
end
end
-- debug.profileend()
end
-- debug.profileend()
end
)
Clock.new(1 / 60, function()
-- debug.profilebegin "Red.Listen.Outgoing"
if not next(Event.Outgoing) then
return
end
local SingleFire = {}
local SendSingleFire = false
if Event.Outgoing[1] then
for EventId, Calls in pairs(Event.Outgoing[1]) do
if #Calls == 1 then
SingleFire[EventId] = Calls[1]
Event.Outgoing[1][EventId] = nil
SendSingleFire = true
end
end
end
-- nils cannot be sent properly across remoteevents in 2013,
-- so we have to use a symbol to represent nil
local sf = nil_symbol
if SendSingleFire then
sf = SingleFire
end
local eo1, eo2 = Event.Outgoing[1], Event.Outgoing[2]
if eo1 == nil then
eo1 = nil_symbol
end
if eo2 == nil then
eo2 = nil_symbol
end
Remote:FireServer(sf, eo1, eo2)
-- table.clear(Event.Outgoing)
for i, _ in pairs(Event.Outgoing) do
Event.Outgoing[i] = nil
end
-- debug.profileend()
end)
else
Remote.OnServerEvent:connect(
function(Player, SingleFire, MultipleFire, IncomingCall)
-- debug.profilebegin "Red.Listen.Incoming"
-- replace nil symbols with nil
if SingleFire.__nil then
SingleFire = nil
end
if MultipleFire.__nil then
MultipleFire = nil
end
if IncomingCall.__nil then
IncomingCall = nil
end
if SingleFire then
-- debug.profilebegin "Red.Listen.Incoming.SingleFire"
for EventId, Call in pairs(SingleFire) do
local Callback = Event.Callbacks[EventId]
if Callback then
if type(Call) == "table" then
Spawn(Callback, Player, unpack(Call))
else
Spawn(Callback, Player, Call)
end
end
end
-- debug.profileend()
end
if MultipleFire then
-- debug.profilebegin "Red.Listen.Incoming.MultipleFire"
for EventId, Calls in pairs(MultipleFire) do
local Callback = Event.Callbacks[EventId]
if Callback then
for _, Call in ipairs(Calls) do
if type(Call) == "table" then
Spawn(Callback, Player, unpack(Call))
else
Spawn(Callback, Player, Call)
end
end
end
end
-- debug.profileend()
end
if IncomingCall then
-- debug.profilebegin "Red.Listen.Incoming.Call"
for EventId, Calls in pairs(IncomingCall) do
if Event.Callbacks[EventId] then
for _, Call in ipairs(Calls) do
Spawn(function()
local CallId = table.remove(Call, 1)
local Result = {
CallId,
pcall(
Event.Callbacks[EventId],
Player,
unpack(Call)
),
}
if Event.Outgoing[Player] == nil then
Event.Outgoing[Player] = {}
end
if Event.Outgoing[Player][2] == nil then
Event.Outgoing[Player][2] = {}
end
table.insert(
Event.Outgoing[Player][2],
Result
)
end)
end
else
if Event.Outgoing[Player] == nil then
Event.Outgoing[Player] = {}
end
if Event.Outgoing[Player][2] == nil then
Event.Outgoing[Player][2] = {}
end
for _, Call in ipairs(Calls) do
table.insert(Event.Outgoing[Player][2], {
Call[1],
false,
"[Red]: Event not found",
})
end
end
end
-- debug.profileend()
end
-- debug.profileend()
end
)
RunService.Heartbeat:connect(function()
-- debug.profilebegin "Red.Listen.Outgoing"
for Player, Packets in pairs(Event.Outgoing) do
local SingleCall = {}
local SendSingleCall = false
if Packets[1] then
for EventId, Calls in pairs(Packets[1]) do
if #Calls == 1 then
SingleCall[EventId] = Calls[1]
Packets[1][EventId] = nil
SendSingleCall = true
end
end
end
-- nils cannot be sent properly across remoteevents in 2013,
-- so we have to use a symbol to represent nil
local sc = nil_symbol
if SendSingleCall then
sc = SingleCall
end
local p1, p2 = Packets[1], Packets[2]
if p1 == nil then
p1 = nil_symbol
end
if p2 == nil then
p2 = nil_symbol
end
Remote:FireClient(Player, sc, p1, p2)
end
-- table.clear(Event.Outgoing)
for i, _ in pairs(Event.Outgoing) do
Event.Outgoing[i] = nil
end
-- debug.profileend()
end)
end
end
function Event.AddQueue(Queue: { any }, Call: { any })
local Length = #Call
if Length == 1 then
local Type = type(Call[1])
if Type ~= "table" then
table.insert(Queue, Call[1])
else
table.insert(Queue, Call)
end
else
table.insert(Queue, Call)
end
end
function Event.FireClient(Player: Player, EventName: string, ...)
assert(IsServer, "Event.FireClient can only be called from the server")
local EventId = Serdes.IdentifierAsync(EventName)
if Event.Outgoing[Player] == nil then
Event.Outgoing[Player] = {}
end
if Event.Outgoing[Player][1] == nil then
Event.Outgoing[Player][1] = {}
end
if Event.Outgoing[Player][1][EventId] == nil then
Event.Outgoing[Player][1][EventId] = {}
end
Event.AddQueue(Event.Outgoing[Player][1][EventId], { ... })
end
function Event.FireServer(EventName: string, ...)
assert(
not IsServer,
"Event.FireServer can only be called on the client"
)
local Args = { ... }
return Serdes.Identifier(EventName):Then(function(EventId)
if Event.Outgoing[1] == nil then
Event.Outgoing[1] = {}
end
if Event.Outgoing[1][EventId] == nil then
Event.Outgoing[1][EventId] = {}
end
Event.AddQueue(Event.Outgoing[1][EventId], Args)
end)
end
function Event.Call(EventName: string, ...)
assert(not IsServer, "Event.Call can only be called on the client")
local Args = { ... }
return Promise.new(function(Resolve, Reject)
local CallId = Serdes.OneTime()
local EventId = Serdes.IdentifierAsync(EventName)
if Event.Outgoing[2] == nil then
Event.Outgoing[2] = {}
end
if Event.Outgoing[2][EventId] == nil then
Event.Outgoing[2][EventId] = {}
end
table.insert(Args, 1, CallId)
table.insert(Event.Outgoing[2][EventId], Args)
Event.ActiveCalls[CallId] = {
Resolve = Resolve,
Reject = Reject,
}
end)
end
function Event.SetCallback(EventName: string, Callback: ((...any) -> any)?)
return Serdes.Identifier(EventName):Then(function(EventId)
Event.Callbacks[EventId] = Callback
end)
end
return Event
end