This is a simple data store script that encodes the data into a JSON format (it auto saves every 30 seconds). The main thing I wan’t to improve is reliability. Right now, the code uses SetAsync(), but would something like IncrementAsync() or UpdateAsync() be better in this situation?
local players = game:GetService("Players")
local httpService = game:GetService("HttpService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local dataStoreService = game:GetService("DataStoreService")
local dataStore = dataStoreService:GetDataStore("DataSerializer_V2")
local clientSerialize = replicatedStorage:FindFirstChild("Events").Serialize
local starterCurrency = 1000
local function serialize(plr)
local key = "plr#" .. plr.UserId
-- the table below only has one value, but you should be able to add more.
local data = {
["Currency"] = plr.leaderstats.Cash.Value
}
local success, err = pcall(function()
dataStore:SetAsync(key, httpService:JSONEncode(data))
end)
if not success then
warn("Failed to serialize data: " .. tostring(err))
return
end
end
local function joinGame(plr)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = plr
local currency = Instance.new("IntValue")
currency.Name = "Cash" -- Just a random name
currency.Value = starterCurrency
currency.Parent = leaderstats
-- DataStore
local key = "plr#" .. plr.UserId
local serializedData
local deserializedData
local success, err = pcall(function()
serializedData = dataStore:GetAsync(key)
end)
if not success then
warn("Failed to read data: " .. tostring(err))
return
end
if serializedData then
deserializedData = httpService:JSONDecode(serializedData)
currency.Value = deserializedData["Currency"]
else
serialize(plr)
end
end
clientSerialize.OnServerEvent:Connect(serialize)
players.PlayerAdded:Connect(joinGame)
players.PlayerRemoving:Connect(serialize)
Client Side (LocalScript)
local replicatedStorage = game:GetService("ReplicatedStorage")
local event = replicatedStorage:WaitForChild("Events").Serialize
local waitTime = 30 -- in seconds
while wait(waitTime) do
event:FireServer()
end
Something I have had happen is the data does not get serialized when the player leaves (sometimes). I wan’t to make the code as close to perfect as possible, since all my games this method to an extent. Also, does formatting the data in JSON do anything. I use it since it makes it more complex to look at (for security purposes), but I don’t know if it really is something to worry about.
JSON stands for JavaScript Object Notation, and it is specially designed to store information about objects in a human AND machine readable ways - I personally often use it as it is easy to read, so JSON encoding does not make it more secure.
The main advantages of JSON are:
It is a very standardised and popular method of storing information about an object, so any major programming language has a library or module to deserialise it (for example HTTPDecode in Roblox’s Lua).
It is human readable, and writable - you can edit your objects using Notepad rather than a complex piece of software
(Within Roblox) you can use it to store objects created using tables in Roblox’s DataStoreService relatively efficiently
The main disadvantages of JSON are:
It is string encoded, meaning that it takes more data to store the same information
It is also more computationally expensive to do operations on a JSON file than objects within your script
Other than that, your code looks pretty solid. I would maybe incorporate a 4 retry system i.e. if your attempts to retrieve data fail, keep trying until you either get a success or run out of retries.
I would also recommend different namespaces or at least naming conventions for your top level singletons and services, but this is nit picky and down to personal preference.
The DataStoreService automatically converts data to a JSON string, so no. Also, there isn’t a valid reason from what I can see to expose control of your data update processes to the client, so I’d do something along the lines of:
local last_update = os.time()
local update_interval = 60*3
local function onStep()
local clock = os.time()
if (os.difftime(clock, last_update) >= update_interval) then
--save
last_update = clock
end
return
end
RunService.Heartbeat:Connect(onStep)
I’m not recommending for or against the JSON system - I personally use :JSONEncode() and :JSONDecode() all the time - although I only save tables, not objects, so that’s better for me as I get finer control over my save system.
What I meant is that you do have to secure the code, by keeping it on the server etc. but you don’t have to secure the data itself - e.g. don’t bother encrypting it before you :SetASync() or :UpdateASync()