twistedsignal/data

Persistent, replicated, reactive player data for Roblox

data

Persistent, replicated player data for Roblox with the same reactive contract as twistedsignal/ui.

data is a fork of leifstout/dataServiceTyped. It keeps typed nested access, ProfileStore persistence, client replication, array helpers, dictionary callbacks, and global profile messages. The public API uses camelCase names.

Install

With pesde:

pesde add twistedsignal/data

The package currently targets pesde because twistedsignal/ui is released on GitHub but is not yet available from the public Wally index. wally.toml stays in the repository for compatibility with the upstream project and a later Wally release.

Create the service

Call the package once from a shared module. The server gets player-indexed data and the client gets its local player's data.

local data = require(path.to.Data)({
	template = {
		coins = 0,
		inventory = {} :: { string },
		settings = { musicEnabled = true },
	},
	profileStoreIndex = "PlayerData",
})

return data
-- Server
local playerData = data.server:waitForData(player)
playerData.coins(function(previous)
	return previous + 10
end)
playerData.inventory.insertArray("Sword")
-- Client
print(data.client.coins())
data.client.settings.musicEnabled(false)

Pass false as the second argument to a write, or the third argument to insertArray, to make a server-only change.

Use it with ui

Every node is callable like ui.value() and participates in ui dependency tracking. It also has an Rx Observable.

local ui = require(path.to.UI)

local labelText = ui.derive(function()
	return `Coins: {data.client.coins()}`
end)

local stop = ui.bind(screenGui, {
	Coins = { Text = labelText },
})

local subscription = data.client.coins.observable:Subscribe(function(coins)
	print("coins changed", coins)
end)

Reading a parent node tracks nested writes too. This makes code such as ui.derive(function() return #data.client.inventory() end) reactive when an item is inserted or removed.

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). Each callback registration returns a cleanup function.

The server service is available as data.server.service. Its methods include waitForData, resetData, getProfile, asyncGetProfile, addPlayerRemovingCallback, sendGlobalMessage, and addGlobalCallback.

Credits

The persistence and replication design comes from DataServiceTyped by Leif Stout. Reactive integration and the camelCase API are maintained by Twisted Signal. Licensed under MIT.