I am just beginning to use profile service as datastore in Roblox. I want to make sure this script is very secure and there are no chances of data loss. I also want to know if I did everything correctly.
local ProfileService = require(game.ServerScriptService.ProfileService)
-- Variables
local ProfileStore = ProfileService.GetProfileStore("PlayerDataStore", {})
local playertabledata = {}
local playerProfiles = {}
local function CheckData(async, dataname)
if async == nil then
return dataname
else
return async
end
end
local function AddNewTemplateData(player)
-- This function can be modified or removed if no template is used
end
local function AddDataToTable(player)
if not playertabledata[player.Name] then playertabledata[player.Name] = {} end
for _, item in pairs(player.DataFolder:GetChildren()) do
local itemData = {
Value = item.Value,
Attributes = {}
}
for attr, _ in pairs(item:GetAttributes()) do
itemData.Attributes[attr] = item:GetAttribute(attr)
end
playertabledata[player.Name][item.Name] = itemData
end
end
local function RetrieveDataFromTable(player)
if not playertabledata[player.Name] then playertabledata[player.Name] = {} end
for i, v in pairs(playertabledata[player.Name]) do
local item = player.DataFolder:FindFirstChild(i) or Instance.new("NumberValue", player.DataFolder)
item.Name = i
item.Value = v.Value
for attr, value in pairs(v.Attributes) do
item:SetAttribute(attr, value)
end
end
end
local function DeepCopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[DeepCopy(orig_key)] = DeepCopy(orig_value)
end
setmetatable(copy, DeepCopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
local function savePlayerData(player)
local profile = playerProfiles[player]
if profile and playertabledata[player.Name] then
AddDataToTable(player)
local safeData = DeepCopy(playertabledata[player.Name])
profile.Data = safeData
end
end
local function deleteValue(player, valueName)
local Folder = player:FindFirstChild("Folder")
if not Folder then
return
end
local value = Folder:FindFirstChild(valueName)
if value then
value:Destroy()
-- Update the data table
if playertabledata[player.Name] then
playertabledata[player.Name][valueName] = nil
end
-- Save the updated data to the ProfileService immediately
savePlayerData(player)
else
warn("Value not found:", valueName)
end
end
game.Players.PlayerAdded:Connect(function(player)
local Folder = Instance.new("Folder")
Folder.Name = "Folder"
Folder.Parent = player
-- Load player data
local profile = ProfileStore:LoadProfileAsync("Player_"..player.UserId)
if profile then
profile:AddUserId(player.UserId)
profile:Reconcile()
playerProfiles[player] = profile
playertabledata[player.Name] = CheckData(profile.Data, {})
AddNewTemplateData(player)
RetrieveDataFromTable(player)
-- Save player data when they leave
player.AncestryChanged:Connect(function()
if not player:IsDescendantOf(game) then
savePlayerData(player)
profile:Release()
playerProfiles[player] = nil
end
end)
else
player:Kick("Could not load data. Please try again later.")
end
end)
game.Players.PlayerRemoving:Connect(function(player)
savePlayerData(player)
local profile = playerProfiles[player]
if profile then
profile:Release()
playerProfiles[player] = nil
end
end)
game:BindToClose(function()
for _, player in pairs(game.Players:GetPlayers()) do
savePlayerData(player)
local profile = playerProfiles[player]
if profile then
profile:Release()
end
end
end)