Skip to main content

Global Updates

The following example shows how you would handle global updates:

DataTemplate.luau
local dataTemplate = {
Coins = 0,
Gems = 0,
}

export type template = typeof(dataTemplate)

return table.freeze(dataTemplate)
GlobalUpdateDataType.luau
export type globalUpdateData = {
Type: "Coins",
Coins: number,
} | {
Type: "Gems", -- something else as an example to show type checking
Gems: number,
}

return {
Coins = "Coins",
Gems = "Gems",
}
Main.luau
local Players = game:GetService("Players")

local DataKeep = require(path_to_datakeep)
local DataTemplate = require(path_to_datatemplate)
local GlobalUpdateDataType = require(path_to_global_update_data_type)

local loadedKeeps = {}

local store = DataKeep.GetStore("PlayerData", DataTemplate):expect()

local function onPlayerAdded(player: Player)
store:LoadKeep(`Player_{player.UserId}`):andThen(function(keep)
keep:Reconcile()
keep:AddUserId(player.UserId) -- help with GDPR requests

keep.Released:Connect(function()
print(`{player.Name}'s Keep has been released!`)

loadedKeeps[player] = nil
player:Kick("Session released!")
end)

keep.ReleaseFailed:Connect(function()
print(`Failed to release {player.Name}'s Keep!`)

loadedKeeps[player] = nil
player:Kick("Failed to release session!")
end)

if not player:IsDescendantOf(Players) then
keep:Release():catch(function() end)
return
end

local function processGlobalUpdate(globalUpdateData: GlobalUpdateDataType.globalUpdateData, globalUpdateId: number)
if not GlobalUpdateDataType[globalUpdateData.Type] then
return
end

keep:ClearLockedUpdate(globalUpdateId):andThen(function()
-- clear locked update first and then apply changes

if globalUpdateData.Type == GlobalUpdateDataType.Coins then
keep.Data.Coins += globalUpdateData.Coins
elseif globalUpdateData.Type == GlobalUpdateDataType.Gems then
keep.Data.Gems += globalUpdateData.Gems
end
end):catch(function(err)
print(`Failed to clear locked update (id: {globalUpdateId}) in {player.Name}'s Keep: {err}`)
end)
end

-- process already locked global updates
for _, globalUpdate in keep:GetLockedGlobalUpdates() do
processGlobalUpdate(globalUpdate.Data, globalUpdate.Id)
end

-- listen for new locked global updates
keep.OnGlobalUpdate:Connect(processGlobalUpdate)

loadedKeeps[player] = keep

print(`Loaded {player.Name}'s Keep!`)
end):catch(function()
player:Kick("Data failed to load")
end)
end

-- loop through already connected players in case they joined before DataKeep loaded
for _, player in Players:GetPlayers() do
task.spawn(onPlayerAdded, player)
end

Players.PlayerAdded:Connect(onPlayerAdded)

Players.PlayerRemoving:Connect(function(player)
local keep = loadedKeeps[player]

if not keep then
return
end

keep:Release():catch(function() end)
end)
GiftingExample.luau
-- Offline / from different server gifting example

local Players = game:GetService("Players")

local DataKeep = require(path_to_datakeep)
local DataTemplate = require(path_to_datatemplate)
local GlobalUpdateDataType = require(path_to_global_update_data_type)

local store = DataKeep.GetStore("PlayerData", DataTemplate):expect()

local coinsToAdd = 100

store:PostGlobalUpdate(`Player_[UserId]`, function(globalUpdates)
for _, globalUpdate: DataKeep.GlobalUpdate<GlobalUpdateDataType.globalUpdateData> in globalUpdates:GetActiveUpdates() do
if not (globalUpdate.Data.Type == GlobalUpdateDataType.Coins) then
continue
end

globalUpdate.Data.Coins += coinsToAdd
globalUpdates:ChangeActiveUpdate(globalUpdate.Id, globalUpdate):expect()
return
end

-- active update not found, add a new one
globalUpdates:AddGlobalUpdate({
Type = GlobalUpdateDataType.Coins,
Coins = coinsToAdd,
}):expect()
end)