mirror of
https://github.com/imezx/Warp.git
synced 2026-06-02 12:18:32 +00:00
183 lines
5 KiB
Text
183 lines
5 KiB
Text
--!optimize 2
|
|
--!strict
|
|
local Replication = {}
|
|
|
|
local RunService = game:GetService("RunService")
|
|
local _repl: RemoteEvent = script.Parent:WaitForChild("_repl")
|
|
|
|
local Buffer = require("./Util/Buffer")
|
|
local Identifier = require("./Util/Identifier")
|
|
|
|
local identifiers_schema = Buffer.Schema.string
|
|
local warp_identifier_registry = shared.__warp_identifier_registry
|
|
|
|
if RunService:IsClient() or RunService:IsRunMode() then
|
|
local pending_id_yields, pending_name_yields, ready_yields = {}, {}, {}
|
|
local is_ready = false
|
|
|
|
if RunService:IsClient() then
|
|
_repl.OnClientEvent:Connect(function(b: buffer)
|
|
if type(b) ~= "buffer" then
|
|
return
|
|
end
|
|
local contents = Buffer.readRepl(b, identifiers_schema)
|
|
if #contents == 0 then return end
|
|
for _, content in contents do
|
|
local id, remote = content[1], content[2]
|
|
warp_identifier_registry.cache[remote] = id
|
|
warp_identifier_registry.name[id] = remote
|
|
|
|
if pending_id_yields[remote] then
|
|
for _, thread in pending_id_yields[remote] do
|
|
task.spawn(thread, id)
|
|
end
|
|
pending_id_yields[remote] = nil
|
|
end
|
|
if pending_name_yields[id] then
|
|
for _, thread in pending_name_yields[id] do
|
|
task.spawn(thread, remote)
|
|
end
|
|
pending_name_yields[id] = nil
|
|
end
|
|
end
|
|
if not is_ready then
|
|
is_ready = true
|
|
for _, thread in ready_yields do
|
|
task.spawn(thread :: thread)
|
|
end
|
|
table.clear(ready_yields)
|
|
end
|
|
end)
|
|
_repl:FireServer()
|
|
end
|
|
|
|
--@yield
|
|
-- wait for the identifiers to be replicated from the server
|
|
Replication.wait_for_ready = function()
|
|
if is_ready then return end
|
|
|
|
local thread = coroutine.running()
|
|
table.insert(ready_yields, thread)
|
|
coroutine.yield()
|
|
end
|
|
|
|
--@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()
|
|
local replication_id: number = Identifier.get_id("id_replication") or 1
|
|
local is_scheduled = false
|
|
|
|
if not Identifier.has_name("id_replication") or not replication_id then
|
|
replication_id = Identifier.get_id("id_replication") or 1
|
|
end
|
|
|
|
local function replicateToAll(content: any, id: number?)
|
|
if #replication_ready == 0 then return end
|
|
|
|
if type(content) == "string" and id then
|
|
pending_replications[content] = id
|
|
else
|
|
for k, v in content :: any do
|
|
pending_replications[k] = v
|
|
end
|
|
end
|
|
|
|
if not is_scheduled then
|
|
is_scheduled = true
|
|
task.defer(function()
|
|
is_scheduled = false
|
|
|
|
local count = 0
|
|
for _ in pending_replications do count += 1 end
|
|
if count == 0 then return end
|
|
|
|
Buffer.writeRepl(writer, pending_replications, count, identifiers_schema)
|
|
local buf = Buffer.build(writer)
|
|
Buffer.reset(writer)
|
|
|
|
for _, player: Player in replication_ready do
|
|
_repl:FireClient(player, buf)
|
|
end
|
|
|
|
table.clear(pending_replications)
|
|
end)
|
|
end
|
|
end
|
|
|
|
local function replicateTo(player: Player)
|
|
Buffer.writeRepl(writer, warp_identifier_registry.cache, warp_identifier_registry.counter, identifiers_schema)
|
|
do
|
|
local buf = Buffer.build(writer)
|
|
Buffer.reset(writer)
|
|
_repl:FireClient(player, buf)
|
|
end
|
|
end
|
|
|
|
Identifier.on_added(replicateToAll)
|
|
|
|
_repl.OnServerEvent:Connect(function(player: Player)
|
|
if table.find(replication_ready, player) then return end
|
|
table.insert(replication_ready, player)
|
|
replicateTo(player)
|
|
end)
|
|
|
|
Replication.remove = function(player: Player)
|
|
table.remove(replication_ready, table.find(replication_ready, player))
|
|
end
|
|
end
|
|
|
|
return Replication :: typeof(Replication)
|