Skip to main content

Server

Require the server half of your data module:

local Data = require(path.to.Data).server

Loading players

create-player-data does not hook PlayerAdded for you. Call startSessionAsync yourself:

local Players = game:GetService 'Players'

Players.PlayerAdded:Connect(function(player)
local ok = Data:startSessionAsync(player)
if not ok then
player:Kick 'Could not load your data. Please rejoin.'
end
end)

startSessionAsync is idempotent — calling it for an already loaded player returns true immediately.

Accessing loaded data

Always use :get(player) to access a player's data root:

local playerData = Data:get(player)
playerData.currency(50)

If the data may not be loaded yet, omit the second argument (returns nil):

local playerData = Data:get(player) -- nil if not loaded
if playerData then
playerData.currency(50)
end

Pass true to assert the data is loaded — throws an error otherwise:

local playerData = Data:get(player, true) -- errors if not loaded

Waiting for data from other scripts

When another script might be loading the player, use waitForData:

local playerData = Data:waitForData(player)
if playerData then
playerData.currency(50)
end

Yields until the data arrives, or returns nil if the player leaves before loading finishes.

Checking if loaded

if Data:isLoaded(player) then
local playerData = Data:get(player)
print(playerData.currency())
end

Changing data

The server is the source of truth. Writes here are authoritative and replicate to every client automatically. Use :get(player) to get the root, then mutate it:

local playerData = Data:get(player)
playerData.currency(50)
playerData.currency(function(currency)
return currency + 10
end)
playerData.questProgress.Insert(10)
playerData.items.sword_001(nil)

See Values for the full read/write/listen API.

Lifecycle signals

Three signals let you react to data loading and unloading:

Data.onSessionStart:connect(function(player)
local playerData = Data:get(player)
-- Apply defaults, run migrations, give starter items
playerData.currency(100)
end)

Data.onSessionEnd:connect(function(player)
-- Data saved and removed. Clean up server-side state.
end)

Data.onSessionFail:connect(function(player, reason)
warn(player.Name, 'data load failed:', reason)
end)

Ending sessions

endSession saves and releases the profile without kicking the player:

Data:endSession(player)

resetData deletes the saved profile and kicks the player:

Data:resetData(player)

Use addPlayerRemovingCallback to run logic before a session ends. Returns a disconnect function:

local disconnect = Data:addPlayerRemovingCallback(function(player, data)
data.lastLeaveTime = os.time()
end)

Global messages

Send data to a player even when they are on a different server:

Data:sendGlobalMessage("GiftCoins", userId, {
amount = 100,
from = "DailyReward",
})

Handle these messages on the receiving server:

Data:addGlobalCallback("GiftCoins", function(player, data)
local playerData = Data:get(player)
if not playerData then return false end

playerData.currency(function(currency)
return currency + data.amount
end)
end)

Return false from the callback to leave the message unprocessed (it will be retried). Add the callback before any player loads — otherwise messages arriving during startup may be missed.

Profiles

Access the raw ProfileStore profile for advanced use cases. getProfile returns the loaded profile for a player:

local profile = Data:getProfile(player)
if profile then
print(profile.Data.currency)
end

asyncGetProfile loads a profile by user ID without starting a session. Useful for admin tools:

local profile = Data:asyncGetProfile(userId)
if profile then
print(profile.Data.currency)
end