mirror of
https://github.com/imezx/Warp.git
synced 2026-06-17 12:27:20 +00:00
rewrite(phase5): add docstring
This commit is contained in:
parent
85f6aa8b77
commit
c4a7934bc4
3 changed files with 459 additions and 404 deletions
|
|
@ -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 } } = {}
|
||||||
|
|
@ -29,214 +29,237 @@ local eventSchemas: { [number]: Buffer.SchemaType } = {}
|
||||||
local pendingInvokes: { [string]: thread } = {}
|
local pendingInvokes: { [string]: thread } = {}
|
||||||
local invokeId = 0
|
local invokeId = 0
|
||||||
|
|
||||||
|
--@optional
|
||||||
|
-- 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.
|
||||||
Client.awaitReady = Replication.wait_for_ready
|
Client.awaitReady = Replication.wait_for_ready
|
||||||
|
|
||||||
|
--@remoteName string
|
||||||
|
--@schema Buffer.SchemaType
|
||||||
|
-- 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
|
||||||
|
--@fn function
|
||||||
|
-- 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
|
||||||
|
--@fn function
|
||||||
|
-- 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
|
||||||
|
-- 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
|
||||||
|
-- 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
|
||||||
|
-- Disconnect all connections and remove the event.
|
||||||
Client.Destroy = Client.DisconnectAll
|
Client.Destroy = Client.DisconnectAll
|
||||||
|
|
||||||
|
--@remoteName string
|
||||||
|
--@reliable boolean
|
||||||
|
-- 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
|
||||||
|
--@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?
|
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
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
@class Client
|
@class Client
|
||||||
@schema
|
@schema
|
||||||
define a schema for your data and use a strict packing
|
define a schema for your data and use a strict packing
|
||||||
]]
|
]]
|
||||||
Client.Schema = Buffer.Schema
|
Client.Schema = Buffer.Schema
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,6 @@ if RunService:IsClient() or RunService:IsRunMode() then
|
||||||
if not obj then
|
if not obj then
|
||||||
warn(`[Replication] timeout: could not find identifier '{name}' after {timeout}s.`)
|
warn(`[Replication] timeout: could not find identifier '{name}' after {timeout}s.`)
|
||||||
end
|
end
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 } = {}
|
||||||
|
|
@ -37,267 +37,300 @@ local players_ready: { Player }, player_bytes: { [Player]: number } = {}, {}
|
||||||
local pendingInvokes: { [string]: thread } = {}
|
local pendingInvokes: { [string]: thread } = {}
|
||||||
local invokeId = 0
|
local invokeId = 0
|
||||||
|
|
||||||
|
--@namespaces { string }
|
||||||
|
--@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 })
|
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
|
||||||
|
--@schema Buffer.SchemaType
|
||||||
|
-- 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
|
||||||
|
--@fn function
|
||||||
|
-- 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
|
||||||
|
--@fn function
|
||||||
|
-- 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
|
||||||
|
-- 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
|
||||||
|
-- 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
|
||||||
|
-- Disconnect all connections and remove the event.
|
||||||
Server.Destroy = Server.DisconnectAll
|
Server.Destroy = Server.DisconnectAll
|
||||||
|
|
||||||
|
--@remoteName string
|
||||||
|
--@reliable boolean
|
||||||
|
--@player 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
|
||||||
|
--@reliable boolean
|
||||||
|
-- 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
|
||||||
|
--@reliable boolean
|
||||||
|
--@except { Player }
|
||||||
|
-- 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
|
||||||
|
--@player Player
|
||||||
|
--@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?
|
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)
|
local bytes: number = (player_bytes[player] or 0) + math.max(buffer.len(b), 800)
|
||||||
if bytes > 8e3 then return end
|
if bytes > 8e3 then return end
|
||||||
player_bytes[player] = bytes
|
player_bytes[player] = bytes
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
@class Server
|
@class Server
|
||||||
@schema
|
@schema
|
||||||
define a schema for your data and use a strict packing
|
define a schema for your data and use a strict packing
|
||||||
]]
|
]]
|
||||||
Server.Schema = Buffer.Schema
|
Server.Schema = Buffer.Schema
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue