I’m trying to figure out the most efficient way to save and load data, but I can’t seem to for Slaying Simulator. Anybody able to help? (I’ve tried looking up tons of forum threads and wikis – I still get data loss reports)
I think that you’ll always get data loss reports due to some players internet speeds. (Their connection to the game server)
Are there any specific methods though? Idk if my data saving techniques are on par with everybody else’s lol
If you can tell us what your method for saving and loading data is then we can tell you if it’s fine or how to improve it.
What I usually do is encode all of the data in a json table, pcall a save function. Works pretty good.
Encoding your tables is not necessary while using datastores; it’s automatically converted for you.
This thread here covers everything you need: How would you use ROBLOX's datastorage API? - #3 by ForeverHD
There’s not enough information on this thread for us to be able to help you. When creating a support request, please include as much details as you can so we can help you better. For starters:
- Please include details of your current implementation or a code sample.
- Please indicate if you have made prior investigation to your data loss reports, or whether you’re just leaving it up to us to do that work for you. You should always investigate first before raising a question like this. This could be something you could solve easily alone.
- If you are willing to share your implementation as it is right now, please post your code. If you do, you can also hop the thread over to #development-support:requests-for-code-feedback since the code would work but you’re asking how to make it “more efficient”.
Can you send your save and load functions? I have never gotten reports of data loss with any of my recent games after failing to understand how to save and load data properly the first time around.
this is probably horrendous but here we go
it saves every 120 seconds and when the player leaves and on game shutdowns
loads when you join the game
function m:SaveData(p)
local data = m:GetPlayerData(p)
local id = p.UserId
local function saveData(store,value)
local t = 0
repeat
t = t + 1
local s,err = pcall(function()
store:SetAsync(id,value)
end)
if not s then wait(1) end
until s or t == 4
end
saveData(levelSave,data.Level)
saveData(killsSave,data.Kills)
saveData(gemSave,data.Gems)
saveData(prestigeSave,data.Prestiges)
saveData(itemSave,{Experience = data.Experience,
CurrentSword = data.CurrentSword,
Pets = data.Pets,
Codes = data.Codes,
Zones = data.Zones,
Quest = data.Quest,
CurrentAura = data.CurrentAura,
Auras = data.Auras})
end
function m:LoadData(p)
local id = p.UserId
local levelSuccess,levelLoad = pcall(function()
return levelSave:GetAsync(id)
end)
if not levelSuccess then
wait(1)
levelLoad = levelSave:GetAsync(id)
end
levelLoad = levelLoad or 1
local killsSuccess,killsLoad = pcall(function()
return killsSave:GetAsync(id)
end)
if not killsSuccess then
wait(1)
killsLoad = killsSave:GetAsync(id)
end
killsLoad = killsLoad or 0
local gemSuccess,gemLoad = pcall(function()
return gemSave:GetAsync(id)
end)
if not gemSuccess then
wait(1)
gemLoad = gemSave:GetAsync(id)
end
gemLoad = gemLoad or 0
local prestigeSuccess,prestigeLoad = pcall(function()
return prestigeSave:GetAsync(id)
end)
if not prestigeSuccess then
wait(1)
prestigeLoad = prestigeSave:GetAsync(id)
end
prestigeLoad = prestigeLoad or 0
local itemSuccess,itemLoad = pcall(function()
return itemSave:GetAsync(id)
end)
if not itemSuccess then
wait(1)
itemLoad = itemSave:GetAsync(id)
end
itemLoad = itemLoad or {
Quest = "None",
CurrentSword = "Wooden Sword",
Experience = 0,
Pets = {},
Codes = {},
Zones = {},
CurrentAura = "None",
Auras = {}
}
players[p].Level = levelLoad
players[p].Kills = killsLoad
players[p].Gems = gemLoad
players[p].Prestiges = prestigeLoad
if itemLoad then
for i,v in pairs(itemLoad) do
players[p][i] = v
end
else
players[p].Quest = "None"
players[p].CurrentSword = "Wooden Sword"
players[p].Experience = 0
players[p].Pets = {}
players[p].Codes = {}
players[p].Zones = {}
players[p].CurrentAura = "None"
players[p].Auras = {}
end
m:UpdateLeaderstats(p)
end
sorry ab the code being in the message, I’m still new to devforum lol
oh it auto translates it
If I were you, I would completely redo your entire data save/load system. There are instances where it’s possible your data doesn’t save.
local function saveData(store,value)
local t = 0
repeat
t = t + 1
local s,err = pcall(function()
store:SetAsync(id,value) end)
if not s then
wait(1)
end
until s or t == 4 end
One issue is that you’re using multiple saves and you stop saving after 4 attempts. Remove “t == 4” and use wait(6) instead. Honestly you should just put all your data into a table and use that in SetAsync.
local levelSuccess,levelLoad = pcall(function()
return levelSave:GetAsync(id)
end)
if not levelSuccess then
wait(1)
levelLoad = levelSave:GetAsync(id)
end
levelLoad = levelLoad or 1
local killsSuccess,killsLoad = pcall(function()
return killsSave:GetAsync(id)
end)
if not killsSuccess then
wait(1)
killsLoad = killsSave:GetAsync(id)
end
killsLoad = killsLoad or 0
local gemSuccess,gemLoad = pcall(function()
return gemSave:GetAsync(id)
end)
if not gemSuccess then
wait(1)
gemLoad = gemSave:GetAsync(id)
end
gemLoad = gemLoad or 0
Along with multiple SetAsyncs will result in multiple GetAsyncs. Again if all you did was one table you would only need to save and load that one table of data.
As for loading with your current system. You’re only trying to load their data twice(one for the first time, and one for if it fails). If those fail then you simply don’t load their data.
Another thing is that both of these eat your allowed requests for SetAsync and GetAsync per minute. With more people in the server, you will go through how many requests you’re allowed to use per minute pretty quick especially if they fail. Also back when I said “wait(6)” that was for the repeated check due to a 6 second cooldown.
Conclusion. Please(I don’t want to sound too harsh but it’s my honest opinion), just remake your system and make all the data into one table. This way you only need one of each request at the minimum for saving and/or loading.
Another precaution is to keep track of players who join and when their data loads. For my games, if a player leaves before their data is loaded, the game won’t overwrite their data with a blank template as well as it will stop the current attempts at loading their data if it didn’t work the first time.
I have been seeing a lot of cases of developers complaining about lost data, but I question if its actually Roblox’s fault or if developers just aren’t taking the necessary precautions(I don’t want this to sound like an attack).
I gotcha, but I do have a quick question about the loading.
If the player is new and their data isn’t logged into the datastore, then the pcall for loading would make the success false, and there would be no way of telling if it was a fetch error or the player was new, so how would I put it on a loop to keep trying to load and know whether the player has played before?
You want to be storing all player data that you can in a single DataStore
. Additionally, avoid random wait()
calls. Don’t manually merge data in like that. Instead, use a generic table merge function to achieve true flexibility. I recommend defining a template and merging loaded data into a copy of the template.
You are not checking if data is loaded before saving! You’re going to want to fix this. This is very irresponsible in games where players can spend R$ to get in-game rewards.
Set a flag in the player’s data describing whether or not the data was loaded successfully.
The pcall won’t return false. Just check if the value you got from GetAsync is nil once the pcall returns true. Pcall simply checks for errors. Not having any data on a new player isn’t an error.
Tomorrow when I get out of bed I’ll write a basic setup for data.
Gotcha, thanks
How’s this?
function m:SaveData(p)
local data = m:GetPlayerData(p)
local id = p.UserId
local function saveData(store,value)
repeat
local s,err = pcall(function()
store:SetAsync(id,value)
end)
if not s then wait(6) end
until s
end
if data.Loaded.Level then
saveData(levelSave,data.Level)
end
if data.Loaded.Kills then
saveData(killsSave,data.Kills)
end
if data.Loaded.Gems then
saveData(gemSave,data.Gems)
end
if data.Loaded.Prestiges then
saveData(prestigeSave,data.Prestiges)
end
if data.Loaded.Items then
saveData(itemSave,{Experience = data.Experience,
CurrentSword = data.CurrentSword,
Pets = data.Pets,
Codes = data.Codes,
Zones = data.Zones,
Quest = data.Quest,
CurrentAura = data.CurrentAura,
Auras = data.Auras})
end
end
function m:LoadData(p)
local id = p.UserId
local function loadData(store,default)
local returnedData = default
repeat
local success = pcall(function()
local data = store:GetAsync(id)
if data then returnedData = data end
end)
if not success then wait(6) end
until success
return returnedData or default
end
local levelLoad = loadData(levelSave,1)
players[p].Loaded.Level = true
local killsLoad = loadData(killsSave,0)
players[p].Loaded.Kills = true
local gemLoad = loadData(gemSave,0)
players[p].Loaded.Gems = true
local prestigeLoad = loadData(prestigeSave,0)
players[p].Loaded.Prestiges = true
local itemLoad = loadData(itemSave,{
Quest = "None",
CurrentSword = "Wooden Sword",
Experience = 0,
Pets = {},
Codes = {},
Zones = {},
CurrentAura = "None",
Auras = {}
})
players[p].Loaded.Items = true
players[p].Level = levelLoad
players[p].Kills = killsLoad
players[p].Gems = gemLoad
players[p].Prestiges = prestigeLoad
if itemLoad then
for i,v in pairs(itemLoad) do
players[p][i] = v
end
else
players[p].Quest = "None"
players[p].CurrentSword = "Wooden Sword"
players[p].Experience = 0
players[p].Pets = {}
players[p].Codes = {}
players[p].Zones = {}
players[p].CurrentAura = "None"
players[p].Auras = {}
end
m:UpdateLeaderstats(p)
end
Sorry for the delayed post(two 10-hour shifts at work the past few days), but here is my method of saving player data. It does checks such as if its new player data and makes sure that the player has had their data loaded before it is attempted to be saved.
Other features include that data is saved automatically every 2 minutes and data is saved when the server shuts down.
--ChuckXZ
local DataLoaded = {} --Table of player ids for if their data was loaded
local DataStoreService = game:GetService("DataStoreService") --Call the service
local PlayerData = DataStoreService:GetDataStore("PlayerData") --The player DataStore
local DataKey = "_Data" --String for the key of data in setting/getting the player's data
function PlayerIsInGame(PlayerName) --Just checks if the player is in game
return game.Players:FindFirstChild(PlayerName)
end
function NewDataTable() --Creates a table of data for out player
return {
--Just an example, table will be different depending on data
Kills = 0,
Deaths = 0,
Money = 100,
Wins = 0,
}
end
function SaveData(Player,DataTable) --Save the player's data with a supplied table of their data
if not DataLoaded[Player.UserId] then --Check if the user's data actually loaded before we try to save it
warn("Player's data did not save as no data was loaded | " .. Player.Name)
return --Kill the function as the player's data wasn't loaded in to save
end
--If we pass the test of whether they had data loaded in or not, we can continue
local Pcalled = false --Flag for our pcall check
warn("Saving player data | " .. Player.Name)
repeat
Pcalled = pcall(function() --Pcall check(s) so we don't run into issues with data saving
PlayerData:SetAsync(Player.UserId .. DataKey, DataTable) --Attempt to save the data
end)
if not Pcalled then --If there was an error saving the player's data
warn("Failed to save player data, Retrying | " .. Player.Name)
end
wait(6.1)
until Pcalled --Do not stop until our pcall test runs perfectly
warn("Data saved successfully! | " .. Player.Name)
end
function LoadData(Player) --Attempt to load the player's data
local Pcalled = false --Flag for our pcall check
local Data --The data we will soon get from out DataStore
warn("Attempting to load player data | " .. Player.Name)
repeat
Pcalled = pcall(function() --Pcall check(s) so we don't run into issues with data no loading
Data = PlayerData:GetAsync(Player.UserId .. DataKey) --Try to retrieve the player's data
end)
if not Pcalled then --If we experienced an error loading the player's data
warn("Failed to load player's data, Retrying | " .. Player.Name)
end
wait(6.1)
until Pcalled --Do not stop until our pcall test runs perfectly
if PlayerIsInGame(Player.Name) then --Just incase the player left befire their data loaded
DataLoaded[Player.UserId] = true --Let the server know that the data was successfully loaded
end
if Data then --Checks if the player has had a previous data save
warn("Data successfully loaded! | " .. Player.Name)
else --If the player doesn't have any data due to it being their first time here
warn("New data detected! | " .. Player.Name)
SaveData(Player,NewDataTable()) --Set the player up with some data
end
return Data --Return the data we retrieved(You can use it at this point)
end
--When a player joins
game.Players.PlayerAdded:Connect(function(Player)
DataLoaded[Player.UserId] = false --Set it so that the server knows their data has not been loaded
end)
--When a player leaves
game.Players.PlayerRemoving:Connect(function(Player)
SaveData(Player)
end)
--Loop to autosave data every 2 minutes
spawn(function()
while wait(121) do --Loop every 2 minutes and save every player's data
for _,Player in pairs(game.Players:GetPlayers()) do
spawn(function()
local Data = NewDataTable()
--Fill in the table(or make a function to do it)
SaveData(Player,Data)
end)
end
end
end)
--Save everyone's data when the server shuts down
game:BindToClose(function()
for _,Player in pairs(game.Players:GetPlayers()) do
spawn(function()
local Data = NewDataTable()
--Fill in the table(or make a function to do it)
SaveData(Player,Data)
end)
end
wait(20) --20 seconds of leeway for saving player data
end)
EDIT: Just made edits that I didn’t catch
I have the same problem.