So uhm, i was making stats for my game, and added the system and stuff, decided to make the stats save, everything works no errors, levels and xp saves but not the stats, idk why, pls help.
local DSS = game:GetService("DataStoreService")
local mainDS = DSS:GetDataStore("EzDataStore")
local levelEvent = RS:WaitForChild("LevelUp")
local function loadPlayerData(player)
local data
local success, errorMsg = pcall(function()
data = mainDS:GetAsync(player.UserId)
end)
if not success then
warn("error, cant recover player data " .. player.Name .. ": " .. errorMsg)
return
end
if data then
local leaderstats = player:FindFirstChild("leaderstats")
local statsFolder = player:FindFirstChild("statsFolder")
if leaderstats then
local level = leaderstats:FindFirstChild("Lvl")
local exp = leaderstats:FindFirstChild("Exp")
local reqexp = player:FindFirstChild("RequiredExp")
if level then level.Value = data[1] or level.Value end
if exp then exp.Value = data[2] or exp.Value end
if reqexp then reqexp.Value = data[3] or reqexp.Value end
end
if statsFolder then
local strength = statsFolder:FindFirstChild("strength")
local dexterity = statsFolder:FindFirstChild("dexterity")
local wits = statsFolder:FindFirstChild("wits")
local luck = statsFolder:FindFirstChild("luck")
if strength then strength.Value = data[4] or strength.Value end
if dexterity then dexterity.Value = data[5] or dexterity.Value end
if wits then wits.Value = data[6] or wits.Value end
if luck then luck.Value = data[7] or luck.Value end
end
end
end
local function savePlayerData(player)
local leaderstats = player:FindFirstChild("leaderstats")
local statsFolder = player:FindFirstChild("statsFolder")
if leaderstats and statsFolder then
local level = leaderstats:FindFirstChild("Lvl")
local exp = leaderstats:FindFirstChild("Exp")
local reqexp = player:FindFirstChild("RequiredExp")
local strength = statsFolder:FindFirstChild("strength")
local dexterity = statsFolder:FindFirstChild("dexterity")
local wits = statsFolder:FindFirstChild("wits")
local luck = statsFolder:FindFirstChild("luck")
local data = {
level and level.Value or 1,
exp and exp.Value or 0,
reqexp and reqexp.Value or 100,
strength and strength.Value or 1,
dexterity and dexterity.Value or 1,
wits and wits.Value or 1,
luck and luck.Value or 1
}
local success, errorMsg = pcall(function()
mainDS:SetAsync(player.UserId, data)
end)
if not success then
warn("Error during saving " .. player.Name .. ": " .. errorMsg)
end
end
end
game.Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder", player)
leaderstats.Name = "leaderstats"
local level = Instance.new("NumberValue", leaderstats)
level.Name = "Lvl"
level.Value = 1
local exp = Instance.new("NumberValue", leaderstats)
exp.Name = "Exp"
exp.Value = 0
local requiredExp = Instance.new("NumberValue", player)
requiredExp.Name = "RequiredExp"
requiredExp.Value = level.Value * 100
local statPoints = Instance.new("IntValue", player)
statPoints.Name = "statPoints"
statPoints.Value = 1
local statsFolder = Instance.new("Folder", player)
statsFolder.Name = "statsFolder"
local strength = Instance.new("IntValue", statsFolder)
strength.Name = "strength"
strength.Value = 1
local dexterity = Instance.new("IntValue", statsFolder)
dexterity.Name = "dexterity"
dexterity.Value = 1
local wits = Instance.new("IntValue", statsFolder)
wits.Name = "wits"
wits.Value = 1
local luck = Instance.new("IntValue", statsFolder)
luck.Name = "luck"
luck.Value = 1
loadPlayerData(player)
local statsGui = player.PlayerGui:WaitForChild("StatTestUI")
statsGui.Frame.StatPoints.Text = statPoints.Value
statsGui.Frame.Stats.Strength.statValue.Text = strength.Value
statsGui.Frame.Stats.Dexterity.statValue.Text = dexterity.Value
statsGui.Frame.Stats.Wits.statValue.Text = wits.Value
statsGui.Frame.Stats.Luck.statValue.Text = luck.Value
exp.Changed:Connect(function()
if exp.Value >= requiredExp.Value then
exp.Value = 0
level.Value = level.Value + 1
requiredExp.Value = level.Value * 100
end
end)
level.Changed:Connect(function()
statPoints.Value = statPoints.Value + 3
levelEvent:FireClient(player)
end)
end)
game.Players.PlayerRemoving:Connect(function(player)
savePlayerData(player)
end)
This is too much unorganized code but from what I can see this would be bad performance wise.
You are connecting an event to a function every time a player is added to the game, which would cause memory leaks.
Memory leak on the server, which would be particularlly bad because servers can last for days or weeks before ending. Which can make it feel like players are experiencing the problem everywhere.
You can simply use these events on a local script instead, and use remote events.
local Player = game.Players.LocalPlayer
local leaderstats = Player.leaderstats
leaderstats.level.Changed:Connect(function()
-- Fire remote events here and I think you know what to do
end)
Which would also prevent these memory leaks from happening by the way.
Edit: I forgot that the player/character does get deleted when they leave, but still.
I don’t know what you mean by stats not saving (if they are nil, 0 or 1), but I am generally unfamiliar with having Boolean operations when setting variables (in the table). I feel like you can just do:
if you are worried about any of the values being nil, then that is not a problem that should be tackled when saving the data. Rather you should make sure that the values are in the stats folder, you should be able to keep track of them if nothing else removes them.
Are you sure this will happen? Having two events connected to a player should not be a problem, even if there are 50 players in the game, only 100 events will be connected, and I have seen games with more than 100 events. I feel like the garbage collector should pick up the dropped events, are you saying that he should use :Disconnect() so that the events don’t accumulate?
I don’t think there is anything wrong with your suggestion, but I do not think it will cause much of a performance boost.
so i went back and used the old code i had, and this is what it does now:
local RS = game:GetService("ReplicatedStorage")
local levelEvent = RS:WaitForChild("LevelUp")
local DSS = game:GetService("DataStoreService")
local mainDS = DSS:GetDataStore("EzDataStore")
game.Players.PlayerAdded:Connect(function(plr)
plr.CharacterAdded:Connect(function(char)
local leaderstats = Instance.new("Folder", plr)
leaderstats.Name = "leaderstats"
local level = Instance.new("NumberValue", leaderstats)
level.Name = "Lvl"
level.Value = 1
local Exp = Instance.new("NumberValue", leaderstats)
Exp.Name = "Exp"
Exp.Value = 0
local RequiredExp = Instance.new("NumberValue", plr)
RequiredExp.Name = "RequiredExp"
RequiredExp.Value = level.Value * 100
local statPoints = Instance.new("IntValue", plr)
statPoints.Name = "statPoints"
statPoints.Value = 1
local statsFolder = Instance.new("Folder", plr)
statsFolder.Name = "statsFolder"
local strength = Instance.new("IntValue", statsFolder)
strength.Name = "strength"
strength.Value = 1
local dexterity = Instance.new("IntValue", statsFolder)
dexterity.Name = "dexterity"
dexterity.Value = 1
local wits = Instance.new("IntValue", statsFolder)
wits.Name = "wits"
wits.Value = 1
local luck = Instance.new("IntValue", statsFolder)
luck.Name = "luck"
luck.Value = 1
local statsGui = plr.PlayerGui:WaitForChild("StatTestUI")
statsGui.Frame.StatPoints.Text = statPoints.Value
statsGui.Frame.Stats.Strength.statValue.Text = strength.Value
statsGui.Frame.Stats.Dexterity.statValue.Text = dexterity.Value
statsGui.Frame.Stats.Wits.statValue.Text = wits.Value
statsGui.Frame.Stats.Luck.statValue.Text = luck.Value
Exp.Changed:Connect(function()
if Exp.Value >= RequiredExp.Value then
Exp.Value = 0
level.Value += 1
RequiredExp.Value = level.Value * 100
end
end)
local data = mainDS:GetAsync(plr.UserId)
if data then
if data[1] then level.Value = data[1] end
if data[2] then Exp.Value = data[2] end
if data[3] then RequiredExp.Value = data[3] end
if data[4] then strength.Value = data[4] end
if data[5] then dexterity.Value = data[5] end
if data[6] then wits.Value = data[6] end
if data[7] then luck.Value = data[7] end
if data[8] then statPoints.Value = data[8] end
end
end)
end)
game.Players.PlayerRemoving:Connect(function(player)
local leaderstats = player:WaitForChild("leaderstats")
local lvl = leaderstats:WaitForChild("Lvl")
local exp = leaderstats:WaitForChild("Exp")
local reqexp = player:WaitForChild("RequiredExp")
local statsFolder = player:WaitForChild("statsFolder")
local strength = statsFolder:WaitForChild("strength")
local dexterity = statsFolder:WaitForChild("dexterity")
local wits = statsFolder:WaitForChild("wits")
local luck = statsFolder:WaitForChild("luck")
local statPoints = player:WaitForChild("statPoints")
local data = {
lvl.Value,
exp.Value,
reqexp.Value,
strength.Value,
dexterity.Value,
wits.Value,
luck.Value,
statPoints.Value
}
local success, errorMsg = pcall(function()
mainDS:SetAsync(player.UserId, data)
end)
if not success then
warn("Failed to save data for player " .. player.Name .. ": " .. errorMsg)
end
end)
At least in this case, you’re setting the UI Values before you even load your data.
local data = mainDS:GetAsync(plr.UserId)
if data then
if data[1] then level.Value = data[1] end
if data[2] then Exp.Value = data[2] end
if data[3] then RequiredExp.Value = data[3] end
if data[4] then strength.Value = data[4] end
if data[5] then dexterity.Value = data[5] end
if data[6] then wits.Value = data[6] end
if data[7] then luck.Value = data[7] end
if data[8] then statPoints.Value = data[8] end
end
local statsGui = plr.PlayerGui:WaitForChild("StatTestUI")
statsGui.Frame.StatPoints.Text = statPoints.Value
statsGui.Frame.Stats.Strength.statValue.Text = strength.Value
statsGui.Frame.Stats.Dexterity.statValue.Text = dexterity.Value
statsGui.Frame.Stats.Wits.statValue.Text = wits.Value
statsGui.Frame.Stats.Luck.statValue.Text = luck.Value
Exp.Changed:Connect(function()
if Exp.Value >= RequiredExp.Value then
Exp.Value = 0
level.Value += 1
RequiredExp.Value = level.Value * 100
end
end)
local button = script.Parent
local plr = game.Players.LocalPlayer
local statsFolder = plr:WaitForChild("statsFolder")
local statsPoints = plr:WaitForChild("statPoints")
local stat = statsFolder:WaitForChild("dexterity")
button.MouseButton1Click:Connect(function()
if statsPoints.Value > 0 then
statsPoints.Value -= 1
stat.Value += 1
end
end)
I know what is going on, This is a classic Server-Client problem. You are changing the stats value in a client script. Which does not replicate to the server, then when you are saving the data. The server saves the server values, not the client values. What I recommend you do is use a remote event to fire from the client to the server, that then (in a server script) changes the stat values.
Something like this:
ServerScript:
yeah no, still doesnt work, like, it changes the value, but it doesnt save it, i think im just gonna rewrite the whole datastore code at this point, not worth spending more time on it tryna figure out whats wrong
Ok, welp, sorry that you couldent get it to work. Just a few more tips on debugging so if this happens again you will be more prepared.
Use print statements in order to make sure that the variables are correct. (print(luck), print(data), print(leaderstats) etc)
Use Breakpoints and Watch, a more advanced way to look at your code. The watch menu lets you look at every variable and function that your code has access to, and can make it very easy to verify that everything is running in the correct order.
local DataStoreService = game:GetService("DataStoreService")
local dataStore = DataStoreService:GetDataStore("CoolData")
--save data function
local function saveData(player)
local tableToSave = {
player.leaderstats.exp.Value;
player.leaderstats.lvl.Value;
player.requiredExp.Value;
player.statsFolder.strength.Value;
player.statsFolder.dexterity.Value;
player.statsFolder.wit.Value;
player.statsFolder.luck.Value;
player.sp.Value
}
local success, err = pcall(function()
dataStore:SetAsync(player.UserId, tableToSave) -- Save the data with the player UserId, and the table we wanna save
end)
if success then -- If the data has been saved
print("Data has been saved!")
else -- Else if the save failed
print("Data hasn't been saved!")
warn(err)
end
end
game.Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder", player)
leaderstats.Name = "leaderstats"
local lvl = Instance.new("IntValue")
lvl.Name = "lvl"
lvl.Value = 1
lvl.Parent = leaderstats
local exp = Instance.new("IntValue")
exp.Name = "exp"
exp.Parent = leaderstats
local requiredExp = Instance.new("IntValue")
requiredExp.Name = "requiredExp"
requiredExp.Value = lvl.Value * 100
requiredExp.Parent = player
--stats
local statsFolder = Instance.new("Folder")
statsFolder.Name = "statsFolder"
statsFolder.Parent = player
local sp = Instance.new("IntValue")
sp.Name = "sp"
sp.Value = 1
sp.Parent = player
local strength = Instance.new("IntValue")
strength.Name = "strength"
strength.Value = 1
strength.Parent = statsFolder
local wit = Instance.new("IntValue")
wit.Name = "wit"
wit.Value = 1
wit.Parent = statsFolder
local dexterity = Instance.new("IntValue")
dexterity.Name = "dexterity"
dexterity.Value = 1
dexterity.Parent = statsFolder
local luck = Instance.new("IntValue")
luck.Name = "luck"
luck.Value = 1
luck.Parent = statsFolder
local statsGui = player.PlayerGui:WaitForChild("StatTestUI")
statsGui.Frame.StatPoints.Text = sp.Value
statsGui.Frame.Stats.Strength.statValue.Text = strength.Value
statsGui.Frame.Stats.Dexterity.statValue.Text = dexterity.Value
statsGui.Frame.Stats.Wits.statValue.Text = wit.Value
statsGui.Frame.Stats.Luck.statValue.Text = luck.Value
exp.Changed:Connect(function(Changed)
if exp.Value >= requiredExp.Value then
exp.Value = 0
lvl.Value += 1
sp.Value += 4
requiredExp.Value = lvl.Value * 100
end
end)
local data
local success, err = pcall(function()
data = dataStore:GetAsync(player.UserId)
end)
if success and data then
exp.Value = data[1]
lvl.Value = data[2]
requiredExp.Value = data[3]
strength.Value = data[4]
dexterity.Value = data[5]
wit.Value = data[6]
luck.Value = data[7]
sp.Value = data[8]
else
print("player has no data")
exp.Value = 0
lvl.Value = 1
requiredExp.Value = 100
strength.Value = 1
dexterity.Value = 1
wit.Value = 1
luck.Value = 1
sp = 1
end
end)
game.Players.PlayerRemoving:Connect(function(player)
saveData(player)
end)
game:BindToClose(function() --when server shutdowns
for _, player in pairs(game.Players:GetPlayers()) do
saveData(player)
end
end)
I forgot that the player/character objects are usually destroyed anyway so it’s fine. I was wrong about that. Events are quite cheap on memory anyway but they can accumulate over time.