Hello, I’ve been working on an ‘‘AFK FOR UGC’’ game. The drop went well, but I’ve noticed a couple people experiencing data loss.
I’d like some recommendations on how to update my current data module and possibly save data in a better way in the future.
ModuleScript in ServerStorage:
local DataStoreService: DataStoreService = game:GetService("DataStoreService")
local MarketplaceService: MarketplaceService = game:GetService("MarketplaceService")
local Players: Players = game:GetService("Players")
local dataStores = {
-- Misc
["Time"] = DataStoreService:GetDataStore("PlayerTime");
["Redeemed"] = DataStoreService:GetDataStore("PlayerRedeemed");
["Vip Bonus"] = DataStoreService:GetDataStore("ReceivedVipBonus");
-- Character Position
["Last Position X"] = DataStoreService:GetDataStore("PlayerPositionX");
["Last Position Y"] = DataStoreService:GetDataStore("PlayerPositionY");
["Last Position Z"] = DataStoreService:GetDataStore("PlayerPositionZ");
-- Boost Potions
["Red Boost"] = DataStoreService:GetDataStore("PlayerRedBoost");
["Blue Boost"] = DataStoreService:GetDataStore("PlayerBlueBoost");
["Green Boost"] = DataStoreService:GetDataStore("PlayerGreenBoost");
["Pink Boost"] = DataStoreService:GetDataStore("PlayerPinkBoost");
["Purple Boost"] = DataStoreService:GetDataStore("PlayerPurpleBoost");
["Golden Boost"] = DataStoreService:GetDataStore("PlayerGoldenBoost");
}
local dataModule = {}
function dataModule.getData(player: Player, dataName: string)
local data
local success, errorMessage = pcall(function()
local dataKey: string = "Player_"..player.UserId
local dataIndex = dataStores[dataName]
data = dataIndex:GetAsync(dataKey)
if not data and dataName == "Time" then -- Defaults new player time to 0
data = 0
elseif not data and dataName == "Redeemed" then -- Defaults new player redeemed to 0
data = 0
elseif not data and dataName == "Last Position X" then
data = 0
elseif not data and dataName == "Last Position Y" then
data = 0
elseif not data and dataName == "Last Position Z" then
data = 0
elseif not data and dataName == "Red Boost" then -- Defaults new player Red Boost time to 0
data = 0
elseif not data and dataName == "Blue Boost" then -- Defaults new player Blue Boost time to 0
data = 0
elseif not data and dataName == "Green Boost" then -- Defaults new player Green Boost time to 0
data = 0
elseif not data and dataName == "Pink Boost" then -- Defaults new player Pink Boost time to 0
data = 0
elseif not data and dataName == "Purple Boost" then -- Defaults new player Purple Boost time to 0
data = 0
elseif not data and dataName == "Golden Boost" then -- Defaults new player Golden Boost time to 0
data = 0
end
return data
end)
if success then
return data
else
warn("Error while getting data: " .. errorMessage)
end
end
function dataModule.saveData(player: Player, dataName: string, newValue: any)
local data
local success, errorMessage = pcall(function()
local dataKey: string = "Player_"..player.UserId
local dataIndex = dataStores[dataName]
data = dataIndex:SetAsync(dataKey, newValue)
return data
end)
if success then
return
else
warn("Error while saving data: " .. errorMessage)
end
end
return dataModule
Script in ServerScriptService:
local Players: Players = game:GetService("Players")
local RunService: RunService = game:GetService("RunService")
local ServerStorage: ServerStorage = game:GetService("ServerStorage")
local dataModule = require(ServerStorage:WaitForChild("DataModule"))
local function onPlayerAdded(player: Player)
player:SetAttribute("IsLoaded", false)
local playerStats: Folder = Instance.new("Folder")
playerStats.Name = "leaderstats"
playerStats.Parent = player
local playerTime: IntValue = Instance.new("IntValue")
playerTime.Name = "Time"
playerTime.Parent = playerStats
local playerRedeemed: IntValue = Instance.new("IntValue")
playerRedeemed.Name = "Redeemed"
playerRedeemed.Parent = playerStats
-- Boost Potions
player:SetAttribute("RedBoostTime", 0)
player:SetAttribute("BlueBoostTime", 0)
player:SetAttribute("GreenBoostTime", 0)
player:SetAttribute("PinkBoostTime", 0)
player:SetAttribute("PurpleBoostTime", 0)
player:SetAttribute("GoldenBoostTime", 0)
local success, errorMessage = pcall(function()
playerTime.Value = dataModule.getData(player, "Time")
playerRedeemed.Value = dataModule.getData(player, "Redeemed")
player:SetAttribute("IsLoaded", true)
player:SetAttribute("RedBoostTime", dataModule.getData(player, "Red Boost"))
player:SetAttribute("BlueBoostTime", dataModule.getData(player, "Blue Boost"))
player:SetAttribute("GreenBoostTime", dataModule.getData(player, "Green Boost"))
player:SetAttribute("PinkBoostTime", dataModule.getData(player, "Pink Boost"))
player:SetAttribute("PurpleBoostTime", dataModule.getData(player, "Purple Boost"))
player:SetAttribute("GoldenBoostTime", dataModule.getData(player, "Golden Boost"))
end)
if not success then
warn("Error while loading player's stats: " .. errorMessage)
player:Kick("Unable to load data, to avoid data corruption, your data was not saved.")
end
local function autoSaveTime()
while task.wait(60) do
dataModule.saveData(player, "Time", playerTime.Value)
-- Boost Potions
dataModule.saveData(player, "Red Boost", player:GetAttribute("RedBoostTime"))
dataModule.saveData(player, "Blue Boost", player:GetAttribute("BlueBoostTime"))
dataModule.saveData(player, "Green Boost", player:GetAttribute("GreenBoostTime"))
dataModule.saveData(player, "Pink Boost", player:GetAttribute("PinkBoostTime"))
dataModule.saveData(player, "Purple Boost", player:GetAttribute("PurpleBoostTime"))
dataModule.saveData(player, "Golden Boost", player:GetAttribute("GoldenBoostTime"))
end
end
local autoSaveCoroutine = coroutine.create(autoSaveTime)
coroutine.resume(autoSaveCoroutine)
local function onPlayerRemoving(playerRemoved: Player)
if playerRemoved ~= player then return end
coroutine.close(autoSaveCoroutine)
dataModule.saveData(player, "Time", playerTime.Value)
dataModule.saveData(player, "Redeemed", playerRedeemed.Value)
-- Boost Potions
dataModule.saveData(player, "Red Boost", player:GetAttribute("RedBoostTime"))
dataModule.saveData(player, "Blue Boost", player:GetAttribute("BlueBoostTime"))
dataModule.saveData(player, "Green Boost", player:GetAttribute("GreenBoostTime"))
dataModule.saveData(player, "Pink Boost", player:GetAttribute("PinkBoostTime"))
dataModule.saveData(player, "Purple Boost", player:GetAttribute("PurpleBoostTime"))
dataModule.saveData(player, "Golden Boost", player:GetAttribute("GoldenBoostTime"))
end
Players.PlayerRemoving:Connect(onPlayerRemoving)
end
Players.PlayerAdded:Connect(onPlayerAdded)
game:BindToClose(function()
if RunService:IsStudio() then return end
for _, player: Player in Players:GetPlayers() do
local playerStats: Folder? = player:FindFirstChild("leaderstats") :: Folder
local playerTime: IntValue? = if playerStats and playerStats:FindFirstChild("Time") then playerStats:FindFirstChild("Time") :: IntValue else nil
local playerRedeemed: IntValue? = if playerStats and playerStats:FindFirstChild("Redeemed") then playerStats:FindFirstChild("Redeemed") :: IntValue else nil
if not playerTime or not playerRedeemed then return end
dataModule.saveData(player, "Time", playerTime.Value)
dataModule.saveData(player, "Redeemed", playerRedeemed.Value)
-- Boost Potions
dataModule.saveData(player, "Red Boost", player:GetAttribute("RedBoostTime"))
dataModule.saveData(player, "Blue Boost", player:GetAttribute("BlueBoostTime"))
dataModule.saveData(player, "Green Boost", player:GetAttribute("GreenBoostTime"))
dataModule.saveData(player, "Pink Boost", player:GetAttribute("PinkBoostTime"))
dataModule.saveData(player, "Purple Boost", player:GetAttribute("PurpleBoostTime"))
dataModule.saveData(player, "Golden Boost", player:GetAttribute("GoldenBoostTime"))
end
end)
Thanks in advance!