mirror of
https://github.com/imezx/Warp.git
synced 2026-03-18 00:44:16 +00:00
Compare commits
No commits in common. "master" and "1.1.0-pre4" have entirely different histories.
master
...
1.1.0-pre4
4 changed files with 453 additions and 407 deletions
|
|
@ -10,11 +10,7 @@ local Client = Warp.Client()
|
|||
|
||||
## `.awaitReady` <Badge type="warning" text="yield" />
|
||||
|
||||
Yields the current thread until the client has successfully initialized and synchronized with the server's replication data (identifier).
|
||||
|
||||
::: info
|
||||
Its optionally, but highly recommended to call this before firing or connecting to any events to ensure the network is fully ready.
|
||||
:::
|
||||
Yields the current thread until the client has successfully initialized and synchronized with the server's replication data (identifier). Its optionally, but highly recommended to call this before firing or connecting to any events to ensure the network is fully ready.
|
||||
|
||||
::: code-group
|
||||
```luau [Variable]
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ local deltaT: number, cycle: number = 0, 1 / 61
|
|||
local writer: Buffer.Writer = Buffer.createWriter()
|
||||
|
||||
type Connection = {
|
||||
Connected: boolean,
|
||||
Disconnect: (self: Connection) -> (),
|
||||
Connected: boolean,
|
||||
Disconnect: (self: Connection) -> (),
|
||||
}
|
||||
type Event = {
|
||||
i: number,
|
||||
c: (Player, ...any?) -> ...any?,
|
||||
i: number,
|
||||
c: (Player, ...any?) -> ...any?,
|
||||
}
|
||||
|
||||
local queueEvent: { { any } } = {}
|
||||
|
|
@ -37,79 +37,79 @@ Client.awaitReady = Replication.wait_for_ready
|
|||
--@schema Buffer.SchemaType
|
||||
-- Define a schema for strict data packing on a specific event.
|
||||
Client.useSchema = function(remoteName: string, schema: Buffer.SchemaType)
|
||||
local id = Replication.get_id[remoteName]
|
||||
if not id then
|
||||
warn(`[Warp]: ".useSchema"::"{remoteName}" does not exist, likely its not registered on the server yet.`)
|
||||
return
|
||||
end
|
||||
eventSchemas[id] = schema
|
||||
local id = Replication.get_id(remoteName)
|
||||
if not id then
|
||||
warn(`[Warp]: ".useSchema"::"{remoteName}" does not exist, likely its not registered on the server yet.`)
|
||||
return
|
||||
end
|
||||
eventSchemas[id] = schema
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
--@fn function
|
||||
-- Connect to an event to receive incoming data from the server.
|
||||
Client.Connect = function(remoteName: string, fn: (Player, ...any?) -> ...any?): Connection
|
||||
local id = Replication.get_id[remoteName]
|
||||
if not id then
|
||||
warn(`[Warp]: ".Connect"::"{remoteName}" does not exist, likely its not registered on the server yet.`)
|
||||
return { Connected = false, Disconnect = function() return end } :: Connection
|
||||
end
|
||||
local detail = {
|
||||
i = id,
|
||||
c = fn,
|
||||
}
|
||||
table.insert(eventListeners, detail)
|
||||
return {
|
||||
Connected = true,
|
||||
Disconnect = function(self: Connection)
|
||||
if not self.Connected then
|
||||
return
|
||||
end
|
||||
self.Connected = false
|
||||
local idx = table.find(eventListeners, detail)
|
||||
if idx then
|
||||
table.remove(eventListeners, idx)
|
||||
end
|
||||
end,
|
||||
} :: Connection
|
||||
local id = Replication.get_id(remoteName)
|
||||
if not id then
|
||||
warn(`[Warp]: ".Connect"::"{remoteName}" does not exist, likely its not registered on the server yet.`)
|
||||
return { Connected = false, Disconnect = function() return end } :: Connection
|
||||
end
|
||||
local detail = {
|
||||
i = id,
|
||||
c = fn,
|
||||
}
|
||||
table.insert(eventListeners, detail)
|
||||
return {
|
||||
Connected = true,
|
||||
Disconnect = function(self: Connection)
|
||||
if not self.Connected then
|
||||
return
|
||||
end
|
||||
self.Connected = false
|
||||
local idx = table.find(eventListeners, detail)
|
||||
if idx then
|
||||
table.remove(eventListeners, idx)
|
||||
end
|
||||
end,
|
||||
} :: Connection
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
--@fn function
|
||||
-- Similar to :Connect but automatically disconnects after the first firing.
|
||||
Client.Once = function(remoteName: string, fn: (...any?) -> ()): Connection
|
||||
local connection
|
||||
connection = Client.Connect(remoteName, function(...: any?)
|
||||
if connection then
|
||||
connection:Disconnect()
|
||||
end
|
||||
fn(...)
|
||||
end)
|
||||
return connection
|
||||
local connection
|
||||
connection = Client.Connect(remoteName, function(...: any?)
|
||||
if connection then
|
||||
connection:Disconnect()
|
||||
end
|
||||
fn(...)
|
||||
end)
|
||||
return connection
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
-- Wait for an event to be triggered. Yields the current thread.
|
||||
Client.Wait = function(remoteName: string): (number, ...any?)
|
||||
local thread, t = coroutine.running(), os.clock()
|
||||
Client.Once(remoteName, function(...: any?)
|
||||
task.spawn(thread, os.clock() - t, ...)
|
||||
end)
|
||||
return coroutine.yield()
|
||||
local thread, t = coroutine.running(), os.clock()
|
||||
Client.Once(remoteName, function(...: any?)
|
||||
task.spawn(thread, os.clock() - t, ...)
|
||||
end)
|
||||
return coroutine.yield()
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
-- Disconnect all connections for a specific event.
|
||||
Client.DisconnectAll = function(remoteName: string)
|
||||
local id = Replication.get_id[remoteName]
|
||||
if not id then
|
||||
return
|
||||
end
|
||||
for idx = #eventListeners, 1, -1 do
|
||||
if eventListeners[idx].i == id then
|
||||
table.remove(eventListeners, idx)
|
||||
end
|
||||
end
|
||||
local id = Replication.get_id(remoteName)
|
||||
if not id then
|
||||
return
|
||||
end
|
||||
for idx = #eventListeners, 1, -1 do
|
||||
if eventListeners[idx].i == id then
|
||||
table.remove(eventListeners, idx)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
|
|
@ -120,140 +120,140 @@ Client.Destroy = Client.DisconnectAll
|
|||
--@reliable boolean
|
||||
-- Fire an event to the server.
|
||||
Client.Fire = function(remoteName: string, reliable: boolean, ...: any?)
|
||||
local id = Replication.get_id[remoteName]
|
||||
if id then
|
||||
table.insert(reliable and queueEvent or queueUnreliableEvent, {
|
||||
Replication.get_id[remoteName],
|
||||
{ ... } :: any,
|
||||
})
|
||||
end
|
||||
local id = Replication.get_id(remoteName)
|
||||
if id then
|
||||
table.insert(reliable and queueEvent or queueUnreliableEvent, {
|
||||
Replication.get_id(remoteName),
|
||||
{ ... } :: any,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
--@timeout number?
|
||||
-- Invoke the server with timeout support. Yields the current thread. Returns nil if timeout occurs.
|
||||
Client.Invoke = function(remoteName: string, timeout: number?, ...: any?): ...any?
|
||||
local id = Replication.get_id[remoteName]
|
||||
if not id then
|
||||
return nil
|
||||
end
|
||||
invokeId += 1
|
||||
local reqid, thread = `{invokeId}`, coroutine.running()
|
||||
local id = Replication.get_id(remoteName)
|
||||
if not id then
|
||||
return nil
|
||||
end
|
||||
invokeId += 1
|
||||
local reqid, thread = `{invokeId}`, coroutine.running()
|
||||
|
||||
pendingInvokes[reqid] = thread
|
||||
task.delay(timeout or 2, function()
|
||||
local pending = pendingInvokes[reqid]
|
||||
if not pending then
|
||||
return
|
||||
end
|
||||
task.spawn(pending, nil)
|
||||
pendingInvokes[reqid] = nil
|
||||
end)
|
||||
table.insert(queueEvent, {
|
||||
0,
|
||||
{ id, reqid :: any, { ... } :: any } :: any,
|
||||
})
|
||||
return coroutine.yield()
|
||||
pendingInvokes[reqid] = thread
|
||||
task.delay(timeout or 2, function()
|
||||
local pending = pendingInvokes[reqid]
|
||||
if not pending then
|
||||
return
|
||||
end
|
||||
task.spawn(pending, nil)
|
||||
pendingInvokes[reqid] = nil
|
||||
end)
|
||||
table.insert(queueEvent, {
|
||||
0,
|
||||
{ id, reqid :: any, { ... } :: any } :: any,
|
||||
})
|
||||
return coroutine.yield()
|
||||
end
|
||||
|
||||
if RunService:IsClient() then
|
||||
local function processIncoming(b: buffer, ref: { Instance }?, handleInvokes: boolean)
|
||||
if type(b) ~= "buffer" then
|
||||
return
|
||||
end
|
||||
local contents = Buffer.readEvents(b, ref, eventSchemas)
|
||||
for _, content in contents do
|
||||
local remote = content[1]
|
||||
local content = content[2]
|
||||
if handleInvokes then
|
||||
if remote == 0 then
|
||||
local id = content[1]
|
||||
local results = content[2]
|
||||
local pending = pendingInvokes[id]
|
||||
if pending then
|
||||
task.spawn(pending :: any, table.unpack(results))
|
||||
pendingInvokes[id] = nil
|
||||
end
|
||||
continue
|
||||
end
|
||||
if remote == 1 then
|
||||
if #eventListeners == 0 then
|
||||
continue
|
||||
end
|
||||
local remoteName = content[1]
|
||||
local id = content[2]
|
||||
local args = content[3]
|
||||
for _, connection in eventListeners do
|
||||
if connection.i == remoteName then
|
||||
Thread(function()
|
||||
local results = { connection.c(table.unpack(args)) }
|
||||
table.insert(queueEvent, {
|
||||
1,
|
||||
{ id, results } :: any,
|
||||
})
|
||||
end)
|
||||
break
|
||||
end
|
||||
end
|
||||
continue
|
||||
end
|
||||
end
|
||||
if #eventListeners == 0 then
|
||||
continue
|
||||
end
|
||||
for _, connection in eventListeners do
|
||||
if connection.i ~= remote then
|
||||
continue
|
||||
end
|
||||
Thread(connection.c, table.unpack(content))
|
||||
end
|
||||
end
|
||||
end
|
||||
local function processIncoming(b: buffer, ref: { Instance }?, handleInvokes: boolean)
|
||||
if type(b) ~= "buffer" then
|
||||
return
|
||||
end
|
||||
local contents = Buffer.readEvents(b, ref, eventSchemas)
|
||||
for _, content in contents do
|
||||
local remote = content[1]
|
||||
local content = content[2]
|
||||
if handleInvokes then
|
||||
if remote == 0 then
|
||||
local id = content[1]
|
||||
local results = content[2]
|
||||
local pending = pendingInvokes[id]
|
||||
if pending then
|
||||
task.spawn(pending :: any, table.unpack(results))
|
||||
pendingInvokes[id] = nil
|
||||
end
|
||||
continue
|
||||
end
|
||||
if remote == 1 then
|
||||
if #eventListeners == 0 then
|
||||
continue
|
||||
end
|
||||
local remoteName = content[1]
|
||||
local id = content[2]
|
||||
local args = content[3]
|
||||
for _, connection in eventListeners do
|
||||
if connection.i == remoteName then
|
||||
Thread(function()
|
||||
local results = { connection.c(table.unpack(args)) }
|
||||
table.insert(queueEvent, {
|
||||
1,
|
||||
{ id, results } :: any,
|
||||
})
|
||||
end)
|
||||
break
|
||||
end
|
||||
end
|
||||
continue
|
||||
end
|
||||
end
|
||||
if #eventListeners == 0 then
|
||||
continue
|
||||
end
|
||||
for _, connection in eventListeners do
|
||||
if connection.i ~= remote then
|
||||
continue
|
||||
end
|
||||
Thread(connection.c, table.unpack(content))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.OnClientEvent:Connect(function(b: buffer, ref: { Instance }?)
|
||||
processIncoming(b, ref, true)
|
||||
end)
|
||||
Event.OnClientEvent:Connect(function(b: buffer, ref: { Instance }?)
|
||||
processIncoming(b, ref, true)
|
||||
end)
|
||||
|
||||
UnreliableEvent.OnClientEvent:Connect(function(b: buffer, ref: { Instance }?)
|
||||
processIncoming(b, ref, false)
|
||||
end)
|
||||
UnreliableEvent.OnClientEvent:Connect(function(b: buffer, ref: { Instance }?)
|
||||
processIncoming(b, ref, false)
|
||||
end)
|
||||
|
||||
RunService.PostSimulation:Connect(function(d: number)
|
||||
deltaT += d
|
||||
if deltaT < cycle then
|
||||
return
|
||||
end
|
||||
deltaT = 0
|
||||
RunService.PostSimulation:Connect(function(d: number)
|
||||
deltaT += d
|
||||
if deltaT < cycle then
|
||||
return
|
||||
end
|
||||
deltaT = 0
|
||||
|
||||
-- reliable
|
||||
if #queueEvent > 0 then
|
||||
Buffer.writeEvents(writer, queueEvent, eventSchemas)
|
||||
do
|
||||
local buf, ref = Buffer.buildWithRefs(writer)
|
||||
Buffer.reset(writer)
|
||||
if not ref or #ref == 0 then
|
||||
Event:FireServer(buf)
|
||||
else
|
||||
Event:FireServer(buf, ref)
|
||||
end
|
||||
end
|
||||
table.clear(queueEvent)
|
||||
end
|
||||
-- unreliable
|
||||
if #queueUnreliableEvent > 0 then
|
||||
Buffer.writeEvents(writer, queueUnreliableEvent, eventSchemas)
|
||||
do
|
||||
local buf, ref = Buffer.buildWithRefs(writer)
|
||||
Buffer.reset(writer)
|
||||
if not ref or #ref == 0 then
|
||||
UnreliableEvent:FireServer(buf)
|
||||
else
|
||||
UnreliableEvent:FireServer(buf, ref)
|
||||
end
|
||||
end
|
||||
table.clear(queueUnreliableEvent)
|
||||
end
|
||||
end)
|
||||
-- reliable
|
||||
if #queueEvent > 0 then
|
||||
Buffer.writeEvents(writer, queueEvent, eventSchemas)
|
||||
do
|
||||
local buf, ref = Buffer.buildWithRefs(writer)
|
||||
Buffer.reset(writer)
|
||||
if not ref or #ref == 0 then
|
||||
Event:FireServer(buf)
|
||||
else
|
||||
Event:FireServer(buf, ref)
|
||||
end
|
||||
end
|
||||
table.clear(queueEvent)
|
||||
end
|
||||
-- unreliable
|
||||
if #queueUnreliableEvent > 0 then
|
||||
Buffer.writeEvents(writer, queueUnreliableEvent, eventSchemas)
|
||||
do
|
||||
local buf, ref = Buffer.buildWithRefs(writer)
|
||||
Buffer.reset(writer)
|
||||
if not ref or #ref == 0 then
|
||||
UnreliableEvent:FireServer(buf)
|
||||
else
|
||||
UnreliableEvent:FireServer(buf, ref)
|
||||
end
|
||||
end
|
||||
table.clear(queueUnreliableEvent)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
--[[
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ if RunService:IsClient() or RunService:IsRunMode() then
|
|||
local contents = Buffer.readRepl(b, identifiers_schema)
|
||||
if #contents == 0 then return end
|
||||
for _, content in contents do
|
||||
local id: number, remote: string = content[1], content[2]
|
||||
local id, remote = content[1], content[2]
|
||||
warp_identifier_registry.cache[remote] = id
|
||||
warp_identifier_registry.name[id] = remote
|
||||
|
||||
|
|
@ -61,8 +61,60 @@ if RunService:IsClient() or RunService:IsRunMode() then
|
|||
coroutine.yield()
|
||||
end
|
||||
|
||||
Replication.get_id = warp_identifier_registry.cache
|
||||
Replication.get_name = warp_identifier_registry.name
|
||||
--@name string
|
||||
--@timeout number (default: 0)
|
||||
Replication.get_id = function(name: string, timeout: number?): number
|
||||
local cached = warp_identifier_registry.cache[name]
|
||||
if cached or type(timeout) ~= "number" then return cached end
|
||||
|
||||
local thread = coroutine.running()
|
||||
|
||||
if not pending_id_yields[name] then pending_id_yields[name] = {} end
|
||||
table.insert(pending_id_yields[name], thread)
|
||||
|
||||
task.delay(timeout, function()
|
||||
if pending_id_yields[name] then
|
||||
local idx = table.find(pending_id_yields[name], thread)
|
||||
if idx then
|
||||
table.remove(pending_id_yields[name], idx)
|
||||
task.spawn(thread, nil)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local obj: number = coroutine.yield()
|
||||
if not obj then
|
||||
warn(`[Replication] timeout: could not find identifier '{name}' after {timeout}s.`)
|
||||
end
|
||||
return obj
|
||||
end
|
||||
|
||||
--@name string
|
||||
--@timeout number (default: 0)
|
||||
Replication.get_name = function(id: number, timeout: number?): string
|
||||
local cached = warp_identifier_registry.name[id]
|
||||
if cached or type(timeout) ~= "number" then return cached end
|
||||
|
||||
local thread = coroutine.running()
|
||||
if not pending_name_yields[id] then pending_name_yields[id] = {} end
|
||||
table.insert(pending_name_yields[id], thread)
|
||||
|
||||
task.delay(timeout, function()
|
||||
if pending_name_yields[id] then
|
||||
local idx = table.find(pending_name_yields[id], thread)
|
||||
if idx then
|
||||
table.remove(pending_name_yields[id], idx)
|
||||
task.spawn(thread, nil)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local obj: string = coroutine.yield()
|
||||
if not id then
|
||||
warn(`[Replication] timeout: could not find identifier '{id}' after {timeout}s.`)
|
||||
end
|
||||
return obj
|
||||
end
|
||||
else
|
||||
local replication_ready: { Player }, pending_replications = {}, {}
|
||||
local writer: Buffer.Writer = Buffer.createWriter()
|
||||
|
|
|
|||
|
|
@ -16,19 +16,19 @@ local deltaT: number, cycle: number = 0, 1 / 61
|
|||
local writer: Buffer.Writer = Buffer.createWriter()
|
||||
|
||||
type Connection = {
|
||||
Connected: boolean,
|
||||
Disconnect: (self: Connection) -> (),
|
||||
Connected: boolean,
|
||||
Disconnect: (self: Connection) -> (),
|
||||
}
|
||||
type Event = {
|
||||
i: number,
|
||||
c: (Player, ...any?) -> ...any?,
|
||||
i: number,
|
||||
c: (Player, ...any?) -> ...any?,
|
||||
}
|
||||
|
||||
local queueEvent: {
|
||||
[Player]: { { any } },
|
||||
[Player]: { { any } },
|
||||
} = {}
|
||||
local queueUnreliableEvent: {
|
||||
[Player]: { { any } },
|
||||
[Player]: { { any } },
|
||||
} = {}
|
||||
local eventListeners: { Event } = {}
|
||||
local eventSchemas: { [number]: Buffer.SchemaType } = {}
|
||||
|
|
@ -41,78 +41,78 @@ local invokeId = 0
|
|||
--@optional
|
||||
-- Register namespaces to ensure all of the namespaces is being registered earlier on the server to prevent any unexpected issues on the client.
|
||||
Server.reg_namespaces = function(namespaces: { string })
|
||||
for _, name: string in namespaces do
|
||||
Identifier.get_id(name)
|
||||
end
|
||||
for _, name: string in namespaces do
|
||||
Identifier.get_id(name)
|
||||
end
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
--@schema Buffer.SchemaType
|
||||
-- Define a schema for strict data packing on a specific event.
|
||||
Server.useSchema = function(remoteName: string, schema: Buffer.SchemaType)
|
||||
eventSchemas[Identifier.get_id(remoteName)] = schema
|
||||
eventSchemas[Identifier.get_id(remoteName)] = schema
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
--@fn function
|
||||
-- Connect to an event to receive incoming data from clients.
|
||||
Server.Connect = function(remoteName: string, fn: (Player, ...any?) -> ...any?): Connection
|
||||
local detail = {
|
||||
i = Identifier.get_id(remoteName),
|
||||
c = fn,
|
||||
}
|
||||
table.insert(eventListeners, detail)
|
||||
return {
|
||||
Connected = true,
|
||||
Disconnect = function(self: Connection)
|
||||
if not self.Connected then
|
||||
return
|
||||
end
|
||||
self.Connected = false
|
||||
local idx = table.find(eventListeners, detail)
|
||||
if idx then
|
||||
table.remove(eventListeners, idx)
|
||||
end
|
||||
end,
|
||||
} :: Connection
|
||||
local detail = {
|
||||
i = Identifier.get_id(remoteName),
|
||||
c = fn,
|
||||
}
|
||||
table.insert(eventListeners, detail)
|
||||
return {
|
||||
Connected = true,
|
||||
Disconnect = function(self: Connection)
|
||||
if not self.Connected then
|
||||
return
|
||||
end
|
||||
self.Connected = false
|
||||
local idx = table.find(eventListeners, detail)
|
||||
if idx then
|
||||
table.remove(eventListeners, idx)
|
||||
end
|
||||
end,
|
||||
} :: Connection
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
--@fn function
|
||||
-- Similar to :Connect but automatically disconnects after the first firing.
|
||||
Server.Once = function(remoteName: string, fn: (...any?) -> ()): Connection
|
||||
local connection
|
||||
connection = Server.Connect(remoteName, function(...: any?)
|
||||
if connection then
|
||||
connection:Disconnect()
|
||||
end
|
||||
fn(...)
|
||||
end)
|
||||
return connection
|
||||
local connection
|
||||
connection = Server.Connect(remoteName, function(...: any?)
|
||||
if connection then
|
||||
connection:Disconnect()
|
||||
end
|
||||
fn(...)
|
||||
end)
|
||||
return connection
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
-- Wait for an event to be triggered. Yields the current thread.
|
||||
Server.Wait = function(remoteName: string): (number, ...any?)
|
||||
local thread, t = coroutine.running(), os.clock()
|
||||
Server.Once(remoteName, function(...: any?)
|
||||
task.spawn(thread, os.clock() - t, ...)
|
||||
end)
|
||||
return coroutine.yield()
|
||||
local thread, t = coroutine.running(), os.clock()
|
||||
Server.Once(remoteName, function(...: any?)
|
||||
task.spawn(thread, os.clock() - t, ...)
|
||||
end)
|
||||
return coroutine.yield()
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
-- Disconnect all connections for a specific event.
|
||||
Server.DisconnectAll = function(remoteName: string)
|
||||
local id = Identifier.get_id(remoteName)
|
||||
if not id then
|
||||
return
|
||||
end
|
||||
for idx = #eventListeners, 1, -1 do
|
||||
if eventListeners[idx].i == id then
|
||||
table.remove(eventListeners, idx)
|
||||
end
|
||||
end
|
||||
local id = Identifier.get_id(remoteName)
|
||||
if not id then
|
||||
return
|
||||
end
|
||||
for idx = #eventListeners, 1, -1 do
|
||||
if eventListeners[idx].i == id then
|
||||
table.remove(eventListeners, idx)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
|
|
@ -124,23 +124,23 @@ Server.Destroy = Server.DisconnectAll
|
|||
--@player Player
|
||||
-- Fire an event to a specific player.
|
||||
Server.Fire = function(remoteName: string, reliable: boolean, player: Player, ...: any?)
|
||||
local targetQueue = reliable and queueEvent or queueUnreliableEvent
|
||||
if not targetQueue[player] then
|
||||
targetQueue[player] = {} :: any
|
||||
end
|
||||
table.insert(targetQueue[player], {
|
||||
Identifier.get_id(remoteName),
|
||||
{ ... } :: any,
|
||||
})
|
||||
local targetQueue = reliable and queueEvent or queueUnreliableEvent
|
||||
if not targetQueue[player] then
|
||||
targetQueue[player] = {} :: any
|
||||
end
|
||||
table.insert(targetQueue[player], {
|
||||
Identifier.get_id(remoteName),
|
||||
{ ... } :: any,
|
||||
})
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
--@reliable boolean
|
||||
-- Fire an event to all connected players.
|
||||
Server.Fires = function(remoteName: string, reliable: boolean, ...: any?)
|
||||
for _, player: Player in players_ready do
|
||||
Server.Fire(remoteName, reliable, player, ...)
|
||||
end
|
||||
for _, player: Player in players_ready do
|
||||
Server.Fire(remoteName, reliable, player, ...)
|
||||
end
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
|
|
@ -148,10 +148,10 @@ end
|
|||
--@except { Player }
|
||||
-- Fire an event to all players except specified ones.
|
||||
Server.FireExcept = function(remoteName: string, reliable: boolean, except: { Player }, ...: any?)
|
||||
for _, player: Player in players_ready do
|
||||
if table.find(except, player) then continue end
|
||||
Server.Fire(remoteName, reliable, player, ...)
|
||||
end
|
||||
for _, player: Player in players_ready do
|
||||
if table.find(except, player) then continue end
|
||||
Server.Fire(remoteName, reliable, player, ...)
|
||||
end
|
||||
end
|
||||
|
||||
--@remoteName string
|
||||
|
|
@ -159,174 +159,172 @@ end
|
|||
--@timeout number?
|
||||
-- Invoke a client with timeout support. Yields the current thread. Returns nil if timeout occurs.
|
||||
Server.Invoke = function(remoteName: string, player: Player, timeout: number?, ...: any?): ...any?
|
||||
invokeId += 1
|
||||
local reqId, thread = `{invokeId}`, coroutine.running()
|
||||
invokeId += 1
|
||||
local reqId, thread = `{invokeId}`, coroutine.running()
|
||||
|
||||
pendingInvokes[reqId] = thread
|
||||
task.delay(timeout or 2, function()
|
||||
local pending = pendingInvokes[reqId]
|
||||
if not pending then
|
||||
return
|
||||
end
|
||||
task.spawn(pending, nil)
|
||||
pendingInvokes[reqId] = nil
|
||||
end)
|
||||
if not queueEvent[player] then
|
||||
queueEvent[player] = {} :: any
|
||||
end
|
||||
table.insert(queueEvent[player], {
|
||||
0,
|
||||
{ Identifier.get_id(remoteName), reqId :: any, { ... } :: any } :: any,
|
||||
})
|
||||
return coroutine.yield()
|
||||
pendingInvokes[reqId] = thread
|
||||
task.delay(timeout or 2, function()
|
||||
local pending = pendingInvokes[reqId]
|
||||
if not pending then
|
||||
return
|
||||
end
|
||||
task.spawn(pending, nil)
|
||||
pendingInvokes[reqId] = nil
|
||||
end)
|
||||
if not queueEvent[player] then
|
||||
queueEvent[player] = {} :: any
|
||||
end
|
||||
table.insert(queueEvent[player], {
|
||||
0,
|
||||
{ Identifier.get_id(remoteName), reqId :: any, { ... } :: any } :: any,
|
||||
})
|
||||
return coroutine.yield()
|
||||
end
|
||||
|
||||
if RunService:IsServer() then
|
||||
local function processIncoming(player: Player, b: buffer, ref: { Instance }?, handleInvokes: boolean)
|
||||
if type(b) ~= "buffer" then
|
||||
return
|
||||
end
|
||||
if not RunService:IsStudio() then
|
||||
local bytes: number = (player_bytes[player] or 0) + math.max(buffer.len(b), 800)
|
||||
if bytes > 8e3 then return end
|
||||
player_bytes[player] = bytes
|
||||
end
|
||||
local function processIncoming(player: Player, b: buffer, ref: { Instance }?, handleInvokes: boolean)
|
||||
if type(b) ~= "buffer" then
|
||||
return
|
||||
end
|
||||
local bytes: number = (player_bytes[player] or 0) + math.max(buffer.len(b), 800)
|
||||
if bytes > 8e3 then return end
|
||||
player_bytes[player] = bytes
|
||||
|
||||
local contents = Buffer.readEvents(b, ref, eventSchemas)
|
||||
for _, content in contents do
|
||||
local remote = content[1]
|
||||
local content = content[2]
|
||||
if handleInvokes then
|
||||
if remote == 1 then
|
||||
local id = content[1]
|
||||
local results = content[2]
|
||||
local pending = pendingInvokes[id]
|
||||
if pending then
|
||||
task.spawn(pending :: any, table.unpack(results))
|
||||
pendingInvokes[id] = nil
|
||||
end
|
||||
continue
|
||||
end
|
||||
if remote == 0 then
|
||||
if #eventListeners == 0 then
|
||||
continue
|
||||
end
|
||||
local remoteName = content[1]
|
||||
local id = content[2]
|
||||
local args = content[3]
|
||||
for _, connection in eventListeners do
|
||||
if connection.i == remoteName then
|
||||
Thread(function()
|
||||
local results = { connection.c(player, table.unpack(args)) }
|
||||
if not queueEvent[player] then
|
||||
queueEvent[player] = {} :: any
|
||||
end
|
||||
table.insert(queueEvent[player], {
|
||||
0,
|
||||
{ id, results } :: any,
|
||||
})
|
||||
end)
|
||||
break
|
||||
end
|
||||
end
|
||||
continue
|
||||
end
|
||||
end
|
||||
if #eventListeners == 0 then
|
||||
continue
|
||||
end
|
||||
for _, connection in eventListeners do
|
||||
if connection.i ~= remote then
|
||||
continue
|
||||
end
|
||||
Thread(connection.c, player, table.unpack(content))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local contents = Buffer.readEvents(b, ref, eventSchemas)
|
||||
for _, content in contents do
|
||||
local remote = content[1]
|
||||
local content = content[2]
|
||||
if handleInvokes then
|
||||
if remote == 1 then
|
||||
local id = content[1]
|
||||
local results = content[2]
|
||||
local pending = pendingInvokes[id]
|
||||
if pending then
|
||||
task.spawn(pending :: any, table.unpack(results))
|
||||
pendingInvokes[id] = nil
|
||||
end
|
||||
continue
|
||||
end
|
||||
if remote == 0 then
|
||||
if #eventListeners == 0 then
|
||||
continue
|
||||
end
|
||||
local remoteName = content[1]
|
||||
local id = content[2]
|
||||
local args = content[3]
|
||||
for _, connection in eventListeners do
|
||||
if connection.i == remoteName then
|
||||
Thread(function()
|
||||
local results = { connection.c(player, table.unpack(args)) }
|
||||
if not queueEvent[player] then
|
||||
queueEvent[player] = {} :: any
|
||||
end
|
||||
table.insert(queueEvent[player], {
|
||||
0,
|
||||
{ id, results } :: any,
|
||||
})
|
||||
end)
|
||||
break
|
||||
end
|
||||
end
|
||||
continue
|
||||
end
|
||||
end
|
||||
if #eventListeners == 0 then
|
||||
continue
|
||||
end
|
||||
for _, connection in eventListeners do
|
||||
if connection.i ~= remote then
|
||||
continue
|
||||
end
|
||||
Thread(connection.c, player, table.unpack(content))
|
||||
end
|
||||
end
|
||||
end
|
||||
Event.OnServerEvent:Connect(function(player: Player, b: buffer, ref: { Instance }?)
|
||||
processIncoming(player, b, ref, true)
|
||||
end)
|
||||
|
||||
Event.OnServerEvent:Connect(function(player: Player, b: buffer, ref: { Instance }?)
|
||||
processIncoming(player, b, ref, true)
|
||||
end)
|
||||
UnreliableEvent.OnServerEvent:Connect(function(player: Player, b: buffer, ref: { Instance }?)
|
||||
processIncoming(player, b, ref, false)
|
||||
end)
|
||||
|
||||
UnreliableEvent.OnServerEvent:Connect(function(player: Player, b: buffer, ref: { Instance }?)
|
||||
processIncoming(player, b, ref, false)
|
||||
end)
|
||||
RunService.PostSimulation:Connect(function(d: number)
|
||||
deltaT += d
|
||||
if deltaT < cycle then
|
||||
return
|
||||
end
|
||||
deltaT = 0
|
||||
|
||||
RunService.PostSimulation:Connect(function(d: number)
|
||||
deltaT += d
|
||||
if deltaT < cycle then
|
||||
return
|
||||
end
|
||||
deltaT = 0
|
||||
-- reliable
|
||||
for player: Player, content in queueEvent do
|
||||
if #content == 0 or player.Parent ~= Players then
|
||||
continue
|
||||
end
|
||||
Buffer.writeEvents(writer, content, eventSchemas)
|
||||
do
|
||||
local buf, ref = Buffer.buildWithRefs(writer)
|
||||
Buffer.reset(writer)
|
||||
if not ref or #ref == 0 then
|
||||
Event:FireClient(player, buf)
|
||||
else
|
||||
Event:FireClient(player, buf, ref)
|
||||
end
|
||||
end
|
||||
player_bytes[player] = 0
|
||||
table.clear(queueEvent[player])
|
||||
end
|
||||
-- unreliable
|
||||
for player: Player, content in queueUnreliableEvent do
|
||||
if #content == 0 or player.Parent ~= Players then
|
||||
continue
|
||||
end
|
||||
Buffer.writeEvents(writer, content, eventSchemas)
|
||||
do
|
||||
local buf, ref = Buffer.buildWithRefs(writer)
|
||||
Buffer.reset(writer)
|
||||
if not ref or #ref == 0 then
|
||||
UnreliableEvent:FireClient(player, buf)
|
||||
else
|
||||
UnreliableEvent:FireClient(player, buf, ref)
|
||||
end
|
||||
end
|
||||
table.clear(player_bytes)
|
||||
table.clear(queueUnreliableEvent[player])
|
||||
end
|
||||
end)
|
||||
|
||||
-- reliable
|
||||
for player: Player, content in queueEvent do
|
||||
if #content == 0 or player.Parent ~= Players then
|
||||
continue
|
||||
end
|
||||
Buffer.writeEvents(writer, content, eventSchemas)
|
||||
do
|
||||
local buf, ref = Buffer.buildWithRefs(writer)
|
||||
Buffer.reset(writer)
|
||||
if not ref or #ref == 0 then
|
||||
Event:FireClient(player, buf)
|
||||
else
|
||||
Event:FireClient(player, buf, ref)
|
||||
end
|
||||
end
|
||||
player_bytes[player] = 0
|
||||
table.clear(queueEvent[player])
|
||||
end
|
||||
-- unreliable
|
||||
for player: Player, content in queueUnreliableEvent do
|
||||
if #content == 0 or player.Parent ~= Players then
|
||||
continue
|
||||
end
|
||||
Buffer.writeEvents(writer, content, eventSchemas)
|
||||
do
|
||||
local buf, ref = Buffer.buildWithRefs(writer)
|
||||
Buffer.reset(writer)
|
||||
if not ref or #ref == 0 then
|
||||
UnreliableEvent:FireClient(player, buf)
|
||||
else
|
||||
UnreliableEvent:FireClient(player, buf, ref)
|
||||
end
|
||||
end
|
||||
table.clear(player_bytes)
|
||||
table.clear(queueUnreliableEvent[player])
|
||||
end
|
||||
end)
|
||||
|
||||
local function onAdded(player: Player)
|
||||
if not table.find(players_ready, player) then
|
||||
table.insert(players_ready, player)
|
||||
end
|
||||
if not queueEvent[player] then
|
||||
queueEvent[player] = {} :: any
|
||||
end
|
||||
if not queueUnreliableEvent[player] then
|
||||
queueUnreliableEvent[player] = {} :: any
|
||||
end
|
||||
end
|
||||
Players.PlayerAdded:Connect(onAdded)
|
||||
Players.PlayerRemoving:Connect(function(player: Player)
|
||||
Replication.remove(player)
|
||||
table.remove(players_ready, table.find(players_ready, player))
|
||||
if queueEvent[player] then
|
||||
table.clear(queueEvent[player])
|
||||
queueEvent[player] = nil
|
||||
end
|
||||
if player_bytes[player] then
|
||||
player_bytes[player] = nil
|
||||
end
|
||||
if queueUnreliableEvent[player] then
|
||||
table.clear(queueUnreliableEvent[player])
|
||||
queueUnreliableEvent[player] = nil
|
||||
end
|
||||
end)
|
||||
for _, player: Player in ipairs(Players:GetPlayers()) do
|
||||
onAdded(player)
|
||||
end
|
||||
local function onAdded(player: Player)
|
||||
if not table.find(players_ready, player) then
|
||||
table.insert(players_ready, player)
|
||||
end
|
||||
if not queueEvent[player] then
|
||||
queueEvent[player] = {} :: any
|
||||
end
|
||||
if not queueUnreliableEvent[player] then
|
||||
queueUnreliableEvent[player] = {} :: any
|
||||
end
|
||||
end
|
||||
Players.PlayerAdded:Connect(onAdded)
|
||||
Players.PlayerRemoving:Connect(function(player: Player)
|
||||
Replication.remove(player)
|
||||
table.remove(players_ready, table.find(players_ready, player))
|
||||
if queueEvent[player] then
|
||||
table.clear(queueEvent[player])
|
||||
queueEvent[player] = nil
|
||||
end
|
||||
if player_bytes[player] then
|
||||
player_bytes[player] = nil
|
||||
end
|
||||
if queueUnreliableEvent[player] then
|
||||
table.clear(queueUnreliableEvent[player])
|
||||
queueUnreliableEvent[player] = nil
|
||||
end
|
||||
end)
|
||||
for _, player: Player in ipairs(Players:GetPlayers()) do
|
||||
onAdded(player)
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
|
|
|
|||
Loading…
Reference in a new issue