Skip to content

Change Subscription Functions ​

OnChanged ​

OnChanged(player, pathOrKey, callback) → Connection?

Subscribes to changes in a specific player's profile.

Parameters:

  • player: Player - The player to watch
  • pathOrKey: string - A top-level key (e.g. "Coins") or a dot-separated path (e.g. "Inventory.Slots")
  • callback: (newValue, oldValue, info) -> () - Fired when data at the path changes

Returns: Connection? - Connection to disconnect when done, or nil if invalid (player invalid, path not a string, or callback not a function)

The third callback argument depends on the change:

  • For path ".", info is always a table: { action, path } (and index for insert/remove).
  • For a normal path or key (not "."):
    • On a direct Set / SetValues at that path, info is the path key array (e.g. { "Coins" } or { "Plot", "Likes" }) — same shape as client OnChanged for the simple case.
    • When the change is on a child path (e.g. listening on "Plot" but something under Plot.Buildings changed), the callback receives (currentValueAtListenedPath, nil, { action, path }) where path is the Replica path array for the mutation.
  • For table operations, info includes action, path, and index where applicable.

Using the root path "." subscribes to every change on that profile. Not recommended except for debugging, since it fires on every mutation and can hurt performance. Prefer listening on the narrowest path you need (e.g. "Coins" or "Mailbox.UnreadCount").

Example
lua
local connection = PlayerState.OnChanged(player, "Coins", function(newValue, oldValue, info)
    -- For a direct Set on "Coins", `info` is often the path key list, e.g. { "Coins" }
    print(`Coins: {oldValue} -> {newValue}`)
end)

PlayerState.OnChanged(player, "Inventory.Slots", function(newValue, oldValue, info)
    if type(info) == "table" and info.action == "TableInsert" then
        print(`Item added at index {info.index}`)
    end
end)

game.Players.PlayerRemoving:Connect(function(leavingPlayer)
    if leavingPlayer == player and connection then
        connection:Disconnect()
    end
end)

Disconnect When Done

Call connection:Disconnect() when the listener is no longer needed (e.g. when the player leaves or the feature is destroyed) to avoid leaks and unnecessary work.

Server-only paths

OnChanged is wired to the player Replica. Updates under ServerOnlyRoots (for example Server.*) apply to profile.Data only and do not change the Replica, so no OnChanged callback runs for those paths. Use your own server flow after writes, or read with GetPath / GetAll on the server.


PlayerState - High-Performance Roblox Data Management