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