Hi, I’ve been trying to bring an old RPG back to life and to do so I had to update the old data persistence script with a new datastore script. I’ve gotten data scripts to work before and I have watched tutorials on how to properly do datastore from alvinblox. I’ve also scoured through devforum the past couple days looking at every datastore topic I could even find and while those helped me fix a LOT of my problems, some new ones that I just can’t find answers to popped up as well. For some reason this script just will not save the players data. Please see if you can find anything wrong with my script so far.
Note: the blarglepart is simply to help me easily check if player data is saving by updating the values
local dataStoreService = game:GetService("DataStoreService")
local playerService = game:GetService("Players")
local dataStore = dataStoreService:GetDataStore("Data")
local playerCount = 0
local playerLeaveEvent = Instance.new("BindableEvent")
playerService.PlayerAdded:Connect(function(player)
local level = Instance.new("IntValue")
level.Name = "Level"
level.Value = 1
level.Parent = player
local xp = Instance.new("IntValue")
xp.Name = "Current"
xp.Value = 0
xp.Parent = level
local maxExp = Instance.new("IntValue")
maxExp.Name = "Max"
maxExp.Value = 100
maxExp.Parent = level
local gold = Instance.new("IntValue")
gold.Name = "Gold"
gold.Value = 0
gold.Parent = level
xp.Changed:Connect(function(val)
if xp.Value >= maxExp.Value then
-- Do stuff
level.Value += 1
xp.Value = xp.Value - maxExp.Value
maxExp.Value *= 2
end
end)
local savedLevel
local savedExp
local savedGold
local success = pcall(function()
savedLevel = dataStore:GetAsync(player.UserId)
savedExp = dataStore:GetAsync(player.UserId)
savedGold = dataStore:GetAsync(player.UserId)
end)
if success then
if savedLevel and savedExp and savedGold then
level.Value = savedLevel
xp.Value = savedExp
gold.Value = savedGold
end
else
level.Value = 1
xp.Value = 0
gold.Value = 10
end
playerCount += 1
end)
playerService.PlayerRemoving:Connect(function(player)
local level = player.Level
local xp = player.Level.Current
local gold = player.Level.Gold
pcall(function()
dataStore:SetAsync(player.UserId, level.Value)
dataStore:SetAsync(player.UserId, xp.Value)
dataStore:SetAsync(player.UserId, gold.Value)
end)
playerCount -= 1
playerLeaveEvent:Fire()
end)
workspace:WaitForChild("BlarglePart").ClickDetector.MouseClick:Connect(function(player)
player.Level.Current.Value += 29
end)
game:BindToClose(function()
while playerCount > 0 do
playerLeaveEvent.Event:Wait()
end
end)
No, the playerLeaveEvent() function is simply for the BindToClose(function(). It allows me to tell it to wait for the event to fire, and since the event doesn’t fire until the saving of the characters values is done, it keeps the game from closing before it finishes, thus helping ensure the players data is saved. I believe that was from one of AlvinBlox’s videos I watched.
You cannot save multiple values to a same DataStore, instead use a table to save your values. I’ve covered a similar situation in this post: Datastore sets every value to 0 - #9 by Sarchyx
I’ve tested both in Roblox Studio AND on a published game and neither work. In fact, in the published game their are MORE problems but I believe they will be fixed either once I fix this problem, or afterwards through some debugging. I’m sorry, I should have stated in the Original Post.
You are a legend my friend thank you! This single-handedly fixed all the problems I had! May I ask what exactly does changing it like this do?? Every other datastore I’ve done was the other way and it worked fine. I can’t help but think this is due to my lack of understanding of SetAsync and GetAsync.
What Denis said has already fixed my problem but I had plans to make it a table once I was finished fixing this problem. The DataStore is working for all three currently like this but I am aware that using a table is much more efficient. I will definitely check that post to better help me with the change, thank you!
OH YEA. I totally forgot to do that… Would have probably been able to figure out my own problem had I done that from the start smh. Thank you for the tip!
what it does is it’s saving different keys for different values instead of the same for all, which lets you, in turn, get those values separately.
If you want to use tables, please also JSONEncode them at :SetAsync() and Decode them at GetAsync() because roblox tends to be weird with tables and it’s a lot more efficient this way too.
Though I don’t recommend using tables if you don’t know anything about how they work since it could confuse things even more
If you need any more help let me or @COUNTYL1MITS know!
not really, you’re essentially just getting a value and assigning it to everything else, since that value exist and everything else checks out, it won’t error at all
local Players = game:GetService("Players")
local DataStores = game:GetService("DataStoreService")
local DataStore = DataStores:GetDataStore("DataStore")
local PlayerLeftEvent = Instance.new("BindableEvent")
local ProtectedCall = pcall
Players.PlayerAdded:Connect(function(Player)
local Level = Instance.new("IntValue")
Level.Name = "Level"
Level.Parent = Player
local Exp = Instance.new("IntValue")
Exp.Name = "Current"
Exp.Parent = Level
local MaxExp = Instance.new("IntValue")
MaxExp.Name = "Max"
MaxExp.Parent = Level
local Gold = Instance.new("IntValue")
Gold.Name = "Gold"
Gold.Value = 0
Gold.Parent = Level
Exp.Changed:Connect(function(NewExp)
if NewExp >= MaxExp.Value then
Level.Value += 1
Exp.Value -= MaxExp.Value
MaxExp.Value *= 2
end
end)
local Success, Result = ProtectedCall(function()
return DataStore:GetAsync(Player.UserId)
end)
if Success then
if Result then
if type(Result) == "table" then
Level.Value = Result[1] or 1
Exp.Value = Result[2] or 0
MaxExp.Value = Result[3] or 100
Gold.Value = Result[4] or 0
end
end
else
warn(Result)
end
end)
Players.PlayerRemoving:Connect(function(Player)
local Success, Result = ProtectedCall(function()
return DataStore:SetAsync(Player.UserId, {Player.Level.Value, Player.Level.Exp.Value, Player.Level.MaxExp.Value, Player.Level.Gold.Value})
end)
if Success then
else
warn(Result)
end
PlayerLeftEvent:Fire()
end)
game:BindToClose(function()
for _, Player in ipairs(Players:GetPlayers()) do
local Success, Result = ProtectedCall(function()
return DataStore:SetAsync(Player.UserId, {Player.Level.Value, Player.Level.Exp.Value, Player.Level.MaxExp.Value, Player.Level.Gold.Value})
end)
if Success then
else
warn(Result)
end
end
end)
Bare in mind that you’re not parenting the instanced BindableEvent anywhere so its parent is nil.
This saves player stats using a table as suggested and will also save “MaxExp” as well. I’ve also binded a function to BindToClose so that if/when the server is shutdown every player’s stats are saved.