Compile Red library from corescripts/Libraries, update types to make Red FULLY TYPE SAFE!
This commit is contained in:
parent
d0c2b9d636
commit
7c4ee737f1
|
|
@ -0,0 +1,411 @@
|
||||||
|
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
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
return function(IsServer: boolean)
|
||||||
|
local RedEvent = require "../RedEvent"(IsServer)
|
||||||
|
local Event = RedEvent.Remote
|
||||||
|
|
||||||
|
local Promise = require "../Util/Promise"
|
||||||
|
local Serdes = {}
|
||||||
|
|
||||||
|
Serdes.NextId = 1 -- StringValues think null bytes are empty strings
|
||||||
|
Serdes.NextOT = 1
|
||||||
|
|
||||||
|
function Serdes.RegisterIdentifier(Name: string)
|
||||||
|
assert(IsServer, "RegisterIdentifier can only be called on the server")
|
||||||
|
|
||||||
|
local Id = string.char(Serdes.NextId)
|
||||||
|
Serdes.NextId += 1
|
||||||
|
|
||||||
|
local e = Event:FindFirstChild(Name)
|
||||||
|
if e then
|
||||||
|
e.Value = Id
|
||||||
|
else
|
||||||
|
e = Instance.new "StringValue"
|
||||||
|
e.Name = Name
|
||||||
|
e.Value = Id
|
||||||
|
e.Parent = Event
|
||||||
|
end
|
||||||
|
|
||||||
|
return Id
|
||||||
|
end
|
||||||
|
|
||||||
|
function Serdes.Identifier(Name: string)
|
||||||
|
if not IsServer then
|
||||||
|
return Promise.new(function(Resolve)
|
||||||
|
local e = Event:WaitForChild(Name)
|
||||||
|
if e.Value ~= nil then
|
||||||
|
Resolve(e.Value)
|
||||||
|
else
|
||||||
|
local Thread = Delay(5, function()
|
||||||
|
print(
|
||||||
|
"[Red.Serdes]: Retrieving identifier exceeded 5 seconds. Make sure '"
|
||||||
|
.. Name
|
||||||
|
.. "' is registered on the server."
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
e.Changed:Once(function()
|
||||||
|
coroutine.yield(Thread :: thread)
|
||||||
|
|
||||||
|
Resolve(e.Value)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
local e = Event:FindFirstChild(Name)
|
||||||
|
if e and e.Value then
|
||||||
|
return Promise.Resolve(e.Value)
|
||||||
|
end
|
||||||
|
return Promise.Resolve(Serdes.RegisterIdentifier(Name))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Serdes.IdentifierAsync(Name: string)
|
||||||
|
return Serdes.Identifier(Name):Await()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Serdes.OneTime()
|
||||||
|
Serdes.NextOT += 1
|
||||||
|
|
||||||
|
if Serdes.NextOT == 0xFFFF + 1 then
|
||||||
|
Serdes.NextOT = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return string.char(Serdes.NextOT)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Serdes
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
return function(IsServer: boolean)
|
||||||
|
local Players = game:GetService "Players"
|
||||||
|
|
||||||
|
local RedEvent = require "../RedEvent"(IsServer)
|
||||||
|
local Remote = RedEvent.Remote
|
||||||
|
local ClientFolder = RedEvent.ClientFolder
|
||||||
|
|
||||||
|
local Serdes = require "./Serdes"(IsServer)
|
||||||
|
local Event = require "./Event"(IsServer)
|
||||||
|
|
||||||
|
local Server = {}
|
||||||
|
Server.__index = Server
|
||||||
|
|
||||||
|
function Server.Server(Name: string)
|
||||||
|
local self = setmetatable({}, Server)
|
||||||
|
|
||||||
|
self.Name = Name
|
||||||
|
self.FolderInstance = nil :: Folder?
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
Server.new = Server.Server
|
||||||
|
|
||||||
|
function Server.Fire(self: Server, Player: Player, EventName: string, ...)
|
||||||
|
Event.FireClient(Player, self.Name .. "_" .. EventName, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Server.FireAll(self: Server, EventName: string, ...)
|
||||||
|
for _, Player in ipairs(Players:GetPlayers()) do
|
||||||
|
self:Fire(Player, EventName, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Server.FireAllExcept(
|
||||||
|
self: Server,
|
||||||
|
Player: Player,
|
||||||
|
EventName: string,
|
||||||
|
...
|
||||||
|
)
|
||||||
|
for _, OtherPlayer in ipairs(Players:GetPlayers()) do
|
||||||
|
if OtherPlayer ~= Player then
|
||||||
|
self:Fire(OtherPlayer, EventName, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Server.FireList(
|
||||||
|
self: Server,
|
||||||
|
PlayerList: { Player },
|
||||||
|
EventName: string,
|
||||||
|
...
|
||||||
|
)
|
||||||
|
for _, Player in ipairs(PlayerList) do
|
||||||
|
self:Fire(Player, EventName, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Server.FireWithFilter(
|
||||||
|
self: Server,
|
||||||
|
Filter: (Player) -> boolean,
|
||||||
|
EventName: string,
|
||||||
|
...
|
||||||
|
)
|
||||||
|
for _, Player in ipairs(Players:GetPlayers()) do
|
||||||
|
if Filter(Player) then
|
||||||
|
self:Fire(Player, EventName, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Server.On(
|
||||||
|
self: Server,
|
||||||
|
EventName: string,
|
||||||
|
Callback: ((Player, ...any) -> ...any)?
|
||||||
|
)
|
||||||
|
Event.SetCallback(self.Name .. "_" .. EventName, Callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Server.Folder(self: Server, Player: Player?)
|
||||||
|
if Player then
|
||||||
|
ClientFolder = (
|
||||||
|
Player:FindFirstChild "PlayerGui" :: any
|
||||||
|
).Red :: ScreenGui
|
||||||
|
|
||||||
|
if ClientFolder:FindFirstChild(self.Name) then
|
||||||
|
return ClientFolder:FindFirstChild(self.Name) :: Model
|
||||||
|
else
|
||||||
|
local Folder = Instance.new "Model"
|
||||||
|
Folder.Name = self.Name
|
||||||
|
Folder.Parent = ClientFolder
|
||||||
|
|
||||||
|
return Folder :: Model
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if not self.FolderInstance then
|
||||||
|
local Folder = Instance.new "Model"
|
||||||
|
Folder.Name = self.Name
|
||||||
|
Folder.Parent = Remote
|
||||||
|
|
||||||
|
self.FolderInstance = Folder
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.FolderInstance :: Model
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
export type Server = typeof(Server.Server "")
|
||||||
|
|
||||||
|
local Client = {}
|
||||||
|
Client.__index = Client
|
||||||
|
|
||||||
|
function Client.Client(Name: string)
|
||||||
|
local self = setmetatable({}, Client)
|
||||||
|
|
||||||
|
self.Name = Name
|
||||||
|
self.FolderInstance = nil :: Folder?
|
||||||
|
self.LocalFolderInstance = nil :: Folder?
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
Client.new = Client.Client
|
||||||
|
|
||||||
|
function Client.Fire(self: Client, EventName: string, ...)
|
||||||
|
return Event.FireServer(self.Name .. "_" .. EventName, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Client.Call(self: Client, EventName: string, ...)
|
||||||
|
return Event.Call(self.Name .. "_" .. EventName, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Client.On(
|
||||||
|
self: Client,
|
||||||
|
EventName: string,
|
||||||
|
Callback: ((...any) -> ())?
|
||||||
|
)
|
||||||
|
return Event.SetCallback(self.Name .. "_" .. EventName, Callback)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Client.Folder(self: Client)
|
||||||
|
if not self.FolderInstance then
|
||||||
|
self.FolderInstance = Remote:WaitForChild(self.Name) :: Model
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.FolderInstance :: Model
|
||||||
|
end
|
||||||
|
|
||||||
|
function Client.LocalFolder(self: Client)
|
||||||
|
if not self.LocalFolderInstance then
|
||||||
|
self.LocalFolderInstance =
|
||||||
|
ClientFolder:WaitForChild(self.Name) :: Model
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.LocalFolderInstance :: Model
|
||||||
|
end
|
||||||
|
|
||||||
|
export type Client = typeof(Client.Client "")
|
||||||
|
|
||||||
|
local Net = {}
|
||||||
|
|
||||||
|
Net.ServerNamespaceList = {}
|
||||||
|
Net.ClientNamespaceList = {}
|
||||||
|
|
||||||
|
function Net.Server(Name: string, Definitions: { string }?): Server
|
||||||
|
assert(IsServer, "Net.Server can only be used on the server")
|
||||||
|
|
||||||
|
if not Net.ServerNamespaceList[Name] then
|
||||||
|
Net.ServerNamespaceList[Name] = Server.Server(Name)
|
||||||
|
end
|
||||||
|
|
||||||
|
if Definitions then
|
||||||
|
for _, Term in ipairs(Definitions) do
|
||||||
|
Serdes.Identifier((Name :: string) .. "_" .. Term)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Net.ServerNamespaceList[Name]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Net.Client(Name: string): Client
|
||||||
|
assert(not IsServer, "Net.Client can only be used on the client")
|
||||||
|
|
||||||
|
if Net.ClientNamespaceList[Name] == nil then
|
||||||
|
Net.ClientNamespaceList[Name] = Client.Client(Name)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Net.ClientNamespaceList[Name]
|
||||||
|
end
|
||||||
|
|
||||||
|
function Net.Identifier(Name: string)
|
||||||
|
return Serdes.Identifier(Name)
|
||||||
|
end
|
||||||
|
|
||||||
|
Event.Listen()
|
||||||
|
|
||||||
|
return Net
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
return function(IsServer: boolean)
|
||||||
|
local ReplicatedStorage = game:GetService "ReplicatedStorage"
|
||||||
|
local Players = game:GetService "Players"
|
||||||
|
|
||||||
|
local RedEvent = {}
|
||||||
|
|
||||||
|
local Remote, ClientFolder
|
||||||
|
|
||||||
|
if IsServer then
|
||||||
|
Remote = ReplicatedStorage:FindFirstChild "RedEvent" :: RemoteEvent
|
||||||
|
|
||||||
|
if not Remote then -- prevent vile promise bugs
|
||||||
|
Remote = Instance.new "RemoteEvent"
|
||||||
|
Remote.Name = "RedEvent"
|
||||||
|
Remote.Parent = ReplicatedStorage
|
||||||
|
end
|
||||||
|
|
||||||
|
local function PlayerAdded(Player: Player)
|
||||||
|
ClientFolder = Instance.new "ScreenGui"
|
||||||
|
|
||||||
|
-- ClientFolder.Enabled = false
|
||||||
|
-- ClientFolder.ResetOnSpawn = false
|
||||||
|
ClientFolder.Name = "Red"
|
||||||
|
ClientFolder.Parent = Player:FindFirstChild "PlayerGui"
|
||||||
|
end
|
||||||
|
|
||||||
|
Players.PlayerAdded:connect(PlayerAdded)
|
||||||
|
|
||||||
|
for _, Player in ipairs(Players:GetPlayers()) do
|
||||||
|
PlayerAdded(Player)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Remote = ReplicatedStorage:WaitForChild "RedEvent" :: RemoteEvent
|
||||||
|
|
||||||
|
ClientFolder = (
|
||||||
|
Players.LocalPlayer:FindFirstChild "PlayerGui" :: PlayerGui
|
||||||
|
):WaitForChild "Red" :: ScreenGui
|
||||||
|
ClientFolder.Parent = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
RedEvent.Remote = Remote
|
||||||
|
RedEvent.ClientFolder = ClientFolder
|
||||||
|
|
||||||
|
return RedEvent
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
local Spawn = require "./Spawn"
|
||||||
|
local typeof = require "../../../Modules/Polyfill/typeof"
|
||||||
|
|
||||||
|
type BinItem = Instance | RBXScriptConnection | () -> ...any
|
||||||
|
|
||||||
|
return function()
|
||||||
|
local Bin: { BinItem } = {}
|
||||||
|
|
||||||
|
return function(Item: BinItem)
|
||||||
|
table.insert(Bin, Item)
|
||||||
|
end, function()
|
||||||
|
for _, Item in ipairs(Bin) do
|
||||||
|
if typeof(Item) == "Instance" then
|
||||||
|
Item:Destroy()
|
||||||
|
elseif typeof(Item) == "RBXScriptConnection" then
|
||||||
|
Item:disconnect()
|
||||||
|
elseif typeof(Item) == "function" then
|
||||||
|
Spawn(Item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- table.clear(Bin)
|
||||||
|
for i, _ in ipairs(Bin) do
|
||||||
|
Bin[i] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
-- local ReplicatedStorage = game:GetService "ReplicatedStorage"
|
||||||
|
local RunService = game:GetService "RunService"
|
||||||
|
|
||||||
|
local function MakeHeartbeatFunction(Clock: Clock)
|
||||||
|
return function(Delta)
|
||||||
|
Clock:Advance(Delta)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local Clock = {}
|
||||||
|
Clock.__index = Clock
|
||||||
|
|
||||||
|
function Clock.Clock(Interval: number, Callback: () -> ())
|
||||||
|
local self = setmetatable({}, Clock)
|
||||||
|
|
||||||
|
self.Interval = Interval
|
||||||
|
self.Callback = Callback
|
||||||
|
self.Delta = 0
|
||||||
|
|
||||||
|
self.Connection = RunService.Heartbeat:connect(MakeHeartbeatFunction(self))
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
Clock.new = Clock.Clock
|
||||||
|
|
||||||
|
function Clock.Pause(self: Clock)
|
||||||
|
if self.Connection then
|
||||||
|
self.Connection:Disconnect()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Clock.Resume(self: Clock)
|
||||||
|
if self.Connection.Connected then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.Connection = RunService.Heartbeat:connect(MakeHeartbeatFunction(self))
|
||||||
|
end
|
||||||
|
|
||||||
|
function Clock.Advance(self: Clock, Delta: number)
|
||||||
|
self.Delta += Delta
|
||||||
|
|
||||||
|
if self.Delta >= self.Interval * 10 then
|
||||||
|
local Skipped = math.floor(self.Delta / self.Interval)
|
||||||
|
self.Delta -= Skipped * self.Interval
|
||||||
|
|
||||||
|
-- if ReplicatedStorage:GetAttribute "RedDebug" then
|
||||||
|
-- warn(
|
||||||
|
-- "[Red.Clock]: Clock is falling behind! Skipped "
|
||||||
|
-- .. Skipped
|
||||||
|
-- .. " intervals."
|
||||||
|
-- )
|
||||||
|
-- end
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.Delta >= self.Interval then
|
||||||
|
self.Delta -= self.Interval
|
||||||
|
self.Callback()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
export type Clock = typeof(Clock.Clock(...))
|
||||||
|
|
||||||
|
return Clock
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
local CollectionService = game:GetService "CollectionService"
|
||||||
|
|
||||||
|
local Spawn = require "./Spawn"
|
||||||
|
|
||||||
|
return function<T...>(
|
||||||
|
Tag: string,
|
||||||
|
Start: (Instance) -> T...,
|
||||||
|
Stop: (T...) -> ()
|
||||||
|
): () -> ()
|
||||||
|
local InstanceMap = {}
|
||||||
|
|
||||||
|
for _, Instance in ipairs(CollectionService:GetTagged(Tag)) do
|
||||||
|
Spawn(function()
|
||||||
|
InstanceMap[Instance] = { Start(Instance) }
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local AddConnection = CollectionService:GetInstanceAddedSignal(Tag)
|
||||||
|
:connect(function(Instance)
|
||||||
|
InstanceMap[Instance] = { Start(Instance) }
|
||||||
|
end)
|
||||||
|
|
||||||
|
local RemoveConnection = CollectionService:GetInstanceRemovedSignal(Tag)
|
||||||
|
:connect(function(Instance)
|
||||||
|
local Value = InstanceMap[Instance]
|
||||||
|
|
||||||
|
if Value then
|
||||||
|
InstanceMap[Instance] = nil
|
||||||
|
Stop(unpack(Value))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return function()
|
||||||
|
AddConnection:Disconnect()
|
||||||
|
RemoveConnection:Disconnect()
|
||||||
|
|
||||||
|
for Instance, Value in pairs(InstanceMap) do
|
||||||
|
Spawn(Stop, unpack(Value))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,206 @@
|
||||||
|
local RunService = game:GetService "RunService"
|
||||||
|
|
||||||
|
local Spawn = require "./Spawn"
|
||||||
|
|
||||||
|
local Promise = {}
|
||||||
|
Promise.__index = Promise
|
||||||
|
|
||||||
|
function Promise.Promise(Callback: (
|
||||||
|
Resolve: (...any) -> (),
|
||||||
|
Reject: (...any) -> ()
|
||||||
|
) -> ())
|
||||||
|
local self = setmetatable({}, Promise)
|
||||||
|
|
||||||
|
self.Status = "Pending"
|
||||||
|
self.OnResolve = {} :: { (...any) -> () }
|
||||||
|
self.OnReject = {} :: { (...any) -> () }
|
||||||
|
|
||||||
|
self.Value = {} :: { any }
|
||||||
|
|
||||||
|
self.Thread = nil :: thread?
|
||||||
|
self.Thread = coroutine.create(function()
|
||||||
|
local ok, err = ypcall(Callback, function(...)
|
||||||
|
self:_Resolve(...)
|
||||||
|
end, function(...)
|
||||||
|
self:_Reject(...)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if not ok then
|
||||||
|
self:_Reject(err)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
coroutine.resume(self.Thread :: thread)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
Promise.new = Promise.Promise :: (
|
||||||
|
Callback: (Resolve: (...any) -> (), Reject: (...any) -> ()) -> ()
|
||||||
|
) -> Promise
|
||||||
|
|
||||||
|
function Promise.Resolve(...: any): Promise
|
||||||
|
local self = setmetatable({}, Promise)
|
||||||
|
|
||||||
|
self.Status = "Resolved"
|
||||||
|
self.OnResolve = {} :: { (...any) -> () }
|
||||||
|
self.OnReject = {} :: { (...any) -> () }
|
||||||
|
self.Value = { ... } :: { any }
|
||||||
|
self.Thread = nil :: thread?
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Promise.Reject(...: any): Promise
|
||||||
|
local self = setmetatable({}, Promise)
|
||||||
|
|
||||||
|
self.Status = "Rejected"
|
||||||
|
self.OnResolve = {} :: { (...any) -> () }
|
||||||
|
self.OnReject = {} :: { (...any) -> () }
|
||||||
|
self.Value = { ... } :: { any }
|
||||||
|
self.Thread = nil :: thread?
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Promise._Resolve(self: Promise, ...: any)
|
||||||
|
assert(
|
||||||
|
self.Status == "Pending",
|
||||||
|
"Cannot resolve a promise that is not pending."
|
||||||
|
)
|
||||||
|
|
||||||
|
self.Status = "Resolved"
|
||||||
|
self.Value = { ... }
|
||||||
|
|
||||||
|
for _, Callback in ipairs(self.OnResolve) do
|
||||||
|
Spawn(Callback, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- task.defer(task.cancel, self.Thread :: thread)
|
||||||
|
coroutine.resume(coroutine.create(function()
|
||||||
|
coroutine.yield(self.Thread :: thread)
|
||||||
|
end))
|
||||||
|
end
|
||||||
|
|
||||||
|
function Promise._Reject(self: Promise, ...: any)
|
||||||
|
assert(
|
||||||
|
self.Status == "Pending",
|
||||||
|
"Cannot reject a promise that is not pending."
|
||||||
|
)
|
||||||
|
|
||||||
|
self.Status = "Rejected"
|
||||||
|
self.Value = { ... }
|
||||||
|
|
||||||
|
for _, Callback in ipairs(self.OnReject) do
|
||||||
|
Spawn(Callback, ...)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- task.defer(task.cancel, self.Thread :: thread)
|
||||||
|
coroutine.resume(coroutine.create(function()
|
||||||
|
coroutine.yield(self.Thread :: thread)
|
||||||
|
end))
|
||||||
|
end
|
||||||
|
|
||||||
|
function Promise.Then(
|
||||||
|
self: Promise,
|
||||||
|
OnResolve: ((...any) -> ...any)?,
|
||||||
|
OnReject: ((...any) -> ...any)?
|
||||||
|
): Promise
|
||||||
|
return Promise.Promise(function(Resolve, Reject)
|
||||||
|
local function PromiseResolutionProcedure(
|
||||||
|
Value: Promise | any,
|
||||||
|
...: any
|
||||||
|
)
|
||||||
|
if type(Value) == "table" and getmetatable(Value) == Promise then
|
||||||
|
if Value.Status == "Pending" then
|
||||||
|
table.insert(Value.OnResolve, Resolve)
|
||||||
|
table.insert(Value.OnReject, Reject)
|
||||||
|
elseif Value.Status == "Resolved" then
|
||||||
|
Resolve(Value.Value)
|
||||||
|
elseif Value.Status == "Rejected" then
|
||||||
|
Reject(Value.Value)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Resolve(Value, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.Status == "Pending" then
|
||||||
|
if OnResolve then
|
||||||
|
table.insert(self.OnResolve, function(...)
|
||||||
|
PromiseResolutionProcedure(OnResolve(...))
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
table.insert(self.OnResolve, PromiseResolutionProcedure)
|
||||||
|
end
|
||||||
|
|
||||||
|
if OnReject then
|
||||||
|
table.insert(self.OnReject, function(...)
|
||||||
|
PromiseResolutionProcedure(OnReject(...))
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
table.insert(self.OnReject, Reject)
|
||||||
|
end
|
||||||
|
elseif self.Status == "Resolved" then
|
||||||
|
if OnResolve then
|
||||||
|
PromiseResolutionProcedure(OnResolve(unpack(self.Value)))
|
||||||
|
else
|
||||||
|
Resolve(unpack(self.Value))
|
||||||
|
end
|
||||||
|
elseif self.Status == "Rejected" then
|
||||||
|
if OnReject then
|
||||||
|
PromiseResolutionProcedure(OnReject(unpack(self.Value)))
|
||||||
|
else
|
||||||
|
Reject(unpack(self.Value))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Promise.Catch(self: Promise, OnReject: (...any) -> ())
|
||||||
|
return self:Then(nil, OnReject)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Promise.Finally(self: Promise, Finally: () -> ())
|
||||||
|
return self:Then(function(...)
|
||||||
|
Finally()
|
||||||
|
return ...
|
||||||
|
end, function(Error)
|
||||||
|
Finally()
|
||||||
|
error(Error)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Promise.Await(self: Promise): ...any
|
||||||
|
if self.Status == "Resolved" then
|
||||||
|
return unpack(self.Value)
|
||||||
|
elseif self.Status == "Rejected" then
|
||||||
|
return error(unpack(self.Value))
|
||||||
|
end
|
||||||
|
|
||||||
|
local c = 0
|
||||||
|
repeat -- bruh
|
||||||
|
RunService.Stepped:wait()
|
||||||
|
c += 1
|
||||||
|
until self.Status ~= "Pending" or c > 500 -- random
|
||||||
|
|
||||||
|
local Current = coroutine.running()
|
||||||
|
|
||||||
|
local function Resume()
|
||||||
|
coroutine.resume(Current)
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(self.OnResolve, Resume)
|
||||||
|
table.insert(self.OnReject, Resume)
|
||||||
|
|
||||||
|
coroutine.yield()
|
||||||
|
|
||||||
|
if self.Status == "Resolved" then
|
||||||
|
return unpack(self.Value)
|
||||||
|
end
|
||||||
|
return error(unpack(self.Value))
|
||||||
|
end
|
||||||
|
|
||||||
|
export type Promise = typeof(Promise.Promise(...))
|
||||||
|
|
||||||
|
return Promise
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
return function<T>(Limit: number, Interval: number)
|
||||||
|
assert(Limit > 0, "Limit must be greater than 0")
|
||||||
|
|
||||||
|
local CountMap = {} :: { [T]: number }
|
||||||
|
local CountKeyless = 0
|
||||||
|
|
||||||
|
return function(Key: T?)
|
||||||
|
if Key then
|
||||||
|
local Count = CountMap[Key]
|
||||||
|
|
||||||
|
if Count == nil then
|
||||||
|
Count = 0
|
||||||
|
|
||||||
|
Delay(Interval, function()
|
||||||
|
CountMap[Key] = nil
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
if Count >= Limit then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
CountMap[Key] = Count + 1
|
||||||
|
else
|
||||||
|
if CountKeyless == 0 then
|
||||||
|
Delay(Interval, function()
|
||||||
|
CountKeyless = 0
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
if CountKeyless >= Limit then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
CountKeyless += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
local Promise = require "./Promise"
|
||||||
|
local Spawn = require "./Spawn"
|
||||||
|
|
||||||
|
type SignalNode<T...> = {
|
||||||
|
Next: SignalNode<T...>?,
|
||||||
|
Callback: (T...) -> (),
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Signal<T...> = {
|
||||||
|
Root: SignalNode<T...>?,
|
||||||
|
|
||||||
|
Connect: (self: Signal<T...>, Callback: (T...) -> ()) -> () -> (),
|
||||||
|
Wait: (self: Signal<T...>) -> Promise.Promise,
|
||||||
|
Fire: (self: Signal<T...>, T...) -> (),
|
||||||
|
DisconnectAll: (self: Signal<T...>) -> (),
|
||||||
|
}
|
||||||
|
|
||||||
|
local Signal = {}
|
||||||
|
Signal.__index = Signal
|
||||||
|
|
||||||
|
function Signal.new<T...>(): Signal<T...>
|
||||||
|
return setmetatable({
|
||||||
|
Root = nil,
|
||||||
|
}, Signal) :: any
|
||||||
|
end
|
||||||
|
|
||||||
|
function Signal.Connect<T...>(self: Signal<T...>, Callback: (T...) -> ()): () -> ()
|
||||||
|
local Node = {
|
||||||
|
Next = self.Root,
|
||||||
|
Callback = Callback,
|
||||||
|
}
|
||||||
|
|
||||||
|
self.Root = Node
|
||||||
|
|
||||||
|
return function()
|
||||||
|
if self.Root == Node then
|
||||||
|
self.Root = Node.Next
|
||||||
|
else
|
||||||
|
local Current = self.Root
|
||||||
|
while Current do
|
||||||
|
if Current.Next == Node then
|
||||||
|
Current.Next = Node.Next
|
||||||
|
break
|
||||||
|
end
|
||||||
|
Current = Current.Next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Signal.Wait<T...>(self: Signal<T...>): Promise.Promise
|
||||||
|
return Promise.new(function(Resolve)
|
||||||
|
local Disconnect
|
||||||
|
|
||||||
|
Disconnect = self:Connect(function(...)
|
||||||
|
Disconnect()
|
||||||
|
Resolve(... :: any)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Signal.Fire<T...>(self: Signal<T...>, ...: T...)
|
||||||
|
local Current = self.Root
|
||||||
|
while Current do
|
||||||
|
Spawn(Current.Callback, ...)
|
||||||
|
Current = Current.Next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Signal.DisconnectAll<T...>(self: Signal<T...>)
|
||||||
|
self.Root = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return Signal
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
local FreeThread: thread? = nil
|
||||||
|
|
||||||
|
local function FunctionPasser(fn, ...)
|
||||||
|
local AquiredThread = FreeThread
|
||||||
|
FreeThread = nil
|
||||||
|
fn(...)
|
||||||
|
FreeThread = AquiredThread
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Yielder()
|
||||||
|
while true do
|
||||||
|
FunctionPasser(coroutine.yield())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return function<T...>(fn: (T...) -> (), ...: T...)
|
||||||
|
if not FreeThread then
|
||||||
|
FreeThread = coroutine.create(Yielder)
|
||||||
|
coroutine.resume(FreeThread :: any)
|
||||||
|
end
|
||||||
|
|
||||||
|
coroutine.resume(FreeThread :: thread, fn, ...)
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
local function Red(_, Script: LuaSourceContainer)
|
||||||
|
local _SERVER
|
||||||
|
|
||||||
|
if Script:IsA "LocalScript" then
|
||||||
|
_SERVER = false
|
||||||
|
elseif Script:IsA "Script" then
|
||||||
|
_SERVER = true
|
||||||
|
else
|
||||||
|
error("Argument must be the script itself", 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local Net = require "./Net"(_SERVER)
|
||||||
|
|
||||||
|
return {
|
||||||
|
Server = Net.Server,
|
||||||
|
Client = Net.Client,
|
||||||
|
|
||||||
|
Collection = require "./Util/Collection",
|
||||||
|
Ratelimit = require "./Util/Ratelimit",
|
||||||
|
Promise = require "./Util/Promise",
|
||||||
|
Signal = require "./Util/Signal",
|
||||||
|
Clock = require "./Util/Clock",
|
||||||
|
Spawn = require "./Util/Spawn",
|
||||||
|
Bin = require "./Util/Bin",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
Help = function()
|
||||||
|
return "See https://redblox.dev/ for more information."
|
||||||
|
end,
|
||||||
|
Load = Red,
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,181 @@
|
||||||
|
-- A basic polyfill for the typeof function
|
||||||
|
|
||||||
|
return function(value)
|
||||||
|
local basicType = type(value)
|
||||||
|
|
||||||
|
if
|
||||||
|
basicType == "nil"
|
||||||
|
or basicType == "boolean"
|
||||||
|
or basicType == "number"
|
||||||
|
or basicType == "string"
|
||||||
|
or basicType == "function"
|
||||||
|
or basicType == "thread"
|
||||||
|
or basicType == "table"
|
||||||
|
then
|
||||||
|
return basicType
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Will short-circuit
|
||||||
|
--[[
|
||||||
|
{
|
||||||
|
name of type to check,
|
||||||
|
{ list of required properties },
|
||||||
|
}
|
||||||
|
]]
|
||||||
|
local tests = {
|
||||||
|
{
|
||||||
|
"Instance",
|
||||||
|
{ "ClassName" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"EnumItem",
|
||||||
|
{ "EnumType", "Name", "Value" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Enum",
|
||||||
|
{ "GetEnumItems" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Enums",
|
||||||
|
{ "MembershipType" }, -- lmao
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RBXScriptSignal",
|
||||||
|
{
|
||||||
|
"connect",
|
||||||
|
-- "connected",
|
||||||
|
-- "connectFirst",
|
||||||
|
-- "connectLast",
|
||||||
|
"wait",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RBXScriptConnection",
|
||||||
|
{
|
||||||
|
"connected",
|
||||||
|
"disconnect",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"TweenInfo",
|
||||||
|
{
|
||||||
|
"EasingDirection",
|
||||||
|
-- "Time",
|
||||||
|
-- "DelayTime",
|
||||||
|
"RepeatCount",
|
||||||
|
"EasingStyle",
|
||||||
|
-- "Reverses",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"CFrame",
|
||||||
|
{
|
||||||
|
"p",
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
"z",
|
||||||
|
"lookVector",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Vector3",
|
||||||
|
{
|
||||||
|
"Lerp",
|
||||||
|
-- "Cross",
|
||||||
|
-- "Dot",
|
||||||
|
"unit",
|
||||||
|
"magnitude",
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
"z",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Vector3int16",
|
||||||
|
{ "z", "x", "y" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Vector2",
|
||||||
|
{ "unit", "magnitude", "x", "y" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Vector2int16",
|
||||||
|
{ "x", "y" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Region3",
|
||||||
|
{ "CFrame", "Size" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Region3int16",
|
||||||
|
{ "Min", "Max" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Ray",
|
||||||
|
{
|
||||||
|
"Origin",
|
||||||
|
"Direction",
|
||||||
|
"Unit",
|
||||||
|
"ClosestPoint",
|
||||||
|
"Distance",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"UDim",
|
||||||
|
{ "Scale", "Offset" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Axes",
|
||||||
|
{ "Z", "X", "Y" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"UDim2",
|
||||||
|
{ "X", "Y" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"BrickColor",
|
||||||
|
{
|
||||||
|
"Number",
|
||||||
|
"Name",
|
||||||
|
"Color",
|
||||||
|
"r",
|
||||||
|
"g",
|
||||||
|
"b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Color3",
|
||||||
|
{ "r", "g", "b" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Faces",
|
||||||
|
{
|
||||||
|
"Right",
|
||||||
|
"Top",
|
||||||
|
"Back",
|
||||||
|
-- "Left",
|
||||||
|
-- "Bottom",
|
||||||
|
-- "Front",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v in ipairs(tests) do
|
||||||
|
local t, test = v[1], v[2]
|
||||||
|
|
||||||
|
local ok, result = pcall(function()
|
||||||
|
for _, prop in ipairs(test) do
|
||||||
|
if value[prop] == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
-- Cannot throw if the property does not exist,
|
||||||
|
-- as userdatas may allow nil indexing
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end)
|
||||||
|
|
||||||
|
if ok and result then
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -5,6 +5,7 @@ done
|
||||||
|
|
||||||
echo "Processing libraries..."
|
echo "Processing libraries..."
|
||||||
darklua process -c dense.json5 ./corescripts/Libraries/Fusion/init.luau ./corescripts/processed/10000001.lua
|
darklua process -c dense.json5 ./corescripts/Libraries/Fusion/init.luau ./corescripts/processed/10000001.lua
|
||||||
|
darklua process -c bundle.json5 ./corescripts/Libraries/Red/init.luau ./corescripts/processed/10000002.lua
|
||||||
|
|
||||||
echo "Processing other corescripts..."
|
echo "Processing other corescripts..."
|
||||||
for file in ./corescripts/luau/[a-z]*.luau; do
|
for file in ./corescripts/luau/[a-z]*.luau; do
|
||||||
|
|
|
||||||
484
defs.d.lua
484
defs.d.lua
File diff suppressed because one or more lines are too long
1334
luau/10000002.luau
1334
luau/10000002.luau
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue