mirror of
https://github.com/littensy/charm.git
synced 2026-10-21 03:36:11 +00:00
test: add all sync tests
This commit is contained in:
parent
0dbc0587a0
commit
5a05f01223
5 changed files with 492 additions and 53 deletions
96
packages/charm-sync/src/__tests__/e2e.spec.luau
Normal file
96
packages/charm-sync/src/__tests__/e2e.spec.luau
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
local Charm = require("../../Charm")
|
||||
local client = require("../client")
|
||||
local config = require("../config")
|
||||
local server = require("../server")
|
||||
local suite = require("../../../../test/suite")
|
||||
|
||||
local afterEach = suite.afterEach
|
||||
local test = suite.test
|
||||
|
||||
afterEach(function()
|
||||
server.reset()
|
||||
client.clearAtoms()
|
||||
config.preserveHistory = false
|
||||
end)
|
||||
|
||||
test("should replicate server state to client", function()
|
||||
local target = {}
|
||||
local serverA = Charm.atom({ count = 0 })
|
||||
local serverB = Charm.atom({ count = 0 })
|
||||
local clientA = Charm.atom({ count = -1 })
|
||||
local clientB = Charm.atom({ count = -1 })
|
||||
|
||||
client.registerAtoms({ a = clientA, b = clientB })
|
||||
server.syncAtomsToClient(target, { a = serverA, b = serverB })
|
||||
server.startSync(function(syncTarget, payloads)
|
||||
if syncTarget == target then
|
||||
client.applyPayloads(payloads)
|
||||
end
|
||||
end)
|
||||
|
||||
server.flush()
|
||||
assert(clientA().count == 0, "Client atom A should be initialized to 0")
|
||||
assert(clientB().count == 0, "Client atom B should be initialized to 0")
|
||||
|
||||
serverA({ count = 1 })
|
||||
serverB({ count = 1 })
|
||||
server.flush()
|
||||
assert(clientA().count == 1, "Client atom A should be updated to 1")
|
||||
assert(clientB().count == 1, "Client atom B should be updated to 1")
|
||||
|
||||
local prevClientB = clientB()
|
||||
serverA({ count = 2 })
|
||||
server.flush()
|
||||
assert(clientA().count == 2, "Client atom A should be updated to 2")
|
||||
assert(clientB() == prevClientB, "Client atom B should not be updated")
|
||||
end)
|
||||
|
||||
test("should replicate historical server state to client", function()
|
||||
local target = {}
|
||||
local serverA = Charm.atom({ count = 0 })
|
||||
local serverB = Charm.atom({ count = 0 })
|
||||
local clientA = Charm.atom({ count = -1 })
|
||||
local clientB = Charm.atom({ count = -1 })
|
||||
|
||||
config.preserveHistory = true
|
||||
|
||||
client.registerAtoms({ a = clientA, b = clientB })
|
||||
server.syncAtomsToClient(target, { a = serverA, b = serverB })
|
||||
server.startSync(function(syncTarget, payloads)
|
||||
if syncTarget == target then
|
||||
client.applyPayloads(payloads)
|
||||
end
|
||||
end)
|
||||
|
||||
local expectedA = { -1, 0, 1, 2, 3, 4 }
|
||||
local expectedB = { -1, 0, 1, 2 }
|
||||
|
||||
Charm.effect(function()
|
||||
local expected = table.remove(expectedA, 1)
|
||||
assert(expected == clientA().count, `Client atom A should be {expected}`)
|
||||
end)
|
||||
|
||||
Charm.effect(function()
|
||||
local expected = table.remove(expectedB, 1)
|
||||
assert(expected == clientB().count, `Client atom B should be {expected}`)
|
||||
end)
|
||||
|
||||
server.flush()
|
||||
|
||||
serverA({ count = 1 })
|
||||
serverA({ count = 2 })
|
||||
serverB({ count = 1 })
|
||||
serverB({ count = 2 })
|
||||
server.flush()
|
||||
assert(clientA().count == 2, "Client atom A should be updated to 2")
|
||||
assert(clientB().count == 2, "Client atom B should be updated to 2")
|
||||
|
||||
local prevClientB = clientB()
|
||||
serverA({ count = 3 })
|
||||
serverA({ count = 4 })
|
||||
server.flush()
|
||||
assert(clientA().count == 4, "Client atom A should be updated to 4")
|
||||
assert(clientB() == prevClientB, "Client atom B should not be updated")
|
||||
end)
|
||||
|
||||
return {}
|
||||
306
packages/charm-sync/src/__tests__/server.spec.luau
Normal file
306
packages/charm-sync/src/__tests__/server.spec.luau
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
local Charm = require("../../Charm")
|
||||
local config = require("../config")
|
||||
local patch = require("../patch")
|
||||
local server = require("../server")
|
||||
local suite = require("../../../../test/suite")
|
||||
|
||||
local afterEach = suite.afterEach
|
||||
local beforeEach = suite.beforeEach
|
||||
local describe = suite.describe
|
||||
local test = suite.test
|
||||
|
||||
afterEach(function()
|
||||
server.reset()
|
||||
end)
|
||||
|
||||
test("should replicate state changes", function()
|
||||
local client = {}
|
||||
local a = Charm.atom(0)
|
||||
local b = Charm.atom(0)
|
||||
local c = Charm.atom(0)
|
||||
local target, payloads
|
||||
|
||||
server.syncAtomsToClient(client, { a = a, b = b, c = c })
|
||||
server.startSync(function(...)
|
||||
target, payloads = ...
|
||||
end)
|
||||
|
||||
server.flush()
|
||||
assert(target == client, "Expected target to be the client")
|
||||
assert(payloads[1].type == "init", "Expected first payload to be init")
|
||||
assert(payloads[1].data.a == a(), "Expected init payload to contain atom 'a'")
|
||||
assert(payloads[1].data.b == b(), "Expected init payload to contain atom 'b'")
|
||||
assert(payloads[1].data.c == c(), "Expected init payload to contain atom 'c'")
|
||||
|
||||
a(1)
|
||||
b(2)
|
||||
server.flush()
|
||||
assert(target == client, "Expected target to be the client")
|
||||
assert(payloads[1].type == "patch", "Expected first payload to be patch")
|
||||
assert(payloads[1].data.a == a(), "Expected patch payload to contain atom 'a'")
|
||||
assert(payloads[1].data.b == b(), "Expected patch payload to contain atom 'b'")
|
||||
end)
|
||||
|
||||
test("should replicate partial state patches", function()
|
||||
local client = {}
|
||||
local a = Charm.atom({ x = 0, y = 0 })
|
||||
local b = Charm.atom(0 :: number?)
|
||||
local target, payloads
|
||||
|
||||
server.syncAtomsToClient(client, { a = a, b = b })
|
||||
server.startSync(function(...)
|
||||
target, payloads = ...
|
||||
end)
|
||||
|
||||
server.flush()
|
||||
assert(target == client, "Expected target to be the client")
|
||||
assert(payloads[1].type == "init", "Expected first payload to be init")
|
||||
assert(payloads[1].data.a.x == a().x, "Expected init payload to contain atom 'a.x'")
|
||||
assert(payloads[1].data.a.y == a().y, "Expected init payload to contain atom 'a.y'")
|
||||
assert(payloads[1].data.b == b(), "Expected init payload to contain atom 'b'")
|
||||
|
||||
a({ x = 1, y = 0 })
|
||||
b(nil)
|
||||
server.flush()
|
||||
assert(target == client, "Expected target to be the client")
|
||||
assert(payloads[1].type == "patch", "Expected first payload to be patch")
|
||||
assert(payloads[1].data.a.x == 1, "Expected patch payload to contain atom 'a.x'")
|
||||
assert(payloads[1].data.a.y == nil, "Expected patch payload to not contain atom 'a.y'")
|
||||
assert(patch.isNone(payloads[1].data.b), "Expected patch payload to contain atom 'b' as None")
|
||||
end)
|
||||
|
||||
test("should init new clients", function()
|
||||
local clientA = {}
|
||||
local clientB = {}
|
||||
local a = Charm.atom(0)
|
||||
local payloadsA, payloadsB
|
||||
|
||||
server.syncAtomsToClient(clientA, { a = a })
|
||||
server.startSync(function(target, payloads)
|
||||
if target == clientA then
|
||||
payloadsA = payloads
|
||||
elseif target == clientB then
|
||||
payloadsB = payloads
|
||||
end
|
||||
end)
|
||||
|
||||
a(1)
|
||||
server.flush()
|
||||
assert(payloadsA[1].type == "init", "Expected first payload to be init for client A")
|
||||
assert(payloadsA[1].data.a == a(), "Expected init payload to contain atom 'a' for client A")
|
||||
|
||||
server.syncAtomsToClient(clientB, { a = a })
|
||||
a(2)
|
||||
server.flush()
|
||||
assert(payloadsA[1].type == "patch", "Expected first payload to be patch for client A")
|
||||
assert(payloadsA[1].data.a == a(), "Expected patch payload to contain atom 'a' for client A")
|
||||
assert(payloadsB[1].type == "init", "Expected first payload to be init for client B")
|
||||
assert(payloadsB[1].data.a == a(), "Expected init payload to contain atom 'a' for client B")
|
||||
end)
|
||||
|
||||
test("should init new atoms", function()
|
||||
local client = {}
|
||||
local a = Charm.atom(0)
|
||||
local b = Charm.atom(0)
|
||||
local target, payloads
|
||||
|
||||
server.syncAtomsToClient(client, { a = a })
|
||||
server.startSync(function(...)
|
||||
target, payloads = ...
|
||||
end)
|
||||
|
||||
server.flush()
|
||||
server.syncAtomsToClient(client, { b = b })
|
||||
a(1)
|
||||
b(1)
|
||||
server.flush()
|
||||
assert(target == client, "Expected target to be the client")
|
||||
assert(payloads[1].type == "init", "Expected first payload to be init")
|
||||
assert(payloads[1].data.b == b(), "Expected init payload to contain atom 'b'")
|
||||
assert(payloads[2].type == "patch", "Expected second payload to be patch")
|
||||
assert(payloads[2].data.a == a(), "Expected patch payload to contain atom 'a'")
|
||||
assert(payloads[2].data.b == nil, "Expected patch payload to not contain atom 'b'")
|
||||
end)
|
||||
|
||||
test("should only replicate to synced clients", function()
|
||||
local clientA = {}
|
||||
local clientB = {}
|
||||
local a = Charm.atom(0)
|
||||
local b = Charm.atom(0)
|
||||
local payloadsA, payloadsB
|
||||
|
||||
server.syncAtomsToClient(clientA, { a = a })
|
||||
server.syncAtomsToClient(clientB, { b = b })
|
||||
server.startSync(function(target, payloads)
|
||||
if target == clientA then
|
||||
payloadsA = payloads
|
||||
elseif target == clientB then
|
||||
payloadsB = payloads
|
||||
end
|
||||
end)
|
||||
|
||||
server.flush()
|
||||
assert(payloadsA[1].type == "init", "Expected first payload to be init for client A")
|
||||
assert(payloadsA[1].data.a == a(), "Expected init payload to contain atom 'a' for client A")
|
||||
assert(payloadsA[1].data.b == nil, "Expected init payload to not contain atom 'b' for client A")
|
||||
assert(payloadsB[1].type == "init", "Expected first payload to be init for client B")
|
||||
assert(payloadsB[1].data.b == b(), "Expected init payload to contain atom 'b' for client B")
|
||||
assert(payloadsB[1].data.a == nil, "Expected init payload to not contain atom 'a' for client B")
|
||||
|
||||
payloadsA = {}
|
||||
payloadsB = {}
|
||||
a(1)
|
||||
server.flush()
|
||||
assert(payloadsA[1].type == "patch", "Expected first payload to be patch for client A")
|
||||
assert(payloadsA[1].data.a == a(), "Expected patch payload to contain atom 'a' for client A")
|
||||
assert(payloadsA[1].data.b == nil, "Expected patch payload to not contain atom 'b' for client A")
|
||||
assert(#payloadsB == 0, "Expected no payloads for client B")
|
||||
|
||||
payloadsA = {}
|
||||
payloadsB = {}
|
||||
b(1)
|
||||
server.flush()
|
||||
assert(#payloadsA == 0, "Expected no payloads for client A")
|
||||
assert(payloadsB[1].type == "patch", "Expected first payload to be patch for client B")
|
||||
assert(payloadsB[1].data.b == b(), "Expected patch payload to contain atom 'b' for client B")
|
||||
assert(payloadsB[1].data.a == nil, "Expected patch payload to not contain atom 'a' for client B")
|
||||
end)
|
||||
|
||||
test("should validate state", function()
|
||||
local client = {}
|
||||
local a = Charm.atom({})
|
||||
|
||||
server.syncAtomsToClient(client, { a = a })
|
||||
server.startSync(function() end)
|
||||
|
||||
a({ [0.5] = true })
|
||||
assert(not pcall(server.flush), "Expected server.flush to fail")
|
||||
|
||||
a({ [0.5] = true, valid = true })
|
||||
server.flush()
|
||||
end)
|
||||
|
||||
test("should serialize state", function()
|
||||
local client = {}
|
||||
local a = Charm.atom({ 1, 2, 3, 4, 5 })
|
||||
local payloads
|
||||
|
||||
server.syncAtomsToClient(client, { a = a })
|
||||
server.startSync(function(_, p)
|
||||
payloads = p
|
||||
end)
|
||||
server.flush()
|
||||
|
||||
a({ 1, 2, 0, 4 })
|
||||
server.flush()
|
||||
assert(payloads[1].data.a["3"] == 0, "Expected index 3 to be serialized as string key")
|
||||
assert(patch.isNone(payloads[1].data.a["5"]), "Expected index 5 to be serialized as string key")
|
||||
end)
|
||||
|
||||
describe("historical", function()
|
||||
beforeEach(function()
|
||||
config.preserveHistory = true
|
||||
end)
|
||||
|
||||
afterEach(function()
|
||||
config.preserveHistory = false
|
||||
end)
|
||||
|
||||
test("should replicate granular state changes", function()
|
||||
local client = {}
|
||||
local a = Charm.atom(0)
|
||||
local b = Charm.atom(0)
|
||||
local c = Charm.atom(0)
|
||||
local target, payloads
|
||||
|
||||
server.syncAtomsToClient(client, { a = a, b = b, c = c })
|
||||
server.startSync(function(...)
|
||||
target, payloads = ...
|
||||
end)
|
||||
|
||||
server.flush()
|
||||
assert(target == client, "Expected target to be the client")
|
||||
assert(payloads[1].type == "init", "Expected first payload to be init")
|
||||
assert(payloads[1].data.a == 0, "Expected init payload to contain atom 'a'")
|
||||
assert(payloads[1].data.b == 0, "Expected init payload to contain atom 'b'")
|
||||
assert(payloads[1].data.c == 0, "Expected init payload to contain atom 'c'")
|
||||
|
||||
a(1)
|
||||
a(2)
|
||||
b(2)
|
||||
server.flush()
|
||||
assert(target == client, "Expected target to be the client")
|
||||
assert(payloads[1].type == "patch", "Expected first payload to be patch")
|
||||
assert(payloads[1].data.a == 1, "Expected patch payload to contain atom 'a'")
|
||||
assert(payloads[2].data.a == 2, "Expected patch payload to contain atom 'a'")
|
||||
assert(payloads[3].data.b == 2, "Expected patch payload to contain atom 'b'")
|
||||
end)
|
||||
|
||||
test("should init new clients", function()
|
||||
local clientA = {}
|
||||
local clientB = {}
|
||||
local a = Charm.atom(0)
|
||||
local payloadsA, payloadsB
|
||||
|
||||
server.syncAtomsToClient(clientA, { a = a })
|
||||
server.startSync(function(target, payloads)
|
||||
if target == clientA then
|
||||
payloadsA = payloads
|
||||
elseif target == clientB then
|
||||
payloadsB = payloads
|
||||
end
|
||||
end)
|
||||
|
||||
a(1)
|
||||
a(2)
|
||||
server.flush()
|
||||
assert(payloadsA[1].type == "init", "Expected first payload to be init for client A")
|
||||
assert(payloadsA[1].data.a == 1, "Expected init payload to contain atom 'a' for client A")
|
||||
assert(payloadsA[2].type == "patch", "Expected second payload to be patch for client A")
|
||||
assert(payloadsA[2].data.a == 2, "Expected second patch payload to contain atom 'a' with value 1 for client A")
|
||||
|
||||
server.syncAtomsToClient(clientB, { a = a })
|
||||
a(3)
|
||||
a(4)
|
||||
server.flush()
|
||||
assert(payloadsA[1].type == "patch", "Expected first payload to be patch for client A")
|
||||
assert(payloadsA[1].data.a == 3, "Expected patch payload to contain atom 'a' for client A")
|
||||
assert(payloadsA[2].data.a == 4, "Expected patch payload to contain atom 'a' for client A")
|
||||
assert(payloadsB[1].type == "init", "Expected first payload to be init for client B")
|
||||
assert(payloadsB[1].data.a == 3, "Expected init payload to contain atom 'a' for client B")
|
||||
assert(payloadsB[2].type == "patch", "Expected second payload to be patch for client B")
|
||||
assert(payloadsB[2].data.a == 4, "Expected second patch payload to contain atom 'a' with value 3 for client B")
|
||||
end)
|
||||
|
||||
test("should init new atoms", function()
|
||||
local client = {}
|
||||
local a = Charm.atom(0)
|
||||
local b = Charm.atom(0)
|
||||
local payloads
|
||||
|
||||
server.syncAtomsToClient(client, { a = a })
|
||||
server.startSync(function(_, ...)
|
||||
payloads = ...
|
||||
end)
|
||||
|
||||
server.flush()
|
||||
assert(payloads[1].type == "init", "Expected first payload to be init")
|
||||
assert(payloads[1].data.a == 0, "Expected init payload to contain atom 'a'")
|
||||
|
||||
server.syncAtomsToClient(client, { b = b })
|
||||
a(1)
|
||||
a(2)
|
||||
b(1)
|
||||
b(2)
|
||||
server.flush()
|
||||
assert(payloads[1].type == "patch", "Expected first payload to be patch")
|
||||
assert(payloads[1].data.a == 1, "Expected patch payload to contain atom 'a'")
|
||||
assert(payloads[2].data.a == 2, "Expected patch payload to contain atom 'a'")
|
||||
assert(payloads[3].type == "init", "Expected third payload to be init")
|
||||
assert(payloads[3].data.b == 1, "Expected init payload to contain atom 'b'")
|
||||
assert(payloads[4].type == "patch", "Expected fourth payload to be patch")
|
||||
assert(payloads[4].data.b == 2, "Expected patch payload to contain atom 'b'")
|
||||
end)
|
||||
end)
|
||||
|
||||
return {}
|
||||
|
|
@ -4,6 +4,10 @@ suite.describe("client", function()
|
|||
require("./client.spec")
|
||||
end)
|
||||
|
||||
suite.describe("e2e", function()
|
||||
require("./e2e.spec")
|
||||
end)
|
||||
|
||||
suite.describe("flatten", function()
|
||||
require("./flatten.spec")
|
||||
end)
|
||||
|
|
@ -12,6 +16,10 @@ suite.describe("patch", function()
|
|||
require("./patch.spec")
|
||||
end)
|
||||
|
||||
suite.describe("server", function()
|
||||
require("./server.spec")
|
||||
end)
|
||||
|
||||
suite.describe("validate", function()
|
||||
require("./validate.spec")
|
||||
end)
|
||||
|
|
|
|||
|
|
@ -54,19 +54,19 @@ end
|
|||
--- @param prevState The previous state.
|
||||
--- @param nextState The next state.
|
||||
--- @returns A patch that can be applied to the previous state.
|
||||
local function diff(prevState: { [any]: any }, nextState: { [any]: any })
|
||||
local patches = table.clone(nextState)
|
||||
local function diff(prevState: any, nextState: any): any
|
||||
if prevState == nextState then
|
||||
return nil
|
||||
elseif nextState == nil then
|
||||
return None
|
||||
elseif type(prevState) ~= "table" or type(nextState) ~= "table" then
|
||||
return nextState
|
||||
end
|
||||
|
||||
local patches: { [any]: any } = table.clone(nextState)
|
||||
|
||||
for key, previous in prevState do
|
||||
local current = nextState[key]
|
||||
|
||||
if previous == current then
|
||||
patches[key] = nil
|
||||
elseif current == nil then
|
||||
patches[key] = None
|
||||
elseif type(previous) == "table" and type(current) == "table" then
|
||||
patches[key] = diff(previous, current)
|
||||
end
|
||||
patches[key] = diff(previous, nextState[key])
|
||||
end
|
||||
|
||||
if config.autoSerialize then
|
||||
|
|
@ -87,20 +87,20 @@ end
|
|||
|
||||
--- Applies a patch to a state and returns the next state.
|
||||
---
|
||||
--- @param state The current state.
|
||||
--- @param patches The patches to apply.
|
||||
--- @param prevState The current state.
|
||||
--- @param statePatch The patches to apply.
|
||||
--- @returns The next state.
|
||||
local function apply(state: any, patches: any): any
|
||||
if isNone(patches) then
|
||||
local function apply(prevState: any, statePatch: any): any
|
||||
if isNone(statePatch) then
|
||||
return nil
|
||||
elseif type(state) ~= "table" or type(patches) ~= "table" then
|
||||
return patches
|
||||
elseif type(prevState) ~= "table" or type(statePatch) ~= "table" then
|
||||
return statePatch
|
||||
end
|
||||
|
||||
local nextState = table.clone(state)
|
||||
local stateIsArray = state[1] ~= nil
|
||||
local nextState = table.clone(prevState)
|
||||
local stateIsArray = prevState[1] ~= nil
|
||||
|
||||
for key, patch in patches do
|
||||
for key, value in statePatch do
|
||||
-- Diff-checking an array produces a sparse array, which will not be
|
||||
-- preserved when converted to JSON. To prevent this, we turn string
|
||||
-- keys back into numeric keys.
|
||||
|
|
@ -108,7 +108,7 @@ local function apply(state: any, patches: any): any
|
|||
key = tonumber(key) or key
|
||||
end
|
||||
|
||||
nextState[key] = apply(nextState[key], patch)
|
||||
nextState[key] = apply(nextState[key], value)
|
||||
end
|
||||
|
||||
return nextState
|
||||
|
|
|
|||
|
|
@ -235,47 +235,73 @@ local function unsyncAtomsFromClient(client: any, ...: string)
|
|||
end
|
||||
end
|
||||
|
||||
local function startSync(onSync: (client: any, payloads: { SyncPayload }) -> ())
|
||||
local function onSyncCallback(_client: any, _payloads: { SyncPayload })
|
||||
warn("[Charm] Override the onSync function by passing a custom function to startSync")
|
||||
end
|
||||
|
||||
local function flush()
|
||||
local patches = if config.preserveHistory then createHistoricalPatches() else createPatch()
|
||||
|
||||
-- While payload objects are technically mutable as they are unique to
|
||||
-- each client, the data inside them is not safe to mutate.
|
||||
if Charm.flags.frozen then
|
||||
Charm.deepFreeze(patches)
|
||||
end
|
||||
|
||||
for client in clientRegister do
|
||||
local payloads: { SyncPayload } = if config.preserveHistory
|
||||
then createHistoricalPayloadsForClient(client, patches)
|
||||
else createPayloadsForClient(client, patches)
|
||||
|
||||
if payloads[1] then
|
||||
if task then
|
||||
task.spawn(onSyncCallback, client, payloads)
|
||||
else
|
||||
onSyncCallback(client, payloads)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unregisterUnusedAtoms()
|
||||
table.clear(globalUpdates)
|
||||
end
|
||||
|
||||
local function stopSync()
|
||||
if connection then
|
||||
connection:Disconnect()
|
||||
connection = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function reset()
|
||||
table.clear(atomRegister)
|
||||
table.clear(clientRegister)
|
||||
table.clear(globalUpdates)
|
||||
stopSync()
|
||||
end
|
||||
|
||||
local function startSync(onSync: (client: any, payloads: { SyncPayload }) -> ()): () -> ()
|
||||
if connection then
|
||||
connection:Disconnect()
|
||||
end
|
||||
|
||||
local elapsedTime = 0
|
||||
onSyncCallback = onSync
|
||||
|
||||
local function sync()
|
||||
local patches = if config.preserveHistory then createHistoricalPatches() else createPatch()
|
||||
if game then
|
||||
local elapsedTime = 0
|
||||
|
||||
-- While payload objects are technically mutable as they are unique to
|
||||
-- each client, the data inside them is not safe to mutate.
|
||||
if Charm.flags.frozen then
|
||||
Charm.deepFreeze(patches)
|
||||
end
|
||||
|
||||
for client in clientRegister do
|
||||
local payloads: { SyncPayload } = if config.preserveHistory
|
||||
then createHistoricalPayloadsForClient(client, patches)
|
||||
else createPayloadsForClient(client, patches)
|
||||
|
||||
if payloads[1] then
|
||||
task.spawn(onSync, client, payloads)
|
||||
connection = game:GetService("RunService").Heartbeat:Connect(function(deltaTime)
|
||||
elapsedTime += deltaTime
|
||||
if elapsedTime >= config.interval then
|
||||
elapsedTime %= config.interval
|
||||
debug.profilebegin("CharmSync")
|
||||
flush()
|
||||
debug.profileend()
|
||||
end
|
||||
end
|
||||
|
||||
unregisterUnusedAtoms()
|
||||
table.clear(globalUpdates)
|
||||
end)
|
||||
end
|
||||
|
||||
connection = game:GetService("RunService").Heartbeat:Connect(function(deltaTime: number)
|
||||
elapsedTime += deltaTime
|
||||
|
||||
if elapsedTime >= config.interval then
|
||||
elapsedTime %= config.interval
|
||||
|
||||
debug.profilebegin("CharmSync")
|
||||
sync()
|
||||
debug.profileend()
|
||||
end
|
||||
end)
|
||||
return stopSync
|
||||
end
|
||||
|
||||
return {
|
||||
|
|
@ -283,4 +309,7 @@ return {
|
|||
unsyncClient = unsyncClient,
|
||||
unsyncAtomsFromClient = unsyncAtomsFromClient,
|
||||
startSync = startSync,
|
||||
stopSync = stopSync,
|
||||
flush = flush,
|
||||
reset = reset,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue