mirror of
https://github.com/littensy/charm.git
synced 2026-10-22 21:01:38 +00:00
refactor: separate code for preserving history
This commit is contained in:
parent
8aef95e2ae
commit
e5f655482f
1 changed files with 148 additions and 86 deletions
|
|
@ -62,32 +62,29 @@ type ServerSyncer = {
|
||||||
local function server(options: ServerOptions): ServerSyncer
|
local function server(options: ServerOptions): ServerSyncer
|
||||||
local self = {} :: ServerSyncer
|
local self = {} :: ServerSyncer
|
||||||
local sync: (player: Player, payload: SyncPayload) -> ()
|
local sync: (player: Player, payload: SyncPayload) -> ()
|
||||||
local snapshots: { { [string]: any } } = {}
|
|
||||||
local changed = false
|
|
||||||
|
|
||||||
-- Start the interval to send state patches over the network.
|
local function createSnapshot()
|
||||||
function self:connect(callback)
|
|
||||||
local cleanups = {}
|
|
||||||
sync = callback
|
|
||||||
|
|
||||||
local function getSnapshot()
|
|
||||||
local snapshot: { [string]: any } = {}
|
local snapshot: { [string]: any } = {}
|
||||||
|
|
||||||
for key, atom in next, options.atoms do
|
for key, atom in next, options.atoms do
|
||||||
snapshot[key] = atom()
|
snapshot[key] = atom()
|
||||||
end
|
end
|
||||||
|
|
||||||
return snapshot
|
return snapshot
|
||||||
end
|
end
|
||||||
|
|
||||||
local function pushSnapshot(key: string, current: any, previous: any)
|
if options.preserveHistory then
|
||||||
if not options.preserveHistory then
|
-- Preserve a history of every state change to allow for more granular
|
||||||
if snapshots[2] then
|
-- syncing with the client.
|
||||||
snapshots[2][key] = current
|
local snapshots: { { [string]: any } } = {}
|
||||||
else
|
local changed = false
|
||||||
snapshots[2] = getSnapshot()
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
|
function self:connect(callback)
|
||||||
|
local subscriptions = {}
|
||||||
|
|
||||||
|
sync = callback
|
||||||
|
|
||||||
|
local function pushSnapshot(key: string, current: any, previous: any)
|
||||||
local lastSnapshot = snapshots[#snapshots]
|
local lastSnapshot = snapshots[#snapshots]
|
||||||
local previousSnapshot = snapshots[#snapshots - 1]
|
local previousSnapshot = snapshots[#snapshots - 1]
|
||||||
|
|
||||||
|
|
@ -103,16 +100,16 @@ local function server(options: ServerOptions): ServerSyncer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Populate the initial state and snapshot for each atom.
|
-- Populate the snapshot with the initial state of each atom.
|
||||||
-- Subscribe to each atom and update the state when it changes.
|
-- Subscribe to each atom and update the state when it changes.
|
||||||
for key, atom in next, options.atoms do
|
for key, atom in next, options.atoms do
|
||||||
cleanups[key] = subscribe(atom, function(current, previous)
|
subscriptions[key] = subscribe(atom, function(current, previous)
|
||||||
pushSnapshot(key, current, previous)
|
pushSnapshot(key, current, previous)
|
||||||
changed = true
|
changed = true
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
snapshots[1] = getSnapshot()
|
snapshots[1] = createSnapshot()
|
||||||
|
|
||||||
local disconnect = setInterval(function()
|
local disconnect = setInterval(function()
|
||||||
if not changed then
|
if not changed then
|
||||||
|
|
@ -145,20 +142,11 @@ local function server(options: ServerOptions): ServerSyncer
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
disconnect()
|
disconnect()
|
||||||
for _, cleanup in next, cleanups do
|
|
||||||
cleanup()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Send the initial state to a player when they join the server.
|
for _, unsubscribe in next, subscriptions do
|
||||||
function self:hydrate(player)
|
unsubscribe()
|
||||||
assert(sync, "connect() must be called before hydrate()")
|
end
|
||||||
|
end
|
||||||
sync(player, {
|
|
||||||
type = "init",
|
|
||||||
data = snapshots[#snapshots],
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function self:_sendPatch(player: Player)
|
function self:_sendPatch(player: Player)
|
||||||
|
|
@ -186,6 +174,80 @@ local function server(options: ServerOptions): ServerSyncer
|
||||||
|
|
||||||
sync(player, unpack(payloads))
|
sync(player, unpack(payloads))
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
-- Only send the most recent state to the client.
|
||||||
|
local previousSnapshot = createSnapshot()
|
||||||
|
local currentSnapshot = table.clone(previousSnapshot)
|
||||||
|
local changed = false
|
||||||
|
|
||||||
|
function self:connect(callback)
|
||||||
|
local subscriptions = {}
|
||||||
|
|
||||||
|
sync = callback
|
||||||
|
|
||||||
|
-- Subscribe to each atom and update the state when one changes.
|
||||||
|
for key, atom in next, options.atoms do
|
||||||
|
subscriptions[key] = subscribe(atom, function(state)
|
||||||
|
currentSnapshot[key] = state
|
||||||
|
changed = true
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local disconnect = setInterval(function()
|
||||||
|
if not changed then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local payload: SyncPayload = {
|
||||||
|
type = "patch",
|
||||||
|
data = patch.diff(previousSnapshot, currentSnapshot),
|
||||||
|
}
|
||||||
|
|
||||||
|
previousSnapshot = table.clone(currentSnapshot)
|
||||||
|
changed = false
|
||||||
|
|
||||||
|
for _, player in next, Players:GetPlayers() do
|
||||||
|
callback(player, payload)
|
||||||
|
end
|
||||||
|
end, options.interval or 0)
|
||||||
|
|
||||||
|
return function()
|
||||||
|
disconnect()
|
||||||
|
|
||||||
|
for _, unsubscribe in next, subscriptions do
|
||||||
|
unsubscribe()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function self:_sendPatch(player: Player)
|
||||||
|
assert(sync, "connect() must be called before _sendPatch()")
|
||||||
|
|
||||||
|
if not changed then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local payload: SyncPayload = {
|
||||||
|
type = "patch",
|
||||||
|
data = patch.diff(previousSnapshot, currentSnapshot),
|
||||||
|
}
|
||||||
|
|
||||||
|
previousSnapshot = table.clone(currentSnapshot)
|
||||||
|
changed = false
|
||||||
|
|
||||||
|
sync(player, payload)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Send the initial state to a player when they join the server.
|
||||||
|
function self:hydrate(player)
|
||||||
|
assert(sync, "connect() must be called before hydrate()")
|
||||||
|
|
||||||
|
sync(player, {
|
||||||
|
type = "init",
|
||||||
|
data = createSnapshot(),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue