chore: fix client & server processing for invoke

This commit is contained in:
Khietsly Tristan 2026-04-22 08:40:17 +07:00
parent 80646eeebb
commit b425db5f0b
3 changed files with 102 additions and 102 deletions

View file

@ -3,10 +3,12 @@
--@EternityDev --@EternityDev
local Client = {} local Client = {}
local RunService = game:GetService("RunService")
local Thread = require("./Util/Thread") local Thread = require("./Util/Thread")
local Buffer = require("./Util/Buffer") local Buffer = require("./Util/Buffer")
local Replication = require("./Replication") local Replication = require("./Replication")
local Xor = require("./Util/Xor")
local RunService = game:GetService("RunService")
local Event: RemoteEvent = script.Parent:WaitForChild("Event") local Event: RemoteEvent = script.Parent:WaitForChild("Event")
local UnreliableEvent: UnreliableRemoteEvent = script.Parent:WaitForChild("UnreliableEvent") local UnreliableEvent: UnreliableRemoteEvent = script.Parent:WaitForChild("UnreliableEvent")
local deltaT: number, cycle: number = 0, 1 / 61 local deltaT: number, cycle: number = 0, 1 / 61
@ -23,9 +25,10 @@ type Event = {
local queueEvent: { { any } } = {} local queueEvent: { { any } } = {}
local queueUnreliableEvent: { { any } } = {} local queueUnreliableEvent: { { any } } = {}
local eventListeners: { Event } = {} local eventListeners: { [number]: { Event } } = {}
local eventSchemas: { [number]: Buffer.SchemaType } = {} local eventSchemas: { [number]: Buffer.SchemaType } = {}
local lastDelta: { [number]: { any } } = {}
local pendingInvokes: { [string]: thread } = {} local pendingInvokes: { [string]: thread } = {}
local invokeId = 0 local invokeId = 0
@ -52,13 +55,21 @@ Client.Connect = function(remoteName: string, fn: (Player, ...any?) -> ...any?):
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) if not eventListeners[id] then
eventListeners[id] = {}
end
table.insert(eventListeners[id], detail)
return { return {
Connected = true, Connected = true,
Disconnect = function(self: Connection) Disconnect = function(self: Connection)
@ -66,9 +77,12 @@ Client.Connect = function(remoteName: string, fn: (Player, ...any?) -> ...any?):
return return
end end
self.Connected = false self.Connected = false
local idx = table.find(eventListeners, detail) local bucket = eventListeners[detail.i]
if bucket then
local idx = table.find(bucket, detail)
if idx then if idx then
table.remove(eventListeners, idx) table.remove(bucket, idx)
end
end end
end, end,
} :: Connection } :: Connection
@ -105,11 +119,7 @@ Client.DisconnectAll = function(remoteName: string)
if not id then if not id then
return return
end end
for idx = #eventListeners, 1, -1 do eventListeners[id] = nil
if eventListeners[idx].i == id then
table.remove(eventListeners, idx)
end
end
end end
--@remoteName string --@remoteName string
@ -158,17 +168,16 @@ 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 end
return local decoded: buffer = handleInvokes and Xor.decodeClient(b) or b
end local contents = Buffer.readEvents(decoded, 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 args = content[2]
if handleInvokes then if handleInvokes then
if remote == 0 then if remote == 0 then
local id = content[1] local id = args[1]
local results = content[2] local results = args[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))
@ -177,35 +186,22 @@ if RunService:IsClient() then
continue continue
end end
if remote == 1 then if remote == 1 then
if #eventListeners == 0 then local remoteName, id, rargs = args[1], args[2], args[3]
continue local connections = eventListeners[remoteName]
end if connections and #connections > 0 then
local remoteName = content[1]
local id = content[2]
local args = content[3]
for _, connection in eventListeners do
if connection.i == remoteName then
Thread(function() Thread(function()
local results = { connection.c(table.unpack(args)) } local results = { connections[1].c(table.unpack(rargs)) }
table.insert(queueEvent, { table.insert(queueEvent, { 1, { id, results } :: any })
1,
{ id, results } :: any,
})
end) end)
break
end
end end
continue continue
end end
end end
if #eventListeners == 0 then local connections = eventListeners[remote]
continue if connections then
for _, connection in connections do
Thread(connection.c, table.unpack(args))
end end
for _, connection in eventListeners do
if connection.i ~= remote then
continue
end
Thread(connection.c, table.unpack(content))
end end
end end
end end
@ -220,9 +216,7 @@ if RunService:IsClient() then
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 end
return
end
deltaT = 0 deltaT = 0
--reliable --reliable
@ -230,11 +224,12 @@ if RunService:IsClient() 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)
local encoded = Xor.encodeClient(buf)
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(encoded)
else else
Event:FireServer(buf, ref) Event:FireServer(encoded, ref)
end end
end end
table.clear(queueEvent) table.clear(queueEvent)

View file

@ -55,7 +55,6 @@ if RunService:IsClient() or RunService:IsRunMode() then
-- wait for the identifiers to be replicated from the server -- wait for the identifiers to be replicated from the server
Replication.wait_for_ready = function() Replication.wait_for_ready = function()
if is_ready then return end if is_ready then return end
local thread = coroutine.running() local thread = coroutine.running()
table.insert(ready_yields, thread) table.insert(ready_yields, thread)
coroutine.yield() coroutine.yield()

View file

@ -3,12 +3,14 @@
--@EternityDev --@EternityDev
local Server = {} local Server = {}
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Thread = require("./Util/Thread") local Thread = require("./Util/Thread")
local Buffer = require("./Util/Buffer") local Buffer = require("./Util/Buffer")
local Identifier = require("./Util/Identifier") local Identifier = require("./Util/Identifier")
local Replication = require("./Replication") local Replication = require("./Replication")
local Xor = require("./Util/Xor")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Event: RemoteEvent = script.Parent:WaitForChild("Event") local Event: RemoteEvent = script.Parent:WaitForChild("Event")
local _repl: RemoteEvent = script.Parent:WaitForChild("_repl") local _repl: RemoteEvent = script.Parent:WaitForChild("_repl")
local UnreliableEvent: UnreliableRemoteEvent = script.Parent:WaitForChild("UnreliableEvent") local UnreliableEvent: UnreliableRemoteEvent = script.Parent:WaitForChild("UnreliableEvent")
@ -30,7 +32,7 @@ local queueEvent: {
local queueUnreliableEvent: { local queueUnreliableEvent: {
[Player]: { { any } }, [Player]: { { any } },
} = {} } = {}
local eventListeners: { Event } = {} local eventListeners: { [number]: { Event } } = {}
local eventSchemas: { [number]: Buffer.SchemaType } = {} local eventSchemas: { [number]: Buffer.SchemaType } = {}
local players_ready: { Player }, player_bytes: { [Player]: number } = {}, {} local players_ready: { Player }, player_bytes: { [Player]: number } = {}, {}
@ -61,7 +63,10 @@ Server.Connect = function(remoteName: string, fn: (Player, ...any?) -> ...any?):
i = Identifier.get_id(remoteName), i = Identifier.get_id(remoteName),
c = fn, c = fn,
} }
table.insert(eventListeners, detail) if not eventListeners[detail.i] then
eventListeners[detail.i] = {}
end
table.insert(eventListeners[detail.i], detail)
return { return {
Connected = true, Connected = true,
Disconnect = function(self: Connection) Disconnect = function(self: Connection)
@ -69,9 +74,12 @@ Server.Connect = function(remoteName: string, fn: (Player, ...any?) -> ...any?):
return return
end end
self.Connected = false self.Connected = false
local idx = table.find(eventListeners, detail) local bucket = eventListeners[detail.i]
if bucket then
local idx = table.find(bucket, detail)
if idx then if idx then
table.remove(eventListeners, idx) table.remove(bucket, idx)
end
end end
end, end,
} :: Connection } :: Connection
@ -108,11 +116,7 @@ Server.DisconnectAll = function(remoteName: string)
if not id then if not id then
return return
end end
for idx = #eventListeners, 1, -1 do eventListeners[id] = nil
if eventListeners[idx].i == id then
table.remove(eventListeners, idx)
end
end
end end
--@remoteName string --@remoteName string
@ -149,7 +153,9 @@ end
-- 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
@ -188,18 +194,21 @@ if RunService:IsServer() then
end end
if not RunService:IsStudio() then if not RunService:IsStudio() then
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
end end
local contents = Buffer.readEvents(b, ref, eventSchemas) local decoded = handleInvokes and Xor.decodeServer(player, b) or b
local contents = Buffer.readEvents(decoded, 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 d = content[2]
if handleInvokes then if handleInvokes then
if remote == 1 then if remote == 1 then
local id = content[1] local id = d[1]
local results = content[2] local results = d[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))
@ -208,16 +217,16 @@ if RunService:IsServer() then
continue continue
end end
if remote == 0 then if remote == 0 then
if #eventListeners == 0 then if not next(eventListeners) then
continue continue
end end
local remoteName = content[1] local remoteName = d[1]
local id = content[2] local id = d[2]
local args = content[3] local args = d[3]
for _, connection in eventListeners do local connections = eventListeners[remoteName]
if connection.i == remoteName then if connections and #connections > 0 then
Thread(function() Thread(function()
local results = { connection.c(player, table.unpack(args)) } local results = { connections[1].c(player, table.unpack(args)) }
if not queueEvent[player] then if not queueEvent[player] then
queueEvent[player] = {} :: any queueEvent[player] = {} :: any
end end
@ -226,20 +235,15 @@ if RunService:IsServer() then
{ id, results } :: any, { id, results } :: any,
}) })
end) end)
break
end
end end
continue continue
end end
end end
if #eventListeners == 0 then local connections = eventListeners[remote]
continue if connections then
for _, connection in connections do
Thread(connection.c, player, table.unpack(d))
end end
for _, connection in eventListeners do
if connection.i ~= remote then
continue
end
Thread(connection.c, player, table.unpack(content))
end end
end end
end end
@ -267,11 +271,12 @@ if RunService:IsServer() then
Buffer.writeEvents(writer, content, eventSchemas) Buffer.writeEvents(writer, content, eventSchemas)
do do
local buf, ref = Buffer.buildWithRefs(writer) local buf, ref = Buffer.buildWithRefs(writer)
local encoded = Xor.encodeServer(player, buf)
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, encoded)
else else
Event:FireClient(player, buf, ref) Event:FireClient(player, encoded, ref)
end end
end end
player_bytes[player] = 0 player_bytes[player] = 0
@ -292,7 +297,7 @@ if RunService:IsServer() then
UnreliableEvent:FireClient(player, buf, ref) UnreliableEvent:FireClient(player, buf, ref)
end end
end end
table.clear(player_bytes) player_bytes[player] = 0
table.clear(queueUnreliableEvent[player]) table.clear(queueUnreliableEvent[player])
end end
end) end)
@ -323,6 +328,7 @@ if RunService:IsServer() then
table.clear(queueUnreliableEvent[player]) table.clear(queueUnreliableEvent[player])
queueUnreliableEvent[player] = nil queueUnreliableEvent[player] = nil
end end
Xor.remove(player)
end) end)
for _, player: Player in ipairs(Players:GetPlayers()) do for _, player: Player in ipairs(Players:GetPlayers()) do
onAdded(player) onAdded(player)