Hello! I’ve rewritten my datastore code to incorporate UpdateAsync and IncementAsync. I’m trying to understand data storage better because this code works but it’s tied to an autosave. My requests pile up quickly and they get added to the queue frequently. I want to make sure that I get this right because I’m about to release my game, and can’t have players losing data. Especially if they buy some Coins.
-- Frriend's Improved Datastore Service --
local DSS = game:GetService("DataStoreService")
local plrDataStore = DSS:GetDataStore(workspace.Constants.Datastores.DataStoreID.Value)
local saveNotifyRE = game.ReplicatedStorage.RemoteEvent.SaveNotify
local currentPatch = game.Workspace.Constants.GLOBALS.CurrentPatch.Value
local TS = game:GetService("TestService")
-- Billboard Constants --
local billboardStats = {"Bricks", "Level", "Wins"}
local billboardStores = {Bricks = workspace.Constants.Datastores.ODSBricksID.Value,
Level = workspace.Constants.Datastores.ODSLevelID.Value,
Wins = workspace.Constants.Datastores.ODSWinsID.Value}
-- Save Notification for Players --
function notifySave(plr, state)
saveNotifyRE:FireClient(plr, state)
end
-- Billboard Functions --
function isBillboardStat(tag)
for _, stat in ipairs(billboardStats) do
if stat == tag then
return true
end
end
return false
end
-- Not as Important, So its Simplified --
function updateOrderedBillboardStats(plr, store, data)
local orderedDataStore = DSS:GetOrderedDataStore(billboardStores[store])
local success, err = pcall(function()
return orderedDataStore:SetAsync(plr.UserId, data)
end)
end
-- Debug --
function writeToConsole(ln, warning, message)
warning = warning or false
message = message or false
local prefix = "[Player Data Handler] "
if warning then
warn(prefix .. ln)
elseif message then
TS:Message(prefix .. ln)
else
print(prefix .. ln)
end
end
-- Only for Checking if store Exists / Gettings its Value if so --
function getAsync(plr, key)
local success, value = pcall(function()
return plrDataStore:GetAsync(plr.UserId .. "_" .. key)
end)
if success then return value else return nil end
end
-- Only for Creating Stores --
function setAsync(plr, key, value)
local success, value = pcall(function()
return plrDataStore:SetAsync(plr.UserId .. "_" .. key, value)
end)
if success then return true else return false end
end
-- Only for Updating Stores --
function updateAsync(plr, key, newValue)
if isBillboardStat(key) then
updateOrderedBillboardStats(plr, key, newValue)
end
local success, err = pcall(function()
plrDataStore:UpdateAsync(plr.UserId .. "_" .. key, function(oldData)
local newData = oldData or -1
if newData ~= -1 then
-- writeToConsole(plr.Name .. " : Updated ['" .. key .. "' : (SWAPPED " .. oldData .. " FOR " .. newValue .. ") Successfully]", false, true)
return newValue
else
writeToConsole(plr.Name .. " : UPDATE ERROR ['" .. key .. "' Not Found] Skipping to Prevent Data Loss..", true)
return nil
end
end)
end)
if success then
return true
else
writeToConsole("[LN 45] " .. err, true)
return false
end
end
-- Checks if user's data was saved on current patch --
function checkPatch(plr)
local patch = getAsync(plr, "Patch")
if patch then
return patch
else
writeToConsole("NOTE : Patch Data not Found, Validating Data..", true)
setAsync(plr, "Patch", currentPatch)
return false
end
end
function checkIfPlayerDataUpdated(plr)
local plrData = plr.Stats:GetChildren()
local tally = 0
for _, data in ipairs(plrData) do
if getAsync(plr, data.Name) then
tally = tally + 1
else
writeToConsole(plr.Name .. " : FLAG ['" .. data.Name .. "' Not Found] Creating Store..", true)
setAsync(plr, data.Name, data.Value)
end
end
writeToConsole(plr.Name .. " : CHECK PATCH Integrity Check: (" .. tally .. "/" .. #plrData .. ") Stores Loaded Properly")
end
-- For use when saving data. We make sure the data is there before we update it --
function savePlayerData(plr)
notifySave(plr, true)
local plrData = plr.Stats:GetChildren()
local tally = 0
for _, data in ipairs(plrData) do
local info = updateAsync(plr, data.Name, data.Value)
if info then
tally = tally + 1
end
end
spawn(function() wait(2) notifySave(plr, false) end)
-- writeToConsole(plr.Name .. " : SAVE Integrity Check: (" .. tally .. "/" .. #plrData .. ") Stores Updated Properly")
end
-- For Loading player Data. Only Load if they are now on the current patch --
function loadPlayerData(plr)
local plrData = plr.Stats:GetChildren()
local tally = 0
for _, data in ipairs(plrData) do
local loadData = getAsync(plr, data.Name)
if loadData then
tally = tally + 1
data.Value = loadData
else
writeToConsole(plr.Name .. " : LOAD ERROR ['" .. data.Name .. "' Not Found] Skipping to Avoid Data Loss..", true)
end
end
writeToConsole(plr.Name .. " : LOAD Integrity Check: (" .. tally .. "/" .. #plrData .. ") Stores Loaded Properly")
end
function incrementStat(plr, stat, value)
local key = plr.UserId .. "_" .. stat
local coins
local success, err = pcall(function()
coins = plrDataStore:IncrementAsync(key, value)
end)
if success then
writeToConsole('Incremented Successfully')
else
writeToConsole(err)
end
end
_G.savePlayerData = function(plr)
print 'SAVING'
savePlayerData(plr)
end
_G.incrementSave = function(plr, stat, value)
writeToConsole("Increment Request Recieve")
incrementStat(plr, stat, value)
end
_G.loadPlayerData = function(plr)
local getPatch = checkPatch(plr)
if getPatch ~= currentPatch then
setAsync(plr, "Patch", currentPatch)
checkIfPlayerDataUpdated(plr)
spawn(function()
wait(1)
loadPlayerData(plr)
end)
else
loadPlayerData(plr)
end
end
Thanks in advance