mirror of
https://github.com/imezx/Warp.git
synced 2025-04-24 23:20:02 +00:00
Compare commits
21 commits
Author | SHA1 | Date | |
---|---|---|---|
|
0b1304f4c5 | ||
|
d8526c7e25 | ||
|
d065bc2e50 | ||
|
925df47d4c | ||
|
f3b674b377 | ||
|
677d3fa675 | ||
|
91862a65fe | ||
|
8ba9540550 | ||
|
1d67954ef9 | ||
|
43c4a1594f | ||
|
22996c9357 | ||
|
a377788f22 | ||
|
3354324c5b | ||
|
20b97eeb54 | ||
|
dbed984eea | ||
|
064075fbd9 | ||
|
77de85b6b8 | ||
|
10de54608a | ||
|
5b2e36b7bb | ||
|
eba9f79655 | ||
|
7309840005 |
26 changed files with 574 additions and 421 deletions
10
.github/workflows/deploy.yml
vendored
10
.github/workflows/deploy.yml
vendored
|
@ -18,16 +18,16 @@ jobs:
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: npm
|
cache: npm
|
||||||
- name: Setup Pages
|
- name: Setup Pages
|
||||||
uses: actions/configure-pages@v3
|
uses: actions/configure-pages@v4
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Build
|
- name: Build
|
||||||
|
@ -35,9 +35,9 @@ jobs:
|
||||||
npm run docs:build
|
npm run docs:build
|
||||||
touch docs/.vitepress/dist
|
touch docs/.vitepress/dist
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-pages-artifact@v2
|
uses: actions/upload-pages-artifact@v3
|
||||||
with:
|
with:
|
||||||
path: docs/.vitepress/dist
|
path: docs/.vitepress/dist
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v2
|
uses: actions/deploy-pages@v4
|
||||||
|
|
BIN
Warp.rbxm
BIN
Warp.rbxm
Binary file not shown.
|
@ -39,7 +39,6 @@ function side() {
|
||||||
text: 'Utilities',
|
text: 'Utilities',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'Signal', link: '/api/1.0/signal' },
|
{ text: 'Signal', link: '/api/1.0/signal' },
|
||||||
{ text: 'Buffer', link: '/api/1.0/buffer' },
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Buffer <Badge type="tip" text="utilities" />
|
|
||||||
|
|
||||||
A Additional buffer.
|
|
|
@ -68,3 +68,4 @@ Pong:Destroy()
|
||||||
|
|
||||||
-- Yay Done!
|
-- Yay Done!
|
||||||
```
|
```
|
||||||
|
:::
|
|
@ -8,7 +8,7 @@
|
||||||
::: code-group
|
::: code-group
|
||||||
```toml [wally.toml]
|
```toml [wally.toml]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
warp = "imezx/warp@1.0.11"
|
warp = "imezx/warp@1.0.13"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Run `wally install` in command.
|
3. Run `wally install` in command.
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
--!strict
|
|
||||||
--!native
|
|
||||||
--!optimize 2
|
|
||||||
local Logger = {}
|
|
||||||
local Logs: {
|
|
||||||
[string]: {
|
|
||||||
[string]: string
|
|
||||||
}
|
|
||||||
} = {}
|
|
||||||
local logging: {
|
|
||||||
[string]: boolean
|
|
||||||
} = {}
|
|
||||||
|
|
||||||
local now = tick()
|
|
||||||
|
|
||||||
function Logger.write(Identifier: string, text: string, log: boolean?)
|
|
||||||
if not Logs[Identifier] then
|
|
||||||
Logs[Identifier] = {}
|
|
||||||
end
|
|
||||||
if log ~= nil then
|
|
||||||
logging[Identifier] = log
|
|
||||||
end
|
|
||||||
now = tick()
|
|
||||||
Logs[Identifier][tostring(now)] = text
|
|
||||||
if logging[Identifier] then
|
|
||||||
print(`[{now}] ->`, text)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Logger.read(Identifier: string)
|
|
||||||
return Logs[Identifier]
|
|
||||||
end
|
|
||||||
|
|
||||||
return Logger
|
|
|
@ -12,18 +12,14 @@ local Spawn = require(Util.Spawn)
|
||||||
local Key = require(Util.Key)
|
local Key = require(Util.Key)
|
||||||
local RateLimit = require(Util.RateLimit)
|
local RateLimit = require(Util.RateLimit)
|
||||||
local Buffer = require(Util.Buffer)
|
local Buffer = require(Util.Buffer)
|
||||||
local Logger = require(script.Logger)
|
|
||||||
|
|
||||||
local clientRatelimit: Type.StoredRatelimit = {}
|
local clientRatelimit: Type.StoredRatelimit = {}
|
||||||
local clientQueue: Type.QueueMap = {}
|
local clientQueue: Type.QueueMap = {}
|
||||||
local unreliableClientQueue: Type.QueueMap = {}
|
local unreliableClientQueue: Type.QueueMap = {}
|
||||||
local clientCallback: Type.CallbackMap = {}
|
local clientCallback: Type.CallbackMap = {}
|
||||||
local clientRequestQueue: Type.QueueMap = {}
|
local clientRequestQueue: Type.QueueMap = {}
|
||||||
local registeredIdentifier: { string } = {}
|
local registeredIdentifier: { [string]: boolean } = {}
|
||||||
|
|
||||||
local queueIn: {
|
|
||||||
[string]: {any}
|
|
||||||
} = {}
|
|
||||||
local queueInRequest: {
|
local queueInRequest: {
|
||||||
[number]: {
|
[number]: {
|
||||||
[string]: {
|
[string]: {
|
||||||
|
@ -38,9 +34,6 @@ local queueOutRequest: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} = {}
|
} = {}
|
||||||
local logger: {
|
|
||||||
[string]: boolean
|
|
||||||
} = {}
|
|
||||||
|
|
||||||
queueInRequest[1] = {}
|
queueInRequest[1] = {}
|
||||||
queueInRequest[2] = {}
|
queueInRequest[2] = {}
|
||||||
|
@ -82,12 +75,9 @@ function ClientProcess.insertRequest(Identifier: string, timeout: number, ...: a
|
||||||
end
|
end
|
||||||
|
|
||||||
function ClientProcess.add(Identifier: any, originId: string, conf: Type.ClientConf)
|
function ClientProcess.add(Identifier: any, originId: string, conf: Type.ClientConf)
|
||||||
if not table.find(registeredIdentifier, Identifier) then
|
if not registeredIdentifier[Identifier] then
|
||||||
table.insert(registeredIdentifier, Identifier)
|
registeredIdentifier[Identifier] = true
|
||||||
|
|
||||||
if conf.logging then
|
|
||||||
ClientProcess.logger(Identifier, conf.logging.store, conf.logging.opt)
|
|
||||||
end
|
|
||||||
if not clientRatelimit[Identifier] then
|
if not clientRatelimit[Identifier] then
|
||||||
clientRatelimit[Identifier] = RateLimit.create(originId)
|
clientRatelimit[Identifier] = RateLimit.create(originId)
|
||||||
end
|
end
|
||||||
|
@ -116,50 +106,40 @@ function ClientProcess.add(Identifier: any, originId: string, conf: Type.ClientC
|
||||||
if not queueInRequest[2][Identifier] then
|
if not queueInRequest[2][Identifier] then
|
||||||
queueInRequest[2][Identifier] = {}
|
queueInRequest[2][Identifier] = {}
|
||||||
end
|
end
|
||||||
if not queueIn[Identifier] then
|
|
||||||
queueIn[Identifier] = {}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ClientProcess.logger(Identifier: string, store: boolean, log: boolean)
|
function ClientProcess.remove(Identifier: string)
|
||||||
logger[Identifier] = store
|
if not registeredIdentifier[Identifier] then return end
|
||||||
Logger.write(Identifier, `state: change -> {log == true and "enabled" or "disabled"} logger.`, log)
|
registeredIdentifier[Identifier] = nil
|
||||||
end
|
clientQueue[Identifier] = nil
|
||||||
|
unreliableClientQueue[Identifier] = nil
|
||||||
function ClientProcess.getlogs(Identifier: string)
|
clientRequestQueue[Identifier] = nil
|
||||||
return Logger.read(Identifier)
|
clientCallback[Identifier] = nil
|
||||||
|
clientRatelimit[Identifier] = nil
|
||||||
|
queueOutRequest[1][Identifier] = nil
|
||||||
|
queueOutRequest[2][Identifier] = nil
|
||||||
|
queueInRequest[1][Identifier] = nil
|
||||||
|
queueInRequest[2][Identifier] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function ClientProcess.addCallback(Identifier: string, key: string, callback)
|
function ClientProcess.addCallback(Identifier: string, key: string, callback)
|
||||||
clientCallback[Identifier][key] = callback
|
clientCallback[Identifier][key] = callback
|
||||||
if logger[Identifier] then
|
|
||||||
task.defer(Logger.write, Identifier, `state: change -> new callback added.`)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function ClientProcess.removeCallback(Identifier: string, key: string)
|
function ClientProcess.removeCallback(Identifier: string, key: string)
|
||||||
clientCallback[Identifier][key] = nil
|
clientCallback[Identifier][key] = nil
|
||||||
if logger[Identifier] then
|
|
||||||
task.defer(Logger.write, Identifier, `state: change -> removed a callback.`)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function ClientProcess.start()
|
function ClientProcess.start()
|
||||||
debug.setmemorycategory("Warp")
|
debug.setmemorycategory("Warp")
|
||||||
local clock_limit = 1/60
|
|
||||||
local past_clock = os.clock()
|
|
||||||
|
|
||||||
RunService.PostSimulation:Connect(function()
|
RunService.PostSimulation:Connect(function()
|
||||||
if (os.clock()-past_clock) >= (clock_limit - 0.006) then -- less potential to skip frames
|
|
||||||
past_clock = os.clock()
|
|
||||||
-- Unreliable
|
-- Unreliable
|
||||||
for Identifier: string, data: any in unreliableClientQueue do
|
for Identifier: string, data: any in unreliableClientQueue do
|
||||||
if #data == 0 then continue end
|
if #data == 0 then continue end
|
||||||
if clientRatelimit[Identifier](#data) then
|
if clientRatelimit[Identifier](#data) then
|
||||||
UnreliableEvent:FireServer(Buffer.revert(Identifier), data)
|
for _, unpacked in data do
|
||||||
if logger[Identifier] then
|
UnreliableEvent:FireServer(Buffer.revert(Identifier), Buffer.write(unpacked))
|
||||||
task.defer(Logger.write, Identifier, `state: out -> unreliable -> {#data} data.`)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
unreliableClientQueue[Identifier] = nil
|
unreliableClientQueue[Identifier] = nil
|
||||||
|
@ -168,9 +148,8 @@ function ClientProcess.start()
|
||||||
for Identifier: string, data: any in clientQueue do
|
for Identifier: string, data: any in clientQueue do
|
||||||
if #data > 0 then
|
if #data > 0 then
|
||||||
if clientRatelimit[Identifier](#data) then
|
if clientRatelimit[Identifier](#data) then
|
||||||
ReliableEvent:FireServer(Buffer.revert(Identifier), data)
|
for _, unpacked in data do
|
||||||
if logger[Identifier] then
|
ReliableEvent:FireServer(Buffer.revert(Identifier), Buffer.write(unpacked))
|
||||||
task.defer(Logger.write, Identifier, `state: out -> reliable -> {#data} data.`)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
clientQueue[Identifier] = nil
|
clientQueue[Identifier] = nil
|
||||||
|
@ -180,23 +159,16 @@ function ClientProcess.start()
|
||||||
for Identifier: string, requestsData in queueOutRequest[1] do
|
for Identifier: string, requestsData in queueOutRequest[1] do
|
||||||
if #requestsData == 0 then continue end
|
if #requestsData == 0 then continue end
|
||||||
RequestEvent:FireServer(Buffer.revert(Identifier), "\1", requestsData)
|
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
|
queueOutRequest[1][Identifier] = nil
|
||||||
end
|
end
|
||||||
-- Sent returning invokes
|
-- Sent returning invokes
|
||||||
for Identifier: string, toReturnDatas in queueOutRequest[2] do
|
for Identifier: string, toReturnDatas in queueOutRequest[2] do
|
||||||
if #toReturnDatas == 0 then continue end
|
if #toReturnDatas == 0 then continue end
|
||||||
RequestEvent:FireServer(Buffer.revert(Identifier), "\0", toReturnDatas)
|
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
|
queueOutRequest[2][Identifier] = nil
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
for _, Identifier: string in registeredIdentifier do
|
for Identifier: string in registeredIdentifier do
|
||||||
if clientRequestQueue[Identifier] then
|
if clientRequestQueue[Identifier] then
|
||||||
for _, requestData in clientRequestQueue[Identifier] do
|
for _, requestData in clientRequestQueue[Identifier] do
|
||||||
if not requestData[3] then continue end
|
if not requestData[3] then continue end
|
||||||
|
@ -204,6 +176,7 @@ function ClientProcess.start()
|
||||||
queueOutRequest[1][Identifier] = {}
|
queueOutRequest[1][Identifier] = {}
|
||||||
end
|
end
|
||||||
table.insert(queueOutRequest[1][Identifier], { requestData[1], requestData[3] })
|
table.insert(queueOutRequest[1][Identifier], { requestData[1], requestData[3] })
|
||||||
|
requestData[3] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -211,18 +184,6 @@ function ClientProcess.start()
|
||||||
local callback = clientCallback[Identifier] or nil
|
local callback = clientCallback[Identifier] or nil
|
||||||
if not callback then continue end
|
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
|
|
||||||
end
|
|
||||||
queueIn[Identifier] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Return Invoke
|
-- Return Invoke
|
||||||
if queueInRequest[1][Identifier] then
|
if queueInRequest[1][Identifier] then
|
||||||
for _, packetDatas: any in queueInRequest[1][Identifier] do
|
for _, packetDatas: any in queueInRequest[1][Identifier] do
|
||||||
|
@ -233,11 +194,11 @@ function ClientProcess.start()
|
||||||
local packetData1 = packetDatas[i][1]
|
local packetData1 = packetDatas[i][1]
|
||||||
local packetData2 = packetDatas[i][2]
|
local packetData2 = packetDatas[i][2]
|
||||||
Spawn(function()
|
Spawn(function()
|
||||||
local requestReturn = { fn(table.unpack(packetData1)) }
|
local requestReturn = { fn(table.unpack(packetData2)) }
|
||||||
if not queueOutRequest[2][Identifier] then
|
if not queueOutRequest[2][Identifier] then
|
||||||
queueOutRequest[2][Identifier] = {}
|
queueOutRequest[2][Identifier] = {}
|
||||||
end
|
end
|
||||||
table.insert(queueOutRequest[2][Identifier], { packetData2, requestReturn })
|
table.insert(queueOutRequest[2][Identifier], { packetData1, requestReturn })
|
||||||
packetData1 = nil
|
packetData1 = nil
|
||||||
packetData2 = nil
|
packetData2 = nil
|
||||||
end)
|
end)
|
||||||
|
@ -269,15 +230,16 @@ function ClientProcess.start()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
local function onClientNetworkReceive(Identifier: any, data: any)
|
local function onClientNetworkReceive(Identifier: buffer | string, data: buffer, ref: { any }?)
|
||||||
if not Identifier or not data then return end
|
if not Identifier or typeof(Identifier) ~= "buffer" or not data or typeof(data) ~= "buffer" then return end
|
||||||
Identifier = Buffer.convert(Identifier)
|
Identifier = Buffer.convert(Identifier)
|
||||||
if not queueIn[Identifier] then
|
if not registeredIdentifier[Identifier :: string] then return end
|
||||||
queueIn[Identifier] = {}
|
local read = Buffer.read(data, ref)
|
||||||
end
|
if not read then return end
|
||||||
table.insert(queueIn[Identifier], data)
|
local callback = clientCallback[Identifier :: string]
|
||||||
if logger[Identifier] then
|
if not callback then return end
|
||||||
task.defer(Logger.write, Identifier, `state: in -> net -> {#data} data.`)
|
for _, fn: any in callback do
|
||||||
|
Spawn(fn, table.unpack(read))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ReliableEvent.OnClientEvent:Connect(onClientNetworkReceive)
|
ReliableEvent.OnClientEvent:Connect(onClientNetworkReceive)
|
||||||
|
@ -296,9 +258,6 @@ function ClientProcess.start()
|
||||||
end
|
end
|
||||||
table.insert(queueInRequest[2][Identifier], data)
|
table.insert(queueInRequest[2][Identifier], data)
|
||||||
end
|
end
|
||||||
if logger[Identifier] then
|
|
||||||
task.defer(Logger.write, Identifier, `state: in -> request -> {#data} data.`)
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -18,23 +18,18 @@ function Client.new(Identifier: string, conf: Type.ClientConf?)
|
||||||
local self = setmetatable({}, Client)
|
local self = setmetatable({}, Client)
|
||||||
|
|
||||||
self._buffer = Buffer.new()
|
self._buffer = Buffer.new()
|
||||||
self._buffer:wu8(Serdes(Identifier, conf and conf.yieldWait))
|
self._buffer:wu8(Serdes.increment(Identifier, conf and conf.yieldWait))
|
||||||
self.id = Buffer.convert(self._buffer:build())
|
self.id = Buffer.convert(self._buffer:build())
|
||||||
self.fn = {}
|
self.fn = {}
|
||||||
self._conf = table.freeze(conf or {})
|
self._conf = table.freeze(conf or {})
|
||||||
self.IsConnected = false
|
self.IsConnected = false
|
||||||
|
|
||||||
ClientProcess.add(self.id, Identifier, conf or { yieldWait = 10, logging = { store = false, opt = false } })
|
ClientProcess.add(self.id, Identifier, conf or { yieldWait = 10 })
|
||||||
self._buffer:remove()
|
self._buffer:remove()
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function Client:logs()
|
|
||||||
Assert(self._conf.logging, "[Client]: Event is not configured with logging.")
|
|
||||||
return ClientProcess.getlogs(self.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Client:Fire(reliable: boolean,...: any)
|
function Client:Fire(reliable: boolean,...: any)
|
||||||
ClientProcess.insertQueue(self.id, reliable, ...)
|
ClientProcess.insertQueue(self.id, reliable, ...)
|
||||||
end
|
end
|
||||||
|
@ -55,7 +50,7 @@ function Client:Once(callback: (args: any) -> ()): string
|
||||||
local key = tostring(Key())
|
local key = tostring(Key())
|
||||||
table.insert(self.fn, key)
|
table.insert(self.fn, key)
|
||||||
self.IsConnected = #self.fn > 0
|
self.IsConnected = #self.fn > 0
|
||||||
ClientProcess.addCallback(self.id, key, function(...)
|
ClientProcess.addCallback(self.id, key, function(...: any?)
|
||||||
self:Disconnect(key)
|
self:Disconnect(key)
|
||||||
task.spawn(callback, ...)
|
task.spawn(callback, ...)
|
||||||
end)
|
end)
|
||||||
|
@ -76,16 +71,19 @@ function Client:DisconnectAll()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Client:Disconnect(key: string): boolean
|
function Client:Disconnect(key: string)
|
||||||
Assert(typeof(key) == "string", "Key must be a string type.")
|
Assert(typeof(key) == "string", "Key must be a string type.")
|
||||||
ClientProcess.removeCallback(self.id, key)
|
ClientProcess.removeCallback(self.id, key)
|
||||||
table.remove(self.fn, table.find(self.fn, key))
|
table.remove(self.fn, table.find(self.fn, key))
|
||||||
self.IsConnected = #self.fn > 0
|
self.IsConnected = #self.fn > 0
|
||||||
return table.find(self.fn, key) == nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Client:Destroy()
|
function Client:Destroy()
|
||||||
self:DisconnectAll()
|
self:DisconnectAll()
|
||||||
|
self._buffer:remove()
|
||||||
|
ClientProcess.remove(self.id)
|
||||||
|
Serdes.decrement()
|
||||||
|
table.clear(self)
|
||||||
setmetatable(self, nil)
|
setmetatable(self, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
--!strict
|
--!strict
|
||||||
--!native
|
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
local RunService = game:GetService("RunService")
|
local RunService = game:GetService("RunService")
|
||||||
local Type = require(script.Parent.Type)
|
local Type = require(script.Parent.Type)
|
||||||
|
|
|
@ -18,7 +18,7 @@ function Server.new(Identifier: string, conf: Type.ServerConf?)
|
||||||
local self = setmetatable({}, Server)
|
local self = setmetatable({}, Server)
|
||||||
|
|
||||||
self._buffer = Buffer.new()
|
self._buffer = Buffer.new()
|
||||||
self._buffer:wu8(Serdes(Identifier))
|
self._buffer:wu8(Serdes.increment(Identifier))
|
||||||
self.id = Buffer.convert(self._buffer:build())
|
self.id = Buffer.convert(self._buffer:build())
|
||||||
self.fn = {}
|
self.fn = {}
|
||||||
self._conf = table.freeze(conf or {})
|
self._conf = table.freeze(conf or {})
|
||||||
|
@ -30,11 +30,6 @@ function Server.new(Identifier: string, conf: Type.ServerConf?)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function Server:logs()
|
|
||||||
Assert(self._conf.logging, "[Server]: Event is not configured with logging.")
|
|
||||||
return ServerProcess.getlogs(self.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Server:Fire(reliable: boolean, player: Player, ...: any)
|
function Server:Fire(reliable: boolean, player: Player, ...: any)
|
||||||
ServerProcess.insertQueue(self.id, reliable, player, ...)
|
ServerProcess.insertQueue(self.id, reliable, player, ...)
|
||||||
end
|
end
|
||||||
|
@ -75,9 +70,9 @@ function Server:Once(callback: (plyer: Player, args: any) -> ()): string
|
||||||
local key = tostring(Key())
|
local key = tostring(Key())
|
||||||
table.insert(self.fn, key)
|
table.insert(self.fn, key)
|
||||||
self.IsConnected = #self.fn > 0
|
self.IsConnected = #self.fn > 0
|
||||||
ServerProcess.addCallback(self.id, key, function(...)
|
ServerProcess.addCallback(self.id, key, function(player: Player, ...: any?)
|
||||||
self:Disconnect(key)
|
self:Disconnect(key)
|
||||||
task.spawn(callback, ...)
|
task.spawn(callback, player, ...)
|
||||||
end)
|
end)
|
||||||
return key
|
return key
|
||||||
end
|
end
|
||||||
|
@ -106,6 +101,10 @@ end
|
||||||
|
|
||||||
function Server:Destroy()
|
function Server:Destroy()
|
||||||
self:DisconnectAll()
|
self:DisconnectAll()
|
||||||
|
self._buffer:remove()
|
||||||
|
ServerProcess.remove(self.id)
|
||||||
|
Serdes.decrement()
|
||||||
|
table.clear(self)
|
||||||
setmetatable(self, nil)
|
setmetatable(self, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
--!strict
|
|
||||||
--!native
|
|
||||||
--!optimize 2
|
|
||||||
local Logger = {}
|
|
||||||
local Logs: {
|
|
||||||
[string]: {
|
|
||||||
[string]: string
|
|
||||||
}
|
|
||||||
} = {}
|
|
||||||
local logging: {
|
|
||||||
[string]: boolean
|
|
||||||
} = {}
|
|
||||||
|
|
||||||
local now = tick()
|
|
||||||
|
|
||||||
function Logger.write(Identifier: string, text: string, log: boolean?)
|
|
||||||
if not Logs[Identifier] then
|
|
||||||
Logs[Identifier] = {}
|
|
||||||
end
|
|
||||||
if log ~= nil then
|
|
||||||
logging[Identifier] = log
|
|
||||||
end
|
|
||||||
now = tick()
|
|
||||||
Logs[Identifier][tostring(now)] = text
|
|
||||||
if logging[Identifier] then
|
|
||||||
print(`[{now}] ->`, text)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Logger.read(Identifier: string)
|
|
||||||
return Logs[Identifier]
|
|
||||||
end
|
|
||||||
|
|
||||||
return Logger
|
|
|
@ -13,24 +13,18 @@ local Spawn = require(Util.Spawn)
|
||||||
local Key = require(Util.Key)
|
local Key = require(Util.Key)
|
||||||
local RateLimit = require(Util.RateLimit)
|
local RateLimit = require(Util.RateLimit)
|
||||||
local Buffer = require(Util.Buffer)
|
local Buffer = require(Util.Buffer)
|
||||||
local Logger = require(script.Logger)
|
|
||||||
|
|
||||||
local serverQueue: Type.QueueMap = {}
|
local serverQueue: Type.QueueMap = {}
|
||||||
local unreliableServerQueue: Type.QueueMap = {}
|
local unreliableServerQueue: Type.QueueMap = {}
|
||||||
local serverCallback: Type.CallbackMap = {}
|
local serverCallback: Type.CallbackMap = {}
|
||||||
local serverRequestQueue: Type.QueueMap = {}
|
local serverRequestQueue: Type.QueueMap = {}
|
||||||
local registeredIdentifier: { string } = {}
|
local registeredIdentifier: { [string]: boolean } = {}
|
||||||
|
|
||||||
local queueOut: {
|
local queueOut: {
|
||||||
[Player]: {
|
[Player]: {
|
||||||
[string]: {any},
|
[string]: {any},
|
||||||
}
|
}
|
||||||
} = {}
|
} = {}
|
||||||
local queueIn: {
|
|
||||||
[string]: {
|
|
||||||
[Player]: {any},
|
|
||||||
}
|
|
||||||
} = {}
|
|
||||||
local queueInRequest: {
|
local queueInRequest: {
|
||||||
[number]: {
|
[number]: {
|
||||||
[string]: {
|
[string]: {
|
||||||
|
@ -45,12 +39,6 @@ local queueOutRequest: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} = {}
|
} = {}
|
||||||
local logger: {
|
|
||||||
[string]: boolean
|
|
||||||
} = {}
|
|
||||||
local players: {
|
|
||||||
Player
|
|
||||||
} = {}
|
|
||||||
|
|
||||||
queueInRequest[1] = {}
|
queueInRequest[1] = {}
|
||||||
queueInRequest[2] = {}
|
queueInRequest[2] = {}
|
||||||
|
@ -61,13 +49,14 @@ local ReliableEvent = Event.Reliable
|
||||||
local UnreliableEvent = Event.Unreliable
|
local UnreliableEvent = Event.Unreliable
|
||||||
local RequestEvent = Event.Request
|
local RequestEvent = Event.Request
|
||||||
|
|
||||||
|
RateLimit.Protect()
|
||||||
|
|
||||||
local function initializeEachPlayer(player: Player)
|
local function initializeEachPlayer(player: Player)
|
||||||
if not player then return end
|
if not player then return end
|
||||||
if not queueOut[player] then
|
if not queueOut[player] then
|
||||||
queueOut[player] = {}
|
queueOut[player] = {}
|
||||||
end
|
end
|
||||||
|
for Identifier: string in registeredIdentifier do
|
||||||
for _, Identifier: string in registeredIdentifier do
|
|
||||||
if not player then break end
|
if not player then break end
|
||||||
if not queueOut[player][Identifier] then
|
if not queueOut[player][Identifier] then
|
||||||
queueOut[player][Identifier] = {}
|
queueOut[player][Identifier] = {}
|
||||||
|
@ -78,9 +67,6 @@ local function initializeEachPlayer(player: Player)
|
||||||
if not serverRequestQueue[Identifier][player] then
|
if not serverRequestQueue[Identifier][player] then
|
||||||
serverRequestQueue[Identifier][player] = {}
|
serverRequestQueue[Identifier][player] = {}
|
||||||
end
|
end
|
||||||
if not queueIn[Identifier][player] then
|
|
||||||
queueIn[Identifier][player] = {}
|
|
||||||
end
|
|
||||||
if not queueOutRequest[1][Identifier] then
|
if not queueOutRequest[1][Identifier] then
|
||||||
queueOutRequest[1][Identifier] = {}
|
queueOutRequest[1][Identifier] = {}
|
||||||
end
|
end
|
||||||
|
@ -99,6 +85,29 @@ local function initializeEachPlayer(player: Player)
|
||||||
end
|
end
|
||||||
|
|
||||||
Players.PlayerAdded:Connect(initializeEachPlayer)
|
Players.PlayerAdded:Connect(initializeEachPlayer)
|
||||||
|
Players.PlayerRemoving:Connect(function(player: Player)
|
||||||
|
if not player then return end
|
||||||
|
if queueOut[player] then
|
||||||
|
queueOut[player] = nil
|
||||||
|
end
|
||||||
|
for _, map in { serverQueue, unreliableServerQueue, serverRequestQueue } do
|
||||||
|
for Identifier: string in map do
|
||||||
|
map[Identifier][player] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i=1,2 do
|
||||||
|
for Identifier: string in queueInRequest[i] do
|
||||||
|
if queueInRequest[i][Identifier][player] then
|
||||||
|
queueInRequest[i][Identifier][player] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for Identifier: string in queueOutRequest[i] do
|
||||||
|
if queueOutRequest[i][Identifier][player] then
|
||||||
|
queueOutRequest[i][Identifier][player] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
function ServerProcess.insertQueue(Identifier: string, reliable: boolean, player: Player, ...: any)
|
function ServerProcess.insertQueue(Identifier: string, reliable: boolean, player: Player, ...: any)
|
||||||
if not reliable then
|
if not reliable then
|
||||||
|
@ -140,14 +149,11 @@ function ServerProcess.insertRequest(Identifier: string, timeout: number, player
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerProcess.add(Identifier: string, originId: string, conf: Type.ServerConf)
|
function ServerProcess.add(Identifier: string, originId: string, conf: Type.ServerConf)
|
||||||
if not table.find(registeredIdentifier, Identifier) then
|
if not registeredIdentifier[Identifier] then
|
||||||
table.insert(registeredIdentifier, Identifier)
|
registeredIdentifier[Identifier] = true
|
||||||
|
|
||||||
RateLimit.create(originId, conf.rateLimit and conf.rateLimit.maxEntrance or 200, conf.rateLimit and conf.rateLimit.interval or 2)
|
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
|
|
||||||
if not serverQueue[Identifier] then
|
if not serverQueue[Identifier] then
|
||||||
serverQueue[Identifier] = {}
|
serverQueue[Identifier] = {}
|
||||||
end
|
end
|
||||||
|
@ -161,9 +167,6 @@ function ServerProcess.add(Identifier: string, originId: string, conf: Type.Serv
|
||||||
unreliableServerQueue[Identifier] = {}
|
unreliableServerQueue[Identifier] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
if not queueIn[Identifier] then
|
|
||||||
queueIn[Identifier] = {}
|
|
||||||
end
|
|
||||||
if not queueInRequest[1][Identifier] then
|
if not queueInRequest[1][Identifier] then
|
||||||
queueInRequest[1][Identifier] = {}
|
queueInRequest[1][Identifier] = {}
|
||||||
end
|
end
|
||||||
|
@ -183,44 +186,36 @@ function ServerProcess.add(Identifier: string, originId: string, conf: Type.Serv
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerProcess.logger(Identifier: string, store: boolean, log: boolean)
|
function ServerProcess.remove(Identifier: string)
|
||||||
logger[Identifier] = store
|
if not registeredIdentifier[Identifier] then return end
|
||||||
Logger.write(Identifier, `state: change -> {log == true and "enabled" or "disabled"} logger.`, log)
|
registeredIdentifier[Identifier] = nil
|
||||||
end
|
serverQueue[Identifier] = nil
|
||||||
|
serverRequestQueue[Identifier] = nil
|
||||||
function ServerProcess.getlogs(Identifier: string)
|
serverCallback[Identifier] = nil
|
||||||
return Logger.read(Identifier)
|
unreliableServerQueue[Identifier] = nil
|
||||||
|
queueInRequest[1][Identifier] = nil
|
||||||
|
queueInRequest[2][Identifier] = nil
|
||||||
|
queueOutRequest[1][Identifier] = nil
|
||||||
|
queueOutRequest[2][Identifier] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerProcess.addCallback(Identifier: string, key: string, callback)
|
function ServerProcess.addCallback(Identifier: string, key: string, callback)
|
||||||
serverCallback[Identifier][key] = callback
|
serverCallback[Identifier][key] = callback
|
||||||
if logger[Identifier] then
|
|
||||||
task.defer(Logger.write, Identifier, `state: change -> new callback added.`)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerProcess.removeCallback(Identifier: string, key: string)
|
function ServerProcess.removeCallback(Identifier: string, key: string)
|
||||||
serverCallback[Identifier][key] = nil
|
serverCallback[Identifier][key] = nil
|
||||||
if logger[Identifier] then
|
|
||||||
task.defer(Logger.write, Identifier, `state: change -> removed a callback.`)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServerProcess.start()
|
function ServerProcess.start()
|
||||||
debug.setmemorycategory("Warp")
|
debug.setmemorycategory("Warp")
|
||||||
local clock_limit = 1/60
|
|
||||||
local past_clock = os.clock()
|
|
||||||
|
|
||||||
RunService.PostSimulation:Connect(function()
|
RunService.PostSimulation:Connect(function()
|
||||||
if (os.clock()-past_clock) >= (clock_limit - 0.006) then -- less potential to skip frames
|
|
||||||
past_clock = os.clock()
|
|
||||||
-- Unreliable
|
-- Unreliable
|
||||||
for Identifier: string, players in unreliableServerQueue do
|
for Identifier: string, players in unreliableServerQueue do
|
||||||
for player: Player, content: any in players do
|
for player: Player, content: any in players do
|
||||||
if #content == 0 then continue end
|
if #content == 0 then continue end
|
||||||
UnreliableEvent:FireClient(player, Buffer.revert(Identifier), content)
|
for _, unpacked in content do
|
||||||
if logger[Identifier] then
|
UnreliableEvent:FireClient(player, Buffer.revert(Identifier), Buffer.write(unpacked))
|
||||||
task.defer(Logger.write, Identifier, `state: out -> unreliable -> {#content} data.`)
|
|
||||||
end
|
end
|
||||||
unreliableServerQueue[Identifier][player] = nil
|
unreliableServerQueue[Identifier][player] = nil
|
||||||
end
|
end
|
||||||
|
@ -230,7 +225,9 @@ function ServerProcess.start()
|
||||||
for Identifier: string, contents: { [Player]: { any } } in serverQueue do
|
for Identifier: string, contents: { [Player]: { any } } in serverQueue do
|
||||||
for player, content: any in contents do
|
for player, content: any in contents do
|
||||||
if #content > 0 and queueOut[player] then
|
if #content > 0 and queueOut[player] then
|
||||||
ReliableEvent:FireClient(player, Buffer.revert(Identifier), content)
|
for _, unpacked in content do
|
||||||
|
ReliableEvent:FireClient(player, Buffer.revert(Identifier), Buffer.write(unpacked))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
serverQueue[Identifier][player] = nil
|
serverQueue[Identifier][player] = nil
|
||||||
end
|
end
|
||||||
|
@ -241,9 +238,6 @@ function ServerProcess.start()
|
||||||
for player: Player, requestsData: any in contents do
|
for player: Player, requestsData: any in contents do
|
||||||
if #requestsData > 0 then
|
if #requestsData > 0 then
|
||||||
RequestEvent:FireClient(player, Buffer.revert(Identifier), "\1", requestsData)
|
RequestEvent:FireClient(player, Buffer.revert(Identifier), "\1", requestsData)
|
||||||
if logger[Identifier] then
|
|
||||||
task.defer(Logger.write, Identifier, `state: out -> request -> {#requestsData} data.`)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
queueOutRequest[1][Identifier][player] = nil
|
queueOutRequest[1][Identifier][player] = nil
|
||||||
end
|
end
|
||||||
|
@ -254,19 +248,16 @@ function ServerProcess.start()
|
||||||
for player: Player, toReturnDatas: any in contents do
|
for player: Player, toReturnDatas: any in contents do
|
||||||
if #toReturnDatas > 0 then
|
if #toReturnDatas > 0 then
|
||||||
RequestEvent:FireClient(player, Buffer.revert(Identifier), "\0", toReturnDatas)
|
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
|
end
|
||||||
queueOutRequest[2][Identifier][player] = nil
|
queueOutRequest[2][Identifier][player] = nil
|
||||||
end
|
end
|
||||||
queueOutRequest[2][Identifier] = nil
|
queueOutRequest[2][Identifier] = nil
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
for _, Identifier: string in registeredIdentifier do
|
for Identifier: string in registeredIdentifier do
|
||||||
if serverRequestQueue[Identifier] then
|
if serverRequestQueue[Identifier] then
|
||||||
for player, content in serverRequestQueue[Identifier] do
|
for player, content in serverRequestQueue[Identifier] do
|
||||||
|
if #content == 0 then serverRequestQueue[Identifier][player] = nil continue end
|
||||||
for _, requestData in content do
|
for _, requestData in content do
|
||||||
if not requestData[3] then continue end
|
if not requestData[3] then continue end
|
||||||
if not queueOutRequest[1][Identifier] then
|
if not queueOutRequest[1][Identifier] then
|
||||||
|
@ -276,6 +267,7 @@ function ServerProcess.start()
|
||||||
queueOutRequest[1][Identifier][player] = {}
|
queueOutRequest[1][Identifier][player] = {}
|
||||||
end
|
end
|
||||||
table.insert(queueOutRequest[1][Identifier][player], { requestData[1], requestData[3] })
|
table.insert(queueOutRequest[1][Identifier][player], { requestData[1], requestData[3] })
|
||||||
|
requestData[3] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -283,21 +275,6 @@ function ServerProcess.start()
|
||||||
local callback = serverCallback[Identifier] or nil
|
local callback = serverCallback[Identifier] or nil
|
||||||
if not callback then continue end
|
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
|
|
||||||
end
|
|
||||||
end
|
|
||||||
queueIn[Identifier][player] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Return Invoke
|
-- Return Invoke
|
||||||
for player, content in queueInRequest[1][Identifier] do
|
for player, content in queueInRequest[1][Identifier] do
|
||||||
if not callback then break end
|
if not callback then break end
|
||||||
|
@ -347,26 +324,20 @@ function ServerProcess.start()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
queueInRequest[2][Identifier][player] = nil
|
queueInRequest[2][Identifier][player] = nil
|
||||||
serverRequestQueue[Identifier] = nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
local function onServerNetworkReceive(player: Player, Identifier: any, data: any)
|
local function onServerNetworkReceive(player: Player, Identifier: buffer | string, data: buffer, ref: { any }?)
|
||||||
if not Identifier or not data then return end
|
if not Identifier or typeof(Identifier) ~= "buffer" or not data or typeof(data) ~= "buffer" then return end
|
||||||
Identifier = Buffer.convert(Identifier)
|
Identifier = Buffer.convert(Identifier :: buffer)
|
||||||
if not serverQueue[Identifier] then
|
if not registeredIdentifier[Identifier :: string] then return end
|
||||||
serverQueue[Identifier] = {}
|
local read = Buffer.read(data, ref)
|
||||||
|
if not read then return end
|
||||||
|
local callback = serverCallback[Identifier :: string]
|
||||||
|
if not callback then return end
|
||||||
|
for _, fn: any in callback do
|
||||||
|
Spawn(fn, player, table.unpack(read))
|
||||||
end
|
end
|
||||||
if not serverQueue[Identifier][player] then
|
|
||||||
serverQueue[Identifier][player] = {}
|
|
||||||
end
|
|
||||||
if not queueIn[Identifier][player] then
|
|
||||||
queueIn[Identifier][player] = {}
|
|
||||||
end
|
|
||||||
if logger[Identifier] then
|
|
||||||
task.defer(Logger.write, Identifier, `state: in -> net -> {#data} data.`)
|
|
||||||
end
|
|
||||||
table.insert(queueIn[Identifier][player], data)
|
|
||||||
end
|
end
|
||||||
ReliableEvent.OnServerEvent:Connect(onServerNetworkReceive)
|
ReliableEvent.OnServerEvent:Connect(onServerNetworkReceive)
|
||||||
UnreliableEvent.OnServerEvent:Connect(onServerNetworkReceive)
|
UnreliableEvent.OnServerEvent:Connect(onServerNetworkReceive)
|
||||||
|
@ -375,6 +346,8 @@ function ServerProcess.start()
|
||||||
Identifier = Buffer.convert(Identifier)
|
Identifier = Buffer.convert(Identifier)
|
||||||
if not queueInRequest[1][Identifier][player] then
|
if not queueInRequest[1][Identifier][player] then
|
||||||
queueInRequest[1][Identifier][player] = {}
|
queueInRequest[1][Identifier][player] = {}
|
||||||
|
end
|
||||||
|
if not queueInRequest[2][Identifier][player] then
|
||||||
queueInRequest[2][Identifier][player] = {}
|
queueInRequest[2][Identifier][player] = {}
|
||||||
end
|
end
|
||||||
if not serverQueue[Identifier] then
|
if not serverQueue[Identifier] then
|
||||||
|
@ -388,9 +361,6 @@ function ServerProcess.start()
|
||||||
else
|
else
|
||||||
table.insert(queueInRequest[2][Identifier][player], data)
|
table.insert(queueInRequest[2][Identifier][player], data)
|
||||||
end
|
end
|
||||||
if logger[Identifier] then
|
|
||||||
task.defer(Logger.write, Identifier, `state: in -> request -> {#data} data.`)
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ function Dedicated.new(signal: any, handler: (...any) -> ())
|
||||||
end
|
end
|
||||||
|
|
||||||
function Dedicated:Disconnect()
|
function Dedicated:Disconnect()
|
||||||
|
table.clear(self)
|
||||||
setmetatable(self, nil)
|
setmetatable(self, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -22,15 +22,15 @@ function Signal.new(Identifier: string)
|
||||||
return Signals[Identifier]
|
return Signals[Identifier]
|
||||||
end
|
end
|
||||||
|
|
||||||
function Signal:Connect(fn: (...any) -> ()): string
|
function Signal:Connect(fn: (...any) -> (), optKey: string?): string
|
||||||
local key = tostring(Key())
|
local key: typeof(Signal) = optKey or tostring(Key()) :: any
|
||||||
self[key] = DedicatedSignal(self, fn)
|
self[key] = DedicatedSignal(self, fn)
|
||||||
return key
|
return key :: any
|
||||||
end
|
end
|
||||||
|
|
||||||
function Signal:Once(fn: (...any) -> ()): string
|
function Signal:Once(fn: (...any) -> ()): string
|
||||||
local key: string
|
local key: string
|
||||||
key = self:Connect(function(...)
|
key = self:Connect(function(...: any)
|
||||||
self:Disconnect(key)
|
self:Disconnect(key)
|
||||||
task.spawn(fn, ...)
|
task.spawn(fn, ...)
|
||||||
end)
|
end)
|
||||||
|
@ -38,7 +38,9 @@ function Signal:Once(fn: (...any) -> ()): string
|
||||||
end
|
end
|
||||||
|
|
||||||
function Signal:Disconnect(key: string)
|
function Signal:Disconnect(key: string)
|
||||||
|
if not self[key] then return end
|
||||||
self[key]:Disconnect()
|
self[key]:Disconnect()
|
||||||
|
self[key] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function Signal:DisconnectAll(): ()
|
function Signal:DisconnectAll(): ()
|
||||||
|
@ -46,7 +48,7 @@ function Signal:DisconnectAll(): ()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Signal:Wait(): number
|
function Signal:Wait(): number
|
||||||
local thread, t = coroutine.running(), os.clock()
|
local t, thread = os.clock(), coroutine.running()
|
||||||
self:Once(function()
|
self:Once(function()
|
||||||
task.spawn(thread, os.clock()-t)
|
task.spawn(thread, os.clock()-t)
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -4,44 +4,41 @@ type rateLimitArg = {
|
||||||
interval: number?,
|
interval: number?,
|
||||||
}
|
}
|
||||||
|
|
||||||
type logging = {
|
|
||||||
store: boolean,
|
|
||||||
opt: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ServerConf = {
|
export type ServerConf = {
|
||||||
rateLimit: rateLimitArg?,
|
rateLimit: rateLimitArg?,
|
||||||
logging: logging?,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ClientConf = {
|
export type ClientConf = {
|
||||||
yieldWait: number?,
|
yieldWait: number?,
|
||||||
logging: logging?,
|
}
|
||||||
|
|
||||||
|
export type Middleware = {
|
||||||
|
middleware: (self: Middleware, middleware: (...any) -> (...any)) -> (),
|
||||||
|
key: (self: Middleware) -> string,
|
||||||
|
destroy: (self: Middleware) -> (),
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Client = {
|
export type Client = {
|
||||||
Fire: (self: Client, reliable: boolean, ...any) -> (),
|
Fire: (self: Client, reliable: boolean, ...any) -> (),
|
||||||
Invoke: (self: Client, timeout: number, ...any) -> any,
|
Invoke: (self: Client, timeout: number, ...any) -> any,
|
||||||
Connect: (self: Client, callback: (...any) -> ()) -> string,
|
Connect: (self: Client, callback: (...any) -> ()) -> Middleware,
|
||||||
Once: (self: Client, callback: (player: Player, ...any) -> ()) -> string,
|
Once: (self: Client, callback: (player: Player, ...any) -> ()) -> Middleware,
|
||||||
Disconnect: (self: Client, key: string) -> (),
|
Disconnect: (self: Client, key: string) -> (),
|
||||||
DisconnectAll: (self: Client) -> (),
|
DisconnectAll: (self: Client) -> (),
|
||||||
Wait: (self: Client) -> number,
|
Wait: (self: Client) -> number,
|
||||||
Destroy: (self: Client) -> (),
|
Destroy: (self: Client) -> (),
|
||||||
logs: (self: Client) -> any,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Server = {
|
export type Server = {
|
||||||
Fire: (self: Server, reliable: boolean, player: Player, ...any) -> (),
|
Fire: (self: Server, reliable: boolean, player: Player, ...any) -> (),
|
||||||
Fires: (self: Server, reliable: boolean, ...any) -> (),
|
Fires: (self: Server, reliable: boolean, ...any) -> (),
|
||||||
Invoke: (self: Server, timeout: number, player: Player, ...any) -> any,
|
Invoke: (self: Server, timeout: number, player: Player, ...any) -> any,
|
||||||
Connect: (self: Server, callback: (player: Player, ...any) -> ()) -> string,
|
Connect: (self: Server, callback: (player: Player, ...any) -> ()) -> Middleware,
|
||||||
Once: (self: Server, callback: (player: Player, ...any) -> ()) -> string,
|
Once: (self: Server, callback: (player: Player, ...any) -> ()) -> Middleware,
|
||||||
Disconnect: (self: Server, key: string) -> (),
|
Disconnect: (self: Server, key: string) -> (),
|
||||||
DisconnectAll: (self: Server) -> (),
|
DisconnectAll: (self: Server) -> (),
|
||||||
Wait: (self: Server) -> number,
|
Wait: (self: Server) -> number,
|
||||||
Destroy: (self: Server) -> (),
|
Destroy: (self: Server) -> (),
|
||||||
logs: (self: Server) -> any,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Signal = {
|
export type Signal = {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
--!strict
|
--!strict
|
||||||
--!native
|
--!native
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
return function(condition: (any), errorMessage: string?): ()
|
return function(condition: (any), errorMessage: string, level: number?): ()
|
||||||
if not (condition) then error(`Warp: {errorMessage}`, 2) end
|
if not (condition) then error(`Warp: {errorMessage}`, level or 2) end
|
||||||
end
|
end
|
|
@ -16,7 +16,7 @@ local writef32 = buffer.writef32
|
||||||
local writef64 = buffer.writef64
|
local writef64 = buffer.writef64
|
||||||
local writestring = buffer.writestring
|
local writestring = buffer.writestring
|
||||||
|
|
||||||
local default = {
|
local default: { [string]: number } = {
|
||||||
point = 0,
|
point = 0,
|
||||||
next = 0,
|
next = 0,
|
||||||
size = 128,
|
size = 128,
|
||||||
|
@ -35,17 +35,16 @@ function DedicatedBuffer.alloc(self: any, byte: number)
|
||||||
local size: number = self.size
|
local size: number = self.size
|
||||||
local b: buffer = self.buffer
|
local b: buffer = self.buffer
|
||||||
|
|
||||||
|
while self.next + byte >= size do
|
||||||
while self.point + byte >= size do
|
size = math.floor(size * 1.25) -- +25% increase
|
||||||
size = math.floor(size * 1.5)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local newBuffer: buffer = create(size)
|
local newBuffer: buffer = create(size)
|
||||||
copy(newBuffer, 0, b)
|
copy(newBuffer, 0, b)
|
||||||
|
|
||||||
b = newBuffer
|
b = newBuffer
|
||||||
|
|
||||||
self.point = self.next
|
self.point = self.next
|
||||||
|
self.buffer = b
|
||||||
self.next += byte
|
self.next += byte
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -57,51 +56,62 @@ function DedicatedBuffer.build(self: any): buffer
|
||||||
return build
|
return build
|
||||||
end
|
end
|
||||||
|
|
||||||
function DedicatedBuffer.wi8(self: any, val: number)
|
function DedicatedBuffer.buildAndRemove(self: any): (buffer, (any)?)
|
||||||
|
local p: number = self.next > self.point and self.next or self.point
|
||||||
|
local build: buffer = create(p)
|
||||||
|
local ref = #self.ref > 0 and table.clone(self.ref) or nil
|
||||||
|
|
||||||
|
copy(build, 0, self.buffer, 0, p)
|
||||||
|
|
||||||
|
self:remove()
|
||||||
|
return build, ref
|
||||||
|
end
|
||||||
|
|
||||||
|
function DedicatedBuffer.wi8(self: any, val: number, alloc: number?)
|
||||||
if not val then return end
|
if not val then return end
|
||||||
self:alloc(1)
|
self:alloc(alloc or 1)
|
||||||
writei8(self.buffer, self.point, val)
|
writei8(self.buffer, self.point, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
function DedicatedBuffer.wi16(self: any, val: number)
|
function DedicatedBuffer.wi16(self: any, val: number, alloc: number?)
|
||||||
if not val then return end
|
if not val then return end
|
||||||
self:alloc(2)
|
self:alloc(alloc or 2)
|
||||||
writei16(self.buffer, self.point, val)
|
writei16(self.buffer, self.point, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
function DedicatedBuffer.wi32(self: any, val: number)
|
function DedicatedBuffer.wi32(self: any, val: number, alloc: number?)
|
||||||
if not val then return end
|
if not val then return end
|
||||||
self:alloc(4)
|
self:alloc(alloc or 4)
|
||||||
writei32(self.buffer, self.point, val)
|
writei32(self.buffer, self.point, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
function DedicatedBuffer.wu8(self: any, val: number)
|
function DedicatedBuffer.wu8(self: any, val: number, alloc: number?)
|
||||||
if not val then return end
|
if not val then return end
|
||||||
self:alloc(1)
|
self:alloc(alloc or 1)
|
||||||
writeu8(self.buffer, self.point, val)
|
writeu8(self.buffer, self.point, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
function DedicatedBuffer.wu16(self: any, val: number)
|
function DedicatedBuffer.wu16(self: any, val: number, alloc: number?)
|
||||||
if not val then return end
|
if not val then return end
|
||||||
self:alloc(2)
|
self:alloc(alloc or 2)
|
||||||
writeu16(self.buffer, self.point, val)
|
writeu16(self.buffer, self.point, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
function DedicatedBuffer.wu32(self: any, val: number)
|
function DedicatedBuffer.wu32(self: any, val: number, alloc: number?)
|
||||||
if not val then return end
|
if not val then return end
|
||||||
self:alloc(4)
|
self:alloc(alloc or 4)
|
||||||
writeu32(self.buffer, self.point, val)
|
writeu32(self.buffer, self.point, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
function DedicatedBuffer.wf32(self: any, val: number)
|
function DedicatedBuffer.wf32(self: any, val: number, alloc: number?)
|
||||||
if not val then return end
|
if not val then return end
|
||||||
self:alloc(4)
|
self:alloc(alloc or 4)
|
||||||
writef32(self.buffer, self.point, val)
|
writef32(self.buffer, self.point, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
function DedicatedBuffer.wf64(self: any, val: number)
|
function DedicatedBuffer.wf64(self: any, val: number, alloc: number?)
|
||||||
if not val then return end
|
if not val then return end
|
||||||
self:alloc(8)
|
self:alloc(alloc or 8)
|
||||||
writef64(self.buffer, self.point, val)
|
writef64(self.buffer, self.point, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -111,11 +121,127 @@ function DedicatedBuffer.wstring(self: any, val: string)
|
||||||
writestring(self.buffer, self.point, val)
|
writestring(self.buffer, self.point, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function DedicatedBuffer.wType(self: any, ref: number)
|
||||||
|
writeu8(self.buffer, self.point, ref)
|
||||||
|
self.point += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function DedicatedBuffer.wRef(self: any, value: any, alloc: number?)
|
||||||
|
if not value then return end
|
||||||
|
self:alloc(alloc or 1)
|
||||||
|
table.insert(self.ref, value)
|
||||||
|
local index = #self.ref
|
||||||
|
writeu8(self.buffer, self.point, index)
|
||||||
|
self.point += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function DedicatedBuffer.pack(self: any, data: {any})
|
||||||
|
if typeof(data) == "nil" then
|
||||||
|
self:wi8(0)
|
||||||
|
elseif typeof(data) == "Instance" then
|
||||||
|
self:wi8(-1) -- Instance marker
|
||||||
|
self:wRef(data)
|
||||||
|
elseif typeof(data) == "table" then
|
||||||
|
--local isArray = (next(data) ~= nil and #data > 0) and true or false
|
||||||
|
local isArray = true
|
||||||
|
local count = 0
|
||||||
|
for k in data do
|
||||||
|
count += 1
|
||||||
|
if typeof(k) ~= "number" or math.floor(k) ~= k then
|
||||||
|
isArray = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if isArray then
|
||||||
|
self:wi8(-2) -- array marker
|
||||||
|
self:wu16(count) -- use 32-bit length
|
||||||
|
for _, v in data do
|
||||||
|
self:pack(v)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self:wi8(-3) -- dictionary marker
|
||||||
|
self:wu16(count) -- number of key-value pairs
|
||||||
|
for k, v in data do
|
||||||
|
self:pack(k) -- pack the key
|
||||||
|
self:pack(v) -- pack the value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif typeof(data) == "EnumItem" then
|
||||||
|
self:wi8(-4)
|
||||||
|
self:wi8(#`{data.EnumType}`)
|
||||||
|
self:wstring(`{data.EnumType}`)
|
||||||
|
self:wu8(data.Value)
|
||||||
|
elseif typeof(data) == "BrickColor" then
|
||||||
|
self:wi8(-5)
|
||||||
|
self:wi16(data.Number)
|
||||||
|
elseif typeof(data) == "Enum" then
|
||||||
|
self:wi8(-6)
|
||||||
|
self:wi8(#`{data}`)
|
||||||
|
self:wstring(`{data}`)
|
||||||
|
elseif typeof(data) == "number" then
|
||||||
|
if math.floor(data) == data then -- Integer
|
||||||
|
if data >= 0 and data <= 255 then
|
||||||
|
self:wi8(1) -- u8 marker
|
||||||
|
self:wu8(data)
|
||||||
|
elseif data >= -32768 and data <= 32767 then
|
||||||
|
self:wi8(2) -- i16 marker
|
||||||
|
self:wi16(data)
|
||||||
|
elseif data >= -2147483647 and data <= 2147483647 then
|
||||||
|
self:wi8(3) -- i32 marker
|
||||||
|
self:wi32(data)
|
||||||
|
else
|
||||||
|
self:wi8(4) -- f64 marker
|
||||||
|
self:wf64(data)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self:wi8(4) -- f64 marker
|
||||||
|
self:wf64(data)
|
||||||
|
end
|
||||||
|
elseif typeof(data) == "boolean" then
|
||||||
|
self:wi8(5) -- boolean marker
|
||||||
|
self:wu8(data and 1 or 0)
|
||||||
|
elseif typeof(data) == "string" then
|
||||||
|
local length = #data
|
||||||
|
if length <= 255 then
|
||||||
|
self:wi8(6)
|
||||||
|
self:wu8(length)
|
||||||
|
elseif length <= 65535 then
|
||||||
|
self:wi8(7)
|
||||||
|
self:wu16(length)
|
||||||
|
else
|
||||||
|
self:wi8(8)
|
||||||
|
self:wi32(length)
|
||||||
|
end
|
||||||
|
self:wstring(data)
|
||||||
|
elseif typeof(data) == "Vector3" then
|
||||||
|
self:wi8(9) -- Vector3 marker
|
||||||
|
self:wf32(data.X)
|
||||||
|
self:wf32(data.Y)
|
||||||
|
self:wf32(data.Z)
|
||||||
|
elseif typeof(data) == "Vector2" then
|
||||||
|
self:wi8(10) -- Vector2 marker
|
||||||
|
self:wf32(data.X)
|
||||||
|
self:wf32(data.Y)
|
||||||
|
elseif typeof(data) == "CFrame" then
|
||||||
|
self:wi8(11) -- CFrame marker
|
||||||
|
for _, v in {data:GetComponents()} do
|
||||||
|
self:wf32(v)
|
||||||
|
end
|
||||||
|
elseif typeof(data) == "Color3" then
|
||||||
|
self:wi8(12) -- Color3 marker
|
||||||
|
self:wu8(data.R * 255)
|
||||||
|
self:wu8(data.G * 255)
|
||||||
|
self:wu8(data.B * 255)
|
||||||
|
else
|
||||||
|
warn(`Unsupported data type: {typeof(data)} value: {data}`)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function DedicatedBuffer.flush(self: any)
|
function DedicatedBuffer.flush(self: any)
|
||||||
self.point = default.point
|
self.point = default.point
|
||||||
self.next = default.next
|
self.next = default.next
|
||||||
self.size = default.size
|
self.size = default.size
|
||||||
self.buffer = create(default.bufferSize)
|
self.buffer = create(default.bufferSize)
|
||||||
|
table.clear(self.ref)
|
||||||
end
|
end
|
||||||
|
|
||||||
function DedicatedBuffer.new()
|
function DedicatedBuffer.new()
|
||||||
|
@ -123,13 +249,17 @@ function DedicatedBuffer.new()
|
||||||
point = default.point,
|
point = default.point,
|
||||||
next = default.next,
|
next = default.next,
|
||||||
size = default.size,
|
size = default.size,
|
||||||
buffer = create(default.bufferSize)
|
buffer = create(default.bufferSize),
|
||||||
|
ref = {},
|
||||||
}, DedicatedBuffer)
|
}, DedicatedBuffer)
|
||||||
end
|
end
|
||||||
|
|
||||||
function DedicatedBuffer.remove(self: any)
|
function DedicatedBuffer.remove(self: any)
|
||||||
self:flush()
|
self:flush()
|
||||||
|
table.clear(self)
|
||||||
setmetatable(self, nil)
|
setmetatable(self, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
export type DedicatedType = typeof(DedicatedBuffer.new())
|
||||||
|
|
||||||
return DedicatedBuffer.new :: typeof(DedicatedBuffer.new)
|
return DedicatedBuffer.new :: typeof(DedicatedBuffer.new)
|
|
@ -8,8 +8,115 @@ local Dedicated = require(script.Dedicated)
|
||||||
|
|
||||||
local tostring = buffer.tostring
|
local tostring = buffer.tostring
|
||||||
local fromstring = buffer.fromstring
|
local fromstring = buffer.fromstring
|
||||||
|
local readu8 = buffer.readu8
|
||||||
|
local readi8 = buffer.readi8
|
||||||
|
local readu16 = buffer.readu16
|
||||||
|
local readi16 = buffer.readi16
|
||||||
|
local readi32 = buffer.readi32
|
||||||
|
local readf32 = buffer.readf32
|
||||||
|
local readf64 = buffer.readf64
|
||||||
|
local readstring = buffer.readstring
|
||||||
|
local len = buffer.len
|
||||||
|
|
||||||
function Buffer.new()
|
local function readValue(b: buffer, position: number, ref: { any }?): (any, number)
|
||||||
|
local typeByte = readi8(b, position)
|
||||||
|
position += 1
|
||||||
|
if typeByte == 0 then -- nil
|
||||||
|
return nil, position
|
||||||
|
elseif typeByte == -1 then -- Instance
|
||||||
|
if not ref or #ref == 0 then
|
||||||
|
return nil, position + 1
|
||||||
|
end
|
||||||
|
local value = ref[readu8(b, position)]
|
||||||
|
if typeof(value) == "Instance" then
|
||||||
|
return value, position + 1
|
||||||
|
end
|
||||||
|
return nil, position + 1
|
||||||
|
elseif typeByte == -2 then -- array
|
||||||
|
local length = readu16(b, position)
|
||||||
|
position += 2
|
||||||
|
local array = {}
|
||||||
|
for _ = 1, length do
|
||||||
|
local value
|
||||||
|
value, position = readValue(b, position, ref)
|
||||||
|
table.insert(array, value)
|
||||||
|
end
|
||||||
|
return array, position
|
||||||
|
elseif typeByte == -3 then -- dictionary
|
||||||
|
local length = readu16(b, position)
|
||||||
|
position += 2
|
||||||
|
local dict = {}
|
||||||
|
for _ = 1, length do
|
||||||
|
local key, value
|
||||||
|
key, position = readValue(b, position, ref)
|
||||||
|
value, position = readValue(b, position, ref)
|
||||||
|
dict[key] = value
|
||||||
|
end
|
||||||
|
return dict, position
|
||||||
|
elseif typeByte == -4 then -- EnumItem
|
||||||
|
local length = readi8(b, position)
|
||||||
|
local value = readstring(b, position + 1, length)
|
||||||
|
local value2 = readu8(b, position + 1 + length)
|
||||||
|
return Enum[value]:FromValue(value2), position + 2 + length
|
||||||
|
elseif typeByte == -5 then -- BrickColor
|
||||||
|
local value = readi16(b, position)
|
||||||
|
return BrickColor.new(value), position + 2
|
||||||
|
elseif typeByte == -6 then -- Enum
|
||||||
|
local length = readi8(b, position)
|
||||||
|
local value = readstring(b, position + 1, length)
|
||||||
|
return Enum[value], position + 1 + length
|
||||||
|
elseif typeByte == 1 then -- int u8
|
||||||
|
local value = readu8(b, position)
|
||||||
|
return value, position + 1
|
||||||
|
elseif typeByte == 2 then -- int i16
|
||||||
|
local value = readi16(b, position)
|
||||||
|
return value, position + 2
|
||||||
|
elseif typeByte == 3 then -- int i32
|
||||||
|
local value = readi32(b, position)
|
||||||
|
return value, position + 4
|
||||||
|
elseif typeByte == 4 then -- f64
|
||||||
|
local value = readf64(b, position)
|
||||||
|
return value, position + 8
|
||||||
|
elseif typeByte == 5 then -- boolean
|
||||||
|
local value = readu8(b, position) == 1
|
||||||
|
return value, position + 1
|
||||||
|
elseif typeByte == 6 then -- string u8
|
||||||
|
local length = readu8(b, position)
|
||||||
|
local value = readstring(b, position + 1, length)
|
||||||
|
return value, position + length + 1
|
||||||
|
elseif typeByte == 7 then -- string u16
|
||||||
|
local length = readu16(b, position)
|
||||||
|
local value = readstring(b, position + 2, length)
|
||||||
|
return value, position + length + 2
|
||||||
|
elseif typeByte == 8 then -- string i32
|
||||||
|
local length = readi32(b, position)
|
||||||
|
local value = readstring(b, position + 4, length)
|
||||||
|
return value, position + length + 4
|
||||||
|
elseif typeByte == 9 then -- Vector3
|
||||||
|
local x = readf32(b, position)
|
||||||
|
local y = readf32(b, position + 4)
|
||||||
|
local z = readf32(b, position + 8)
|
||||||
|
return Vector3.new(x, y, z), position + 12
|
||||||
|
elseif typeByte == 10 then -- Vector2
|
||||||
|
local x = readf32(b, position)
|
||||||
|
local y = readf32(b, position + 8)
|
||||||
|
return Vector2.new(x, y), position + 8
|
||||||
|
elseif typeByte == 11 then -- CFrame
|
||||||
|
local components = {}
|
||||||
|
for i = 1, 12 do
|
||||||
|
table.insert(components, readf32(b, position + (i - 1) * 4))
|
||||||
|
end
|
||||||
|
return CFrame.new(unpack(components)), position + 48
|
||||||
|
elseif typeByte == 12 then -- Color3
|
||||||
|
local r = readu8(b, position)
|
||||||
|
local g = readu8(b, position + 1)
|
||||||
|
local b = readu8(b, position + 2)
|
||||||
|
return Color3.fromRGB(r, g, b), position + 3
|
||||||
|
end
|
||||||
|
error(`Unsupported type marker: {typeByte}`)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Buffer.new(): Dedicated.DedicatedType
|
||||||
return Dedicated()
|
return Dedicated()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -21,4 +128,22 @@ function Buffer.revert(s: string): buffer
|
||||||
return fromstring(s)
|
return fromstring(s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Buffer.write(data: { any }): (buffer, (any)?)
|
||||||
|
local newBuffer = Dedicated()
|
||||||
|
newBuffer:pack(data)
|
||||||
|
return newBuffer:buildAndRemove()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Buffer.read(b: buffer, ref: { any }?): any?
|
||||||
|
local position = 0
|
||||||
|
local result = {}
|
||||||
|
while position < len(b) do
|
||||||
|
local value
|
||||||
|
value, position = readValue(b, position, ref)
|
||||||
|
table.insert(result, value)
|
||||||
|
end
|
||||||
|
ref = nil
|
||||||
|
return table.unpack(result)
|
||||||
|
end
|
||||||
|
|
||||||
return Buffer :: typeof(Buffer)
|
return Buffer :: typeof(Buffer)
|
|
@ -1,6 +1,5 @@
|
||||||
--!strict
|
--!strict
|
||||||
--!native
|
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
return function(): number?
|
return function(): number?
|
||||||
return tonumber(string.sub(tostring(Random.new():NextNumber()), 3, 6)) -- 4 digits
|
return tonumber(string.sub(tostring(Random.new():NextNumber()), 3, 8)) -- 6 digits
|
||||||
end
|
end
|
|
@ -1,25 +1,54 @@
|
||||||
--!strict
|
--!strict
|
||||||
--!native
|
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
local RateLimit = {}
|
local RateLimit = {}
|
||||||
|
|
||||||
local RunService = game:GetService("RunService")
|
local RunService = game:GetService("RunService")
|
||||||
local Assert = require(script.Parent.Assert)
|
local Assert = require(script.Parent.Assert)
|
||||||
local Event = require(script.Parent.Parent.Event).Reliable
|
local Events = require(script.Parent.Parent.Event)
|
||||||
|
local Reliable, Unreliable, Request = Events.Reliable, Events.Unreliable, Events.Request
|
||||||
|
local Signal = require(script.Parent.Parent.Signal)("Warp_OnSpamSignal")
|
||||||
|
|
||||||
|
local map, activity, meta = {}, {}, {}
|
||||||
|
setmetatable(meta , {
|
||||||
|
__index = map,
|
||||||
|
__newindex = function(self, key, value)
|
||||||
|
if not activity[key] then
|
||||||
|
activity[key] = os.clock()
|
||||||
|
end
|
||||||
|
if (os.clock()-activity[key]) >= 1 then
|
||||||
|
activity[key] = os.clock()
|
||||||
|
map[key] = 1
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if value >= 1e2 then -- 100
|
||||||
|
Signal:Fire(key)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
map[key] = value
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
local function onReceived(player: Player)
|
||||||
|
if not meta[player] then
|
||||||
|
meta[player] = 1
|
||||||
|
return
|
||||||
|
end
|
||||||
|
meta[player] += 1
|
||||||
|
end
|
||||||
|
|
||||||
function RateLimit.create(Identifier: string, entrance: number?, interval: number?)
|
function RateLimit.create(Identifier: string, entrance: number?, interval: number?)
|
||||||
Assert(typeof(Identifier) == "string", "Identifier must a string type.")
|
Assert(typeof(Identifier) == "string", "Identifier must a string type.")
|
||||||
if RunService:IsServer() then
|
if RunService:IsServer() then
|
||||||
Assert(typeof(entrance) == "number", "entrance must a number type.")
|
Assert(typeof(entrance) == "number", "entrance must a number type.")
|
||||||
Assert(entrance :: number > 0, "entrance must above 0.")
|
Assert(entrance :: number > 0, "entrance must above 0.")
|
||||||
Event:SetAttribute(Identifier.."_ent", entrance)
|
Reliable:SetAttribute(Identifier.."_ent", entrance)
|
||||||
Event:SetAttribute(Identifier.."_int", interval)
|
Reliable:SetAttribute(Identifier.."_int", interval)
|
||||||
else
|
else
|
||||||
while (not Event:GetAttribute(Identifier.."_ent")) or (not Event:GetAttribute(Identifier.."_int")) do
|
while (not Reliable:GetAttribute(Identifier.."_ent")) or (not Reliable:GetAttribute(Identifier.."_int")) do
|
||||||
task.wait(0.5)
|
task.wait(0.1)
|
||||||
end
|
end
|
||||||
entrance = tonumber(Event:GetAttribute(Identifier.."_ent"))
|
entrance = tonumber(Reliable:GetAttribute(Identifier.."_ent"))
|
||||||
interval = tonumber(Event:GetAttribute(Identifier.."_int"))
|
interval = tonumber(Reliable:GetAttribute(Identifier.."_int"))
|
||||||
end
|
end
|
||||||
local entrances: number = 0
|
local entrances: number = 0
|
||||||
return function(incoming: number?): boolean
|
return function(incoming: number?): boolean
|
||||||
|
@ -33,4 +62,14 @@ function RateLimit.create(Identifier: string, entrance: number?, interval: numbe
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function RateLimit.Protect()
|
||||||
|
if not RunService:IsServer() or Reliable:GetAttribute("Protected") or Unreliable:GetAttribute("Protected") or Request:GetAttribute("Protected") then return end
|
||||||
|
Reliable:SetAttribute("Protected", true)
|
||||||
|
Unreliable:SetAttribute("Protected", true)
|
||||||
|
Request:SetAttribute("Protected", true)
|
||||||
|
Reliable.OnServerEvent:Connect(onReceived)
|
||||||
|
Unreliable.OnServerEvent:Connect(onReceived)
|
||||||
|
Request.OnServerEvent:Connect(onReceived)
|
||||||
|
end
|
||||||
|
|
||||||
return RateLimit :: typeof(RateLimit)
|
return RateLimit :: typeof(RateLimit)
|
|
@ -1,18 +1,19 @@
|
||||||
--!strict
|
--!strict
|
||||||
--!native
|
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
|
local SerDes = {}
|
||||||
local RunService = game:GetService("RunService")
|
local RunService = game:GetService("RunService")
|
||||||
local SerInt = 0
|
local SerInt = 0
|
||||||
|
|
||||||
local Event = require(script.Parent.Parent.Event).Reliable
|
local Event = require(script.Parent.Parent.Event).Reliable
|
||||||
local Assert = require(script.Parent.Assert)
|
local Assert = require(script.Parent.Assert)
|
||||||
|
|
||||||
return function(Identifier: string, timeout: number?): number
|
function SerDes.increment(Identifier: string, timeout: number?): number
|
||||||
Assert(typeof(Identifier) == "string", "Identifier must be a string type.")
|
Assert(typeof(Identifier) == "string", "Identifier must be a string type.")
|
||||||
Assert(SerInt < 255, "reached max 255 identifiers.")
|
|
||||||
if RunService:IsServer() then
|
if RunService:IsServer() then
|
||||||
|
Assert(SerInt < 255, "reached max 255 identifiers.")
|
||||||
if not Event:GetAttribute(Identifier) then
|
if not Event:GetAttribute(Identifier) then
|
||||||
SerInt += 1
|
SerInt += 1
|
||||||
|
Event:SetAttribute(`{SerInt}`, Identifier)
|
||||||
Event:SetAttribute(Identifier, SerInt)
|
Event:SetAttribute(Identifier, SerInt)
|
||||||
--Event:SetAttribute(Identifier, string.pack("I1", SerInt)) -- I1 -> 255 max, I2 -> ~ 6.5e4 max. (SerInt), removed/disabled for buffer migration.
|
--Event:SetAttribute(Identifier, string.pack("I1", SerInt)) -- I1 -> 255 max, I2 -> ~ 6.5e4 max. (SerInt), removed/disabled for buffer migration.
|
||||||
end
|
end
|
||||||
|
@ -20,11 +21,11 @@ return function(Identifier: string, timeout: number?): number
|
||||||
local yieldThread: thread = coroutine.running()
|
local yieldThread: thread = coroutine.running()
|
||||||
local cancel = task.delay(timeout or 10, function() -- yield cancelation (timerout)
|
local cancel = task.delay(timeout or 10, function() -- yield cancelation (timerout)
|
||||||
task.spawn(yieldThread, nil)
|
task.spawn(yieldThread, nil)
|
||||||
error(`Serdes: {Identifier} is taking too long to retrieve, seems like not replicated on server.`, 2)
|
error(`Serdes: {Identifier} is taking too long to retrieve, seems like it's not replicated on server.`, 2)
|
||||||
end)
|
end)
|
||||||
task.spawn(function()
|
task.spawn(function()
|
||||||
while coroutine.status(cancel) ~= "dead" and task.wait(0.5) do -- let it loop for yields!
|
while coroutine.status(cancel) ~= "dead" and task.wait(0.04) do -- let it loop for yields! 1/24
|
||||||
if (Event:GetAttribute(Identifier)) then
|
if Event:GetAttribute(Identifier) then
|
||||||
task.cancel(cancel)
|
task.cancel(cancel)
|
||||||
task.spawn(yieldThread, Event:GetAttribute(Identifier))
|
task.spawn(yieldThread, Event:GetAttribute(Identifier))
|
||||||
break
|
break
|
||||||
|
@ -35,3 +36,15 @@ return function(Identifier: string, timeout: number?): number
|
||||||
end
|
end
|
||||||
return Event:GetAttribute(Identifier)
|
return Event:GetAttribute(Identifier)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function SerDes.decrement()
|
||||||
|
if not RunService:IsServer() or SerInt <= 0 then return end
|
||||||
|
local Identifier = Event:GetAttribute(`{SerInt}`)
|
||||||
|
if not Identifier then return end
|
||||||
|
Event:SetAttribute(`{Identifier}`, nil)
|
||||||
|
Event:SetAttribute(`{SerInt}`, nil)
|
||||||
|
SerInt -= 1
|
||||||
|
Identifier = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return SerDes :: typeof(SerDes)
|
|
@ -1,30 +1,21 @@
|
||||||
--!native
|
--!native
|
||||||
--!strict
|
--!strict
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
local thread: thread? = nil
|
local thread: thread?
|
||||||
|
|
||||||
local function passer(fn, ...): ()
|
local function passer<T...>(func: (T...) -> (), ...: T...): ()
|
||||||
local hold = thread
|
local HoldThread: thread = thread :: thread
|
||||||
thread = nil
|
thread = nil
|
||||||
fn(...)
|
func(...)
|
||||||
thread = hold
|
thread = HoldThread
|
||||||
end
|
end
|
||||||
|
|
||||||
local function yield(): never
|
local function newThread(): ()
|
||||||
while true do
|
thread = coroutine.running()
|
||||||
passer(coroutine.yield())
|
while true do passer(coroutine.yield()) end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if not thread then
|
return function<T...>(func: (T...) -> (), ...: T...): ()
|
||||||
thread = coroutine.create(yield)
|
if not thread then task.spawn(newThread) end
|
||||||
coroutine.resume(thread :: any, thread)
|
task.spawn(thread :: thread, func, ...)
|
||||||
end
|
|
||||||
|
|
||||||
return function(fn: (...any) -> (...any?), ...: any): ()
|
|
||||||
if not thread then
|
|
||||||
thread = coroutine.create(yield)
|
|
||||||
coroutine.resume(thread :: any, thread)
|
|
||||||
end
|
|
||||||
task.spawn(thread :: thread, fn, ...)
|
|
||||||
end
|
end
|
|
@ -32,32 +32,32 @@ function Index.Client(Identifier: string, conf: Type.ClientConf?): Type.Client
|
||||||
return require(Client.Index)(Identifier, conf) :: Type.Client
|
return require(Client.Index)(Identifier, conf) :: Type.Client
|
||||||
end
|
end
|
||||||
|
|
||||||
function Index.fromServerArray(arrays: { any }): Type.fromServerArray
|
function Index.fromServerArray(arrays: { string } | { [string]: Type.ServerConf }): Type.fromServerArray
|
||||||
Assert(IsServer, `[Warp]: Calling .fromServerArray({arrays}) on client side (expected server side)`)
|
Assert(IsServer, `[Warp]: Calling .fromServerArray({arrays}) on client side (expected server side)`)
|
||||||
Assert(typeof(arrays) == "table", "[Warp]: Array must be a table type, got {typeof(arrays)}")
|
Assert(typeof(arrays) == "table", "[Warp]: Array must be a table type, got {typeof(arrays)}")
|
||||||
local copy = {}
|
local copy: { [string]: Type.Server } = {}
|
||||||
for param1: any, param2: any in arrays do
|
for param1, param2: string | Type.ServerConf in arrays do
|
||||||
if typeof(param2) == "table" then
|
if typeof(param2) == "table" then
|
||||||
copy[param1] = Index.Server(param1, param2)
|
copy[param1] = Index.Server(param1, param2)
|
||||||
else
|
else
|
||||||
copy[param2] = Index.Server(param2)
|
copy[param2] = Index.Server(param2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return copy :: typeof(copy)
|
return copy
|
||||||
end
|
end
|
||||||
|
|
||||||
function Index.fromClientArray(arrays: { any }): Type.fromClientArray
|
function Index.fromClientArray(arrays: { string } | { [string]: Type.ClientConf }): Type.fromClientArray
|
||||||
Assert(not IsServer, `[Warp]: Calling .fromClientArray({arrays}) on server side (expected client side)`)
|
Assert(not IsServer, `[Warp]: Calling .fromClientArray({arrays}) on server side (expected client side)`)
|
||||||
Assert(typeof(arrays) == "table", `[Warp]: Array must be a table type, got {typeof(arrays)}`)
|
Assert(typeof(arrays) == "table", `[Warp]: Array must be a table type, got {typeof(arrays)}`)
|
||||||
local copy = {}
|
local copy = {}
|
||||||
for param1: any, param2: any in arrays do
|
for param1, param2: string | Type.ClientConf in arrays do
|
||||||
if typeof(param2) == "table" then
|
if typeof(param2) == "table" then
|
||||||
copy[param1] = Index.Client(param1, param2)
|
copy[param1] = Index.Client(param1, param2)
|
||||||
else
|
else
|
||||||
copy[param2] = Index.Client(param2)
|
copy[param2] = Index.Client(param2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return copy :: typeof(copy)
|
return copy
|
||||||
end
|
end
|
||||||
|
|
||||||
function Index.Signal(Identifier: string)
|
function Index.Signal(Identifier: string)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
-- Warp Library (@Eternity_Devs)
|
-- Warp Library (@Eternity_Devs)
|
||||||
-- version 1.0.11
|
-- version 1.0.14
|
||||||
--!strict
|
--!strict
|
||||||
--!native
|
--!native
|
||||||
--!optimize 2
|
--!optimize 2
|
||||||
|
@ -11,6 +11,8 @@ return {
|
||||||
fromServerArray = Index.fromServerArray,
|
fromServerArray = Index.fromServerArray,
|
||||||
fromClientArray = Index.fromClientArray,
|
fromClientArray = Index.fromClientArray,
|
||||||
|
|
||||||
|
OnSpamSignal = Index.OnSpamSignal,
|
||||||
|
|
||||||
Signal = Index.Signal,
|
Signal = Index.Signal,
|
||||||
fromSignalArray = Index.fromSignalArray,
|
fromSignalArray = Index.fromSignalArray,
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
[package]
|
[package]
|
||||||
name = "imezx/warp"
|
name = "imezx/warp"
|
||||||
version = "1.0.11"
|
version = "1.0.14"
|
||||||
registry = "https://github.com/UpliftGames/wally-index"
|
registry = "https://github.com/UpliftGames/wally-index"
|
||||||
realm = "shared"
|
realm = "shared"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
exclude = ["node_modules", "docs", ".github", "*.rbxl", "*.rbxmx", "*.rbxml", "TestEZ", "test.project.json"]
|
exclude = ["node_modules", "docs", ".github", "*.rbxl", "*.rbxmx", "*.rbxml", "*.rbxm", "TestEZ", "test.project.json"]
|
||||||
description = "A very-fast & powerful networking library for Roblox."
|
description = "A very-fast & powerful networking library for Roblox."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
Loading…
Reference in a new issue