Skip to content

Replicators

Replicators are server-owned replicated state trees added in Vanguard 0.1.15. They are designed for larger structured state where a single remote property would become noisy or awkward.

Use a replicator when clients need to read and observe current state, while all mutation remains on the server.

Define a Replicator

local MatchService = Vanguard.CreateService({
    Name = "MatchService",

    Client = {
        State = Vanguard.CreateReplicator({
            Phase = "Lobby",
            Score = {
                Red = 0,
                Blue = 0,
            },
        }),
    },
})

During server startup, Vanguard replaces the marker with a server replicator object and publishes a protocol 2 remote folder for clients.

Server API

Get

local fullState = self.Client.State:Get()
local score = self.Client.State:Get(nil, "Score.Red")
local playerState = self.Client.State:Get(player)

Get(player?, path?) returns a cloned value. When a Player has an override, the override is used; otherwise the global state is used.

Set

self.Client.State:Set({
    Phase = "Active",
    Score = { Red = 0, Blue = 0 },
})

Set replaces the global state and sends the full replacement to all clients.

SetPath

self.Client.State:SetPath("Score.Red", 3)
self.Client.State:SetPath({ "Players", player.UserId, "Ready" }, true)

Paths can be dot strings or arrays of string/number segments. Missing intermediate tables are created automatically. The replicated root must be a table when using path updates.

Patch

self.Client.State:Patch({
    Phase = "Overtime",
    Score = {
        Blue = 2,
    },
})

Patch recursively merges tables into the current root state. Non-table values replace existing values.

Per-Player State

self.Client.State:SetFor(player, {
    Phase = "Spectating",
})

self.Client.State:SetPathFor(player, "UI.Hidden", true)
self.Client.State:PatchFor(player, { Tutorial = { Step = 2 } })
self.Client.State:ClearFor(player)

Per-player overrides are removed automatically when the Player leaves. ClearFor removes the override and sends the current global state to that Player.

Observe

local connection = self.Client.State:Observe(function(player, change, state)
    print(player, change.Operation, state.Phase)
end)

Server observers receive player, a change descriptor, and the effective state after the change. player is nil for global updates.

Client API

Get

local MatchService = Vanguard.GetService("MatchService")

local state = MatchService.State:Get()
local redScore = MatchService.State:Get("Score.Red")

The first Get hydrates the client by invoking the server. Reads pass through the same network guard pipeline as remote property reads.

Observe

local subscription = MatchService.State:Observe(function(state, change)
    print(state.Phase)
end)

subscription:Disconnect()

Observe subscribes to future changes and asynchronously fetches the current state. The initial callback receives change == nil.

ObservePath

local subscription = MatchService.State:ObservePath("Score.Red", function(value, change)
    print("Red score", value)
end)

Path observers read the selected subtree after every replicated change. They do not filter changes by path yet; callbacks should be inexpensive.

Change Shape

type ReplicatorChange = {
    Operation: "Set" | "SetPath" | "Patch" | "Clear",
    Path: { string | number }?,
    Value: any?,
}

Set carries the replacement state. SetPath carries the normalized path and new value. Patch carries the patch table.

Network Security

Replicator reads use RemoteType = "Replicator" in the network context. They can be rate-limited, authenticated, and verified with normal service network rules:

Network = {
    State = {
        Authenticate = function(player)
            return ProfileService:IsLoaded(player), "Profile is not ready"
        end,
        RateLimit = {
            Limit = 10,
            Window = 1,
        },
    },
}

Clients cannot mutate replicator state through Vanguard. Expose explicit server remote methods or signals for client requests, then validate, authenticate, verify, and mutate the replicator from server code.

Replicator vs RemoteProperty

Use Prefer
One small scalar or object CreateProperty
Larger table state CreateReplicator
Frequent partial updates SetPath or Patch on a replicator
Per-player current value Either, depending on shape
Event history CreateSignal, not replicated state

Replicators are state replacement tools, not event logs. If every historical change matters, use a signal or a server-owned queue and send explicit events.

Protocol Compatibility

Replicators require network protocol 2. Vanguard 0.1.14 clients use protocol 1 and cannot safely interpret the new remote kind. Mixed protocol installs fail during client service proxy construction with VG-NET-001.

Continue with Networking, Network Security, and Network Protocols.