diff --git a/Warp.rbxm b/Warp.rbxm index 6c9c335..a33fd2e 100644 Binary files a/Warp.rbxm and b/Warp.rbxm differ diff --git a/docs/api/1.0/ratelimit.md b/docs/api/1.0/ratelimit.md index ab3a7a6..d3c6048 100644 --- a/docs/api/1.0/ratelimit.md +++ b/docs/api/1.0/ratelimit.md @@ -13,8 +13,10 @@ When creating a event on Server, you can add second argument (optional) as table -- Server -- Let's make the event have ratelimit with max 50 entrance for 2 seconds. local Remote = Warp.Server("Remote1", { - maxEntrance = 50, -- maximum 50 fires. - interval = 2, -- 2 seconds + rateLimit = { + maxEntrance = 50, -- maximum 50 fires. + interval = 2, -- 2 seconds + } }) -- Now the Event RateLimit is configured, and ready to use. -- No need anything to adds on client side. diff --git a/docs/api/1.0/server.md b/docs/api/1.0/server.md index 430ea1e..554e5e6 100644 --- a/docs/api/1.0/server.md +++ b/docs/api/1.0/server.md @@ -36,12 +36,16 @@ Create new Warp events with array. ```lua [Example] local Events = Warp.fromServerArray({ ["Remote1"] = { - maxEntrance: 50, - interval: 1, + rateLimit = { + maxEntrance: 50, + interval: 1, + } }, -- with rateLimit configuration "Remote2", -- without rateLimit configuration ["Remote3"] = { - maxEntrance: 10, + rateLimit = { + maxEntrance: 10, + } }, -- with rateLimit configuration }) diff --git a/docs/api/1.0/signal.md b/docs/api/1.0/signal.md index f283653..6985a9d 100644 --- a/docs/api/1.0/signal.md +++ b/docs/api/1.0/signal.md @@ -102,7 +102,23 @@ Signal1:DisconnectAll() ## `:Fire` -Fire the signal. +Fire the signal (Immediate) + +::: code-group +```lua [Variable] +( + ...: any +) +``` + +```lua [Example] +Signal1:Fire("Hello World!") +``` +::: + +## `:DeferFire` + +Fire the signal (Deferred) ::: code-group ```lua [Variable] @@ -122,7 +138,7 @@ This uses `pcall`, which means it never error (safe-mode, sacrificed debugging), ## `:FireTo` -Fire to other signal, this also use `:Fire`. +Fire to other signal, this uses `:Fire`. ::: code-group ```lua [Variable] diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 61304f4..925582f 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -8,7 +8,7 @@ ::: code-group ```toml [wally.toml] [dependencies] -warp = "imezx/warp@1.0.5" +warp = "imezx/warp@1.0.11" ``` 3. Run `wally install` in command. diff --git a/src/Index/Client/ClientProcess/init.luau b/src/Index/Client/ClientProcess/init.luau index a09f623..abc828b 100644 --- a/src/Index/Client/ClientProcess/init.luau +++ b/src/Index/Client/ClientProcess/init.luau @@ -19,6 +19,7 @@ local clientQueue: Type.QueueMap = {} local unreliableClientQueue: Type.QueueMap = {} local clientCallback: Type.CallbackMap = {} local clientRequestQueue: Type.QueueMap = {} +local registeredIdentifier: { string } = {} local queueIn: { [string]: {any} @@ -37,11 +38,6 @@ local queueOutRequest: { } } } = {} -local incoming_cache: { - [string]: { - any - } -} = {} local logger: { [string]: boolean } = {} @@ -86,7 +82,9 @@ function ClientProcess.insertRequest(Identifier: string, timeout: number, ...: a end function ClientProcess.add(Identifier: any, originId: string, conf: Type.ClientConf) - if not clientQueue[Identifier] then + if not table.find(registeredIdentifier, Identifier) then + table.insert(registeredIdentifier, Identifier) + if conf.logging then ClientProcess.logger(Identifier, conf.logging.store, conf.logging.opt) end @@ -149,28 +147,56 @@ end function ClientProcess.start() debug.setmemorycategory("Warp") + local clock_limit = 1/60 + local past_clock = os.clock() + RunService.PostSimulation:Connect(function() - for Identifier: string, data: any in unreliableClientQueue do - if #data == 0 then continue end - if clientRatelimit[Identifier](#data) then - UnreliableEvent:FireServer(Buffer.revert(Identifier), data) - if logger[Identifier] then - task.defer(Logger.write, Identifier, `state: out -> unreliable -> {#data} data.`) - end - end - unreliableClientQueue[Identifier] = nil - end - for Identifier: string, data: any in clientQueue do - local callback = clientCallback[Identifier] or nil - if #data > 0 then + if (os.clock()-past_clock) >= (clock_limit - 0.006) then -- less potential to skip frames + past_clock = os.clock() + -- Unreliable + for Identifier: string, data: any in unreliableClientQueue do + if #data == 0 then continue end if clientRatelimit[Identifier](#data) then - ReliableEvent:FireServer(Buffer.revert(Identifier), data) + UnreliableEvent:FireServer(Buffer.revert(Identifier), data) if logger[Identifier] then - task.defer(Logger.write, Identifier, `state: out -> reliable -> {#data} data.`) + task.defer(Logger.write, Identifier, `state: out -> unreliable -> {#data} data.`) end end - clientQueue[Identifier] = nil + unreliableClientQueue[Identifier] = nil end + -- Reliable + for Identifier: string, data: any in clientQueue do + if #data > 0 then + if clientRatelimit[Identifier](#data) then + ReliableEvent:FireServer(Buffer.revert(Identifier), data) + if logger[Identifier] then + task.defer(Logger.write, Identifier, `state: out -> reliable -> {#data} data.`) + end + end + clientQueue[Identifier] = nil + end + end + -- Sent new invokes + for Identifier: string, requestsData in queueOutRequest[1] do + if #requestsData == 0 then continue end + RequestEvent:FireServer(Buffer.revert(Identifier), "\1", requestsData) + if logger[Identifier] then + task.defer(Logger.write, Identifier, `state: out -> request -> {#requestsData} data.`) + end + queueOutRequest[1][Identifier] = nil + end + -- Sent returning invokes + for Identifier: string, toReturnDatas in queueOutRequest[2] do + if #toReturnDatas == 0 then continue end + RequestEvent:FireServer(Buffer.revert(Identifier), "\0", toReturnDatas) + if logger[Identifier] then + task.defer(Logger.write, Identifier, `state: out -> return request -> {#toReturnDatas} data.`) + end + queueOutRequest[2][Identifier] = nil + end + end + + for _, Identifier: string in registeredIdentifier do if clientRequestQueue[Identifier] then for _, requestData in clientRequestQueue[Identifier] do if not requestData[3] then continue end @@ -179,87 +205,69 @@ function ClientProcess.start() end table.insert(queueOutRequest[1][Identifier], { requestData[1], requestData[3] }) end - clientRequestQueue[Identifier] = nil end - if callback then - if incoming_cache[Identifier] then - for _, packet in incoming_cache[Identifier] do - if #packet == 0 then continue end - for _, fn: any in callback do - for i=1,#packet do - Spawn(fn, table.unpack(packet[i] or {})) - end + + -- Unreliable & Reliable + local callback = clientCallback[Identifier] or nil + if not callback then continue end + + if queueIn[Identifier] then + for _, packedDatas: any in queueIn[Identifier] do + if #packedDatas == 0 then continue end + for _, fn: any in callback do + for i=1,#packedDatas do + Spawn(fn, table.unpack(packedDatas[i] or {})) end end - incoming_cache[Identifier] = nil end - if queueIn[Identifier] then - for _, packedDatas: any in queueIn[Identifier] do - if #packedDatas == 0 then continue end - for _, fn: any in callback do - for i=1,#packedDatas do - Spawn(fn, table.unpack(packedDatas[i] or {})) - end + queueIn[Identifier] = nil + end + + -- Return Invoke + if queueInRequest[1][Identifier] then + for _, packetDatas: any in queueInRequest[1][Identifier] do + if #packetDatas == 0 then continue end + for _, fn: any in callback do + for i=1,#packetDatas do + if not packetDatas[i] then continue end + local packetData1 = packetDatas[i][1] + local packetData2 = packetDatas[i][2] + Spawn(function() + local requestReturn = { fn(table.unpack(packetData1)) } + if not queueOutRequest[2][Identifier] then + queueOutRequest[2][Identifier] = {} + end + table.insert(queueOutRequest[2][Identifier], { packetData2, requestReturn }) + packetData1 = nil + packetData2 = nil + end) end end - queueIn[Identifier] = nil end - if queueInRequest[1][Identifier] then - for _, packetDatas: any in queueInRequest[1][Identifier] do - if #packetDatas == 0 then continue end - for _, fn: any in callback do - for i=1,#packetDatas do - local packetData = packetDatas[i] - if not packetData then continue end - Spawn(function() - local requestReturn = { fn(table.unpack(packetData[2])) } - if not queueOutRequest[2][Identifier] then - queueOutRequest[2][Identifier] = {} - end - table.insert(queueOutRequest[2][Identifier], { packetData[1], requestReturn }) - end) - end - end - end - queueInRequest[1][Identifier] = nil - end - if queueInRequest[2][Identifier] then - if clientRequestQueue[Identifier] then - for _, packetDatas: any in queueInRequest[2][Identifier] do - for _, packetData in packetDatas do - if #packetData == 1 then continue end - for y=1,#clientRequestQueue[Identifier] do - local clientRequest = clientRequestQueue[Identifier][y] - if not clientRequest then continue end - if clientRequest[1] == packetData[1] then - Spawn(clientRequest[2], table.unpack(packetData[2])) - table.remove(clientRequestQueue[Identifier], y) - break - end + queueInRequest[1][Identifier] = nil + end + + -- Call to Invoke + if queueInRequest[2][Identifier] then + if clientRequestQueue[Identifier] then + for _, packetDatas: any in queueInRequest[2][Identifier] do + for _, packetData in packetDatas do + if #packetData == 1 then continue end + for y=1,#clientRequestQueue[Identifier] do + local clientRequest = clientRequestQueue[Identifier][y] + if not clientRequest then continue end + if clientRequest[1] == packetData[1] then + Spawn(clientRequest[2], table.unpack(packetData[2])) + table.remove(clientRequestQueue[Identifier], y) + break end end end end - queueInRequest[2][Identifier] = nil end + queueInRequest[2][Identifier] = nil end end - for Identifier: string, requestsData in queueOutRequest[1] do - if #requestsData == 0 then continue end - RequestEvent:FireServer(Buffer.revert(Identifier), "\1", requestsData) - if logger[Identifier] then - task.defer(Logger.write, Identifier, `state: out -> request -> {#requestsData} data.`) - end - queueOutRequest[1][Identifier] = nil - end - for Identifier: string, toReturnDatas in queueOutRequest[2] do - if #toReturnDatas == 0 then continue end - RequestEvent:FireServer(Buffer.revert(Identifier), "\0", toReturnDatas) - if logger[Identifier] then - task.defer(Logger.write, Identifier, `state: out -> return request -> {#toReturnDatas} data.`) - end - queueOutRequest[2][Identifier] = nil - end end) local function onClientNetworkReceive(Identifier: any, data: any) if not Identifier or not data then return end @@ -267,16 +275,6 @@ function ClientProcess.start() if not queueIn[Identifier] then queueIn[Identifier] = {} end - if not clientCallback[Identifier] then - if not incoming_cache[Identifier] then - incoming_cache[Identifier] = {} - end - table.insert(incoming_cache[Identifier], data) - if logger[Identifier] then - task.defer(Logger.write, Identifier, `state: cache -> net -> {#data} data.`) - end - return - end table.insert(queueIn[Identifier], data) if logger[Identifier] then task.defer(Logger.write, Identifier, `state: in -> net -> {#data} data.`) diff --git a/src/Index/Server/ServerProcess/init.luau b/src/Index/Server/ServerProcess/init.luau index 20f0481..b1940d8 100644 --- a/src/Index/Server/ServerProcess/init.luau +++ b/src/Index/Server/ServerProcess/init.luau @@ -72,12 +72,21 @@ local function initializeEachPlayer(player: Player) if not queueOut[player][Identifier] then queueOut[player][Identifier] = {} end + if not serverRequestQueue[Identifier] then + serverRequestQueue[Identifier] = {} + end if not serverRequestQueue[Identifier][player] then serverRequestQueue[Identifier][player] = {} end if not queueIn[Identifier][player] then queueIn[Identifier][player] = {} end + if not queueOutRequest[1][Identifier] then + queueOutRequest[1][Identifier] = {} + end + if not queueOutRequest[2][Identifier] then + queueOutRequest[2][Identifier] = {} + end if not queueInRequest[1][Identifier][player] then queueInRequest[1][Identifier][player] = {} queueInRequest[2][Identifier][player] = {} @@ -93,12 +102,18 @@ Players.PlayerAdded:Connect(initializeEachPlayer) function ServerProcess.insertQueue(Identifier: string, reliable: boolean, player: Player, ...: any) if not reliable then + if not unreliableServerQueue[Identifier] then + unreliableServerQueue[Identifier] = {} + end if not unreliableServerQueue[Identifier][player] then unreliableServerQueue[Identifier][player] = {} end table.insert(unreliableServerQueue[Identifier][player], { ... }) return end + if not serverQueue[Identifier] then + serverQueue[Identifier] = {} + end if not serverQueue[Identifier][player] then serverQueue[Identifier][player] = {} end @@ -106,12 +121,12 @@ function ServerProcess.insertQueue(Identifier: string, reliable: boolean, player end function ServerProcess.insertRequest(Identifier: string, timeout: number, player: Player, ...: any) - if not serverQueue[Identifier][player] then - serverQueue[Identifier][player] = {} - end if not serverRequestQueue[Identifier] then serverRequestQueue[Identifier] = {} end + if not serverRequestQueue[Identifier][player] then + serverRequestQueue[Identifier][player] = {} + end local yieldThread: thread, start = coroutine.running(), os.clock() local cancel = task.delay(timeout, function() task.spawn(yieldThread, nil) @@ -127,7 +142,9 @@ end function ServerProcess.add(Identifier: string, originId: string, conf: Type.ServerConf) if not table.find(registeredIdentifier, Identifier) then table.insert(registeredIdentifier, Identifier) + RateLimit.create(originId, conf.rateLimit and conf.rateLimit.maxEntrance or 200, conf.rateLimit and conf.rateLimit.interval or 2) + if conf.logging then ServerProcess.logger(Identifier, conf.logging.store, conf.logging.opt) end @@ -191,116 +208,146 @@ end function ServerProcess.start() debug.setmemorycategory("Warp") + local clock_limit = 1/60 + local past_clock = os.clock() + RunService.PostSimulation:Connect(function() - for Identifier: string, players in unreliableServerQueue do - for player: Player, content: any in players do - if #content == 0 then continue end - UnreliableEvent:FireClient(player, Buffer.revert(Identifier), content) - if logger[Identifier] then - task.defer(Logger.write, Identifier, `state: out -> unreliable -> {#content} data.`) + if (os.clock()-past_clock) >= (clock_limit - 0.006) then -- less potential to skip frames + past_clock = os.clock() + -- Unreliable + for Identifier: string, players in unreliableServerQueue do + for player: Player, content: any in players do + if #content == 0 then continue end + UnreliableEvent:FireClient(player, Buffer.revert(Identifier), content) + if logger[Identifier] then + task.defer(Logger.write, Identifier, `state: out -> unreliable -> {#content} data.`) + end + unreliableServerQueue[Identifier][player] = nil end - unreliableServerQueue[Identifier][player] = nil + unreliableServerQueue[Identifier] = nil + end + -- Reliable + for Identifier: string, contents: { [Player]: { any } } in serverQueue do + for player, content: any in contents do + if #content > 0 and queueOut[player] then + ReliableEvent:FireClient(player, Buffer.revert(Identifier), content) + end + serverQueue[Identifier][player] = nil + end + serverQueue[Identifier] = nil + end + -- Sent new invokes + for Identifier: string, contents in queueOutRequest[1] do + for player: Player, requestsData: any in contents do + if #requestsData > 0 then + RequestEvent:FireClient(player, Buffer.revert(Identifier), "\1", requestsData) + if logger[Identifier] then + task.defer(Logger.write, Identifier, `state: out -> request -> {#requestsData} data.`) + end + end + queueOutRequest[1][Identifier][player] = nil + end + queueOutRequest[1][Identifier] = nil + end + -- Sent returning invokes + for Identifier: string, contents in queueOutRequest[2] do + for player: Player, toReturnDatas: any in contents do + if #toReturnDatas > 0 then + RequestEvent:FireClient(player, Buffer.revert(Identifier), "\0", toReturnDatas) + if logger[Identifier] then + task.defer(Logger.write, Identifier, `state: out -> return request -> {#toReturnDatas} data.`) + end + end + queueOutRequest[2][Identifier][player] = nil + end + queueOutRequest[2][Identifier] = nil end end - - for Identifier: string, contents: { [Player]: { any } } in serverQueue do - - for player: Player, requestsData: any in queueOutRequest[1][Identifier] do - if #requestsData > 0 then - RequestEvent:FireClient(player, Buffer.revert(Identifier), "\1", requestsData) - if logger[Identifier] then - task.defer(Logger.write, Identifier, `state: out -> request -> {#requestsData} data.`) - end - end - queueOutRequest[1][Identifier][player] = nil - end - - for player: Player, toReturnDatas: any in queueOutRequest[2][Identifier] do - if #toReturnDatas > 0 then - RequestEvent:FireClient(player, Buffer.revert(Identifier), "\0", toReturnDatas) - if logger[Identifier] then - task.defer(Logger.write, Identifier, `state: out -> return request -> {#toReturnDatas} data.`) - end - end - queueOutRequest[2][Identifier][player] = nil - end - - local callback = serverCallback[Identifier] or nil - for player, content: any in contents do - if #content > 0 and queueOut[player] then - ReliableEvent:FireClient(player, Buffer.revert(Identifier), content) - end - serverQueue[Identifier][player] = nil - - if serverRequestQueue[Identifier][player] then - for _, requestData in serverRequestQueue[Identifier][player] do + + for _, Identifier: string in registeredIdentifier do + if serverRequestQueue[Identifier] then + for player, content in serverRequestQueue[Identifier] do + for _, requestData in content do if not requestData[3] then continue end + if not queueOutRequest[1][Identifier] then + queueOutRequest[1][Identifier] = {} + end if not queueOutRequest[1][Identifier][player] then queueOutRequest[1][Identifier][player] = {} end table.insert(queueOutRequest[1][Identifier][player], { requestData[1], requestData[3] }) end - serverRequestQueue[Identifier][player] = nil end - - if callback then - local requestIn1: any = queueInRequest[1][Identifier][player] - local requestIn2: any = queueInRequest[2][Identifier][player] - local incoming: any = queueIn[Identifier][player] - - if incoming then - for _, packedDatas: any in incoming do - if #packedDatas == 0 then continue end - for _, fn: any in callback do - for i=1,#packedDatas do - Spawn(fn, player, table.unpack(packedDatas[i] or {})) - end - end + end + + local callback = serverCallback[Identifier] or nil + if not callback then continue end + + -- Unreliable & Reliable + for player, content in queueIn[Identifier] do + if not callback then break end + for _, incoming in content do + if not callback then break end + if #incoming == 0 then continue end + for _, fn: any in callback do + for i=1,#incoming do + Spawn(fn, player, table.unpack(incoming[i] or {})) end - incoming = nil - queueIn[Identifier][player] = nil - end - if requestIn1 then - for _, packetDatas: any in requestIn1 do - if #packetDatas == 0 then continue end - for _, fn: any in callback do - for i=1,#packetDatas do - local packetData = packetDatas[i] - if not packetData then continue end - Spawn(function() - local requestReturn = { fn(player, table.unpack(packetData[2])) } - local state = queueOutRequest[2][Identifier][player] - if not queueOutRequest[2][Identifier][player] then - queueOutRequest[2][Identifier][player] = {} - end - table.insert(queueOutRequest[2][Identifier][player], { packetData[1], requestReturn }) - end) - end - end - end - requestIn1 = nil - queueInRequest[1][Identifier][player] = nil - end - if requestIn2 then - for _, packetDatas: any in requestIn2 do - for _, packetData in packetDatas do - if #packetData == 1 then continue end - local data = serverRequestQueue[Identifier][player] - for i=1,#data do - local serverRequest = data[i] - if not serverRequest then continue end - if serverRequest[1] == packetData[1] then - Spawn(serverRequest[2], table.unpack(packetData[2])) - table.remove(data, i) - break - end - end - end - end - requestIn2 = nil - queueInRequest[2][Identifier][player] = nil end end + queueIn[Identifier][player] = nil + end + + -- Return Invoke + for player, content in queueInRequest[1][Identifier] do + if not callback then break end + for _, packetDatas in content do + if not callback then break end + if #packetDatas == 0 then continue end + for _, fn: any in callback do + for i=1,#packetDatas do + if not packetDatas[i] then continue end + local packetData1 = packetDatas[i][1] + local packetData2 = packetDatas[i][2] + Spawn(function() + local requestReturn = { fn(player, table.unpack(packetData2)) } + if not queueOutRequest[2][Identifier] then + queueOutRequest[2][Identifier] = {} + end + if not queueOutRequest[2][Identifier][player] then + queueOutRequest[2][Identifier][player] = {} + end + table.insert(queueOutRequest[2][Identifier][player], { packetData1, requestReturn }) + packetData1 = nil + packetData2 = nil + end) + end + end + end + queueInRequest[1][Identifier][player] = nil + end + + -- Call to Invoke + for player, content in queueInRequest[2][Identifier] do + if not callback then break end + for _, packetDatas in content do + for _, packetData in packetDatas do + if not callback then break end + if #packetData == 1 then continue end + local data = serverRequestQueue[Identifier][player] + for i=1,#data do + local serverRequest = data[i] + if not serverRequest then continue end + if serverRequest[1] == packetData[1] then + Spawn(serverRequest[2], table.unpack(packetData[2])) + table.remove(data, i) + break + end + end + end + end + queueInRequest[2][Identifier][player] = nil + serverRequestQueue[Identifier] = nil end end end) @@ -330,6 +377,9 @@ function ServerProcess.start() queueInRequest[1][Identifier][player] = {} queueInRequest[2][Identifier][player] = {} end + if not serverQueue[Identifier] then + serverQueue[Identifier] = {} + end if not serverQueue[Identifier][player] then serverQueue[Identifier][player] = {} end diff --git a/src/Index/Signal/init.luau b/src/Index/Signal/init.luau index 567ab01..f7fe2a3 100644 --- a/src/Index/Signal/init.luau +++ b/src/Index/Signal/init.luau @@ -53,6 +53,12 @@ function Signal:Wait(): number return coroutine.yield() end +function Signal:DeferFire(...: any): () + for _, handle in self do + task.defer(handle.fn, ...) + end +end + function Signal:Fire(...: any): () for _, handle in self do task.spawn(handle.fn, ...) diff --git a/src/init.luau b/src/init.luau index 7b183f1..ccc8644 100644 --- a/src/init.luau +++ b/src/init.luau @@ -1,5 +1,5 @@ -- Warp Library (@Eternity_Devs) --- version 1.0.10 +-- version 1.0.11 --!strict --!native --!optimize 2 diff --git a/wally.toml b/wally.toml index 193ac0c..71d26fb 100644 --- a/wally.toml +++ b/wally.toml @@ -1,6 +1,6 @@ [package] name = "imezx/warp" -version = "1.0.10" +version = "1.0.11" registry = "https://github.com/UpliftGames/wally-index" realm = "shared" license = "MIT"