Replicate state to clients
Publish
Section titled “Publish”Twill.Replication.Set("RoundEndsAt", os.time() + 120)
Twill.Replication.SetFor(player, "Loadout", loadout)Twill.Replication.SetPathFor(player, "Loadout", "Primary", "rifle")Twill.Replication.IncrementFor(player, "Loadout", "Ammo", -1)Every name has a For twin. Without it the value is shared by everybody. With
it, the value belongs to one player and no one else ever receives it.
Subscribe
Section titled “Subscribe”local subscription = Twill.Replication.Subscribe("Loadout.Primary", function(weapon) label.Text = weapon or "none"end)
bag:Add(subscription)The callback runs once immediately if the value is already known, so there is no separate “read the current value” step.
Read without subscribing
Section titled “Read without subscribing”local endsAt = Twill.Replication.Get("RoundEndsAt")Works on both sides. On the client this reads what was last received, so it can
be nil early. To wait:
local endsAt, arrived = Twill.Replication.WaitFor("RoundEndsAt", 10)Key names cannot contain dots
Section titled “Key names cannot contain dots”The first dot separates the key from the path inside it.
Replication.Set("Player.Data", value) -- never matches any subscriptionReplication.Set("PlayerData", value) -- correctA subscription to "Player.Data" reads it as key Player, path Data, and
waits forever.
Change one field in a large table
Section titled “Change one field in a large table”Twill.Replication.SetPathFor(player, "Data", "Stats.Coins", 250)Only that field travels. Replication keeps a private copy of what each player was last sent and diffs against it, so a deep write inside a large table costs one field, not the table.
SetPath creates any tables missing along the path. It returns false when a
step along the path holds something other than a table, or when the key’s
validator refuses the write.
Rearrange without sending every step
Section titled “Rearrange without sending every step”Twill.Replication.Freeze("Board")
for _, move in moves do Twill.Replication.SetPath("Board", move.Path, move.Value)end
Twill.Replication.Unfreeze("Board")One diff for the whole rearrangement instead of a message per step.
Slow a noisy key
Section titled “Slow a noisy key”Twill.Replication.SetThrottle("Position", 0.5)Writes are already batched on a global interval. This slows one key further, for values that change far more often than a player could notice.
Guard against your own mistakes
Section titled “Guard against your own mistakes”Twill.Replication.SetValidator("RoundEndsAt", function(value) return type(value) == "number" and value > os.time()end)A client cannot publish anything, so this is not a security boundary. It catches
the bug where some code path publishes nil at three in the morning.
Player data is already wired
Section titled “Player data is already wired”If you only want a player to see their own saved fields, do not publish by hand.
Name them in Data.Configure:
Replicate = { "Coins", "Stats" },They arrive under the Data key and follow direct mutation. See
Store and save player data.
Watch the cost
Section titled “Watch the cost”local stats = Twill.Replication.GetStats()print(stats.Messages, stats.Keys)The diff is what keeps messages small, and the private per-player copy is what the diff costs in memory. A key holding a very large table for every player is paid for twice.