I made a data store script recently that uses a “Caching” system, both for current data and data that failed to save when a player leaves(If it does happen).
--> Services
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--> Remotes
local Remotes = ReplicatedStorage:WaitForChild("Remotes")
local DataEvent = Remotes:WaitForChild("Data")
local DataFunc = Remotes:WaitForChild("Data_F")
local DataBind = Remotes:WaitForChild("Data_B")
local DataBindFunc = Remotes:WaitForChild("Data_B_F")
--> Data Stores
local DataStore = DataStoreService:GetDataStore("PlayerData", "UserData")
--> Data Template
local Template = {
Time = 0,
Gears = {},
Gradients = {},
Equipped = {
Gradient = "",
Title = "",
},
Titles = {},
Kills = 0,
ProductsBought = {},
}
--> Profiles
local Profiles = {}
local Cache = {}
--> Functions
local function GetData(player: Player)
return Profiles[player]
end
local function LoadData(player: Player)
local UserId = tostring(player.UserId)
local Data = nil
pcall(function()
Data = DataStore:GetAsync(UserId)
end)
if Data == nil then
Data = Template
end
return Data
end
local function SaveData(player: Player, Data: {})
local UserId = tostring(player.UserId)
local succ, err = pcall(function()
DataStore:SetAsync(UserId, Data)
end)
if succ == false then
Cache[UserId] = Data
end
end
local function PlayerAdded(player: Player)
local Data = LoadData(player)
Profiles[player] = Data
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
local Time = Instance.new("NumberValue")
Time.Name = "Time"
Time.Value = Data.Time
Time.Parent = leaderstats
leaderstats.Parent = player
task.spawn(function()
while task.wait(60) do
Profiles[player].Time += 1
Time.Value = Profiles[player].Time
end
end)
end
local function PlayerRemoving(player: Player)
local Data = Profiles[player]
SaveData(player, Data)
Profiles[player] = nil
end
local function onEvent(player: Player, Key: string, ...)
end
local function onBind(key: string, ...)
end
local function onBindFunc(key: string, ...)
if key:lower() == "get" then
return GetData(...)
end
return nil
end
local function onFunc(player: Player, Key: string, ...)
if Key:lower() == "get" then
return GetData(player)
end
return nil
end
--> Race Conditions (If a player loads in before this script runs.)
for _, v in pairs(Players:GetPlayers()) do
PlayerAdded(v)
end
--> Connections
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(PlayerRemoving)
--> Remotes
DataEvent.OnServerEvent:Connect(onEvent)
DataFunc.OnServerInvoke = onFunc
DataBind.Event:Connect(onBind)
DataBindFunc.OnInvoke = onBindFunc
--> Game Shutdown
game:BindToClose(function()
for _, v in pairs(Players:GetPlayers()) do
PlayerRemoving(v)
end
--> Also save the data of players that failed to save.
for UserId, Data in pairs(Cache) do
DataStore:SetAsync(UserId, Data)
Cache[UserId] = nil
end
end)
You would use remotes to retrieve the players data from other scripts, and from the client.
An example of getting a players data with all the listed remotes in the variables:
DataFunc: (Client)
--> Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--> remotes
local Remotes = ReplicatedStorage:WaitForChild("Remotes")
local DataFunc = Remotes:WaitForChild("Data_F")
--> Added Wait
task.wait(1)
--> Get Player Data
local PlayerData = DataFunc:InvokeServer("get")
--> Print Player Data
print(PlayerData)
DataBindFunc: (Server)
--> Services
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--> Remotes
local Remotes = ReplicatedStorage:WaitForChild("Remotes")
local DataBindFunc = Remotes:WaitForChild("Data_B_F")
--> Functions
Players.PlayerAdded:Connect(function(player: Player)
--> wait so that data can load
task.wait(3)
--> Get Player Data
local PlayerData = DataBindFunc:Invoke("get", player)
--> Print Data
print(PlayerData)
end)
But like someone previously mentioned, ProfileService is a much better data store that uses a cache.
Profile Service: