twistedsignal/replication

Typed reactive server-to-client replication for Roblox

replication

Typed, reactive replication for Roblox. The server owns shared in-memory state, changes replicate to every connected client, and clients that join later fetch the latest server state. Nothing is saved after the server closes.

This package follows the behavior of ReplicationTyped, itself based on dataServiceTyped. It retains the camelCase API and integration with twistedsignal/ui.

Install

pesde add twistedsignal/replication

Create a replica

Give each replica a unique name and an initial template:

local replication = require(path.to.Replication)

local gameState = replication("gameState", {
	round = 1,
	status = "waiting",
	playersAlive = {} :: { string },
})

return gameState

On the server, use .server:

gameState.server.round(function(round)
	return round + 1
end)

gameState.server.status("playing")
gameState.server.playersAlive.insertArray("Builderman")

On the client, use .client:

print(gameState.client.round())

gameState.client.status.changed(function(status)
	print("status", status)
end)

Server changes go to all current clients. When a client initializes the replica, it requests a snapshot of its current state from the server, so it receives changes made before that player joined.

Private values

Pass false as shouldReplicate to change server state without sending it to clients:

gameState.server.secret("server only", false)

The package remembers that path and removes it from snapshots sent to late joiners. Array methods accept the same option:

gameState.server.queue.insertArray(item, nil, false)
gameState.server.queue.removeArray(1, false)

Value API

All values support reads, writes, updater functions, .observable, and changed(callback).

Array values add:

  • insertArray(item, position?, shouldReplicate?)
  • removeArray(position?, shouldReplicate?)
  • onArrayInserted(callback)
  • onArrayRemoved(callback)

Dictionary values add onKeyAdded(callback) and onKeyRemoved(callback). Callback registration returns a cleanup function.

Every value participates in twistedsignal/ui dependency tracking:

local labelText = ui.derive(function()
	return `Round {gameState.client.round()}`
end)

Credits

Based on twistedsignal/data, ReplicationTyped, and dataServiceTyped. Licensed under MIT.