OP’s problem isn’t about the server shutting down and player data not being saved, but instead about a player leaving and their data not saving.
Which falls back to the server shutting down when they leave. If the data is not saving, it’s through fault of OP’s code, or the server closing too quick. You yourself replied with this.
local Data = {
Strength = 0;
Rebirths = 0;
Cash = 1000;
Speed = 20;
Items = 0;
Inventory = nil
}
I tried changing
Inventory = nil
to
Inventory = {}
I am unsure if thats a better method if we are going to be saving stuff in the players Inventory
That’s most likely unimportant to the problem you have, as you were only changing a nil variable to a table. It’s due to how you’re trying to save it. You could try using Datastore2 and seeing if the player doesn’t lose their data.
Is there anyway I can just make my script for datastore2 without rewriting the whole thing? My game is unreleased so data loss isn’t a concern.
You could keep the basics of what you did without Datastore2 and transfer it. It’s best you keep the original script as a reference.
I can not find anything like what I am doing for datastore2 when looking on the devforum
I am referring to this part of the code
local Data = {
Strength = 0;
Rebirths = 0;
Cash = 1000;
Speed = 20;
Items = 0;
Inventory = nil
}
local playersavetable = {};
local function loadStarterData(Player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = Player
local Inventory = Instance.new("Folder")
Inventory.Name = "Inventory"
Inventory.Parent = leaderstats
for statname, statvalue in pairs(Data) do
if type(statvalue) == 'number' then
local intvalue = Instance.new("IntValue")
intvalue.Name = statname
intvalue.Value = statvalue
intvalue.Parent = leaderstats
else if type(statvalue) == 'boolean' then
local intvalue = Instance.new("BoolValue")
intvalue.Name = statname
intvalue.Value = statvalue
intvalue.Parent = Inventory
end
end
end
end
local function loadData(player)
local Data
local s, e = pcall(function()
Data = DataStore:GetAsync('UserId'..player.UserId)
end)
if s then
print (player.Name.."Data loaded")
else
print(player.Name.."Data failed to load")
end
if Data then
for statname, statvalue in pairs(Data) do
if type(statvalue) == "number" then
player.leaderstats[statname].Value = statvalue
else if type(statvalue) == "boolean" then
player.leaderstats.Inventory[statname].Value = statvalue
end
end
end
print(player.Name.."Data has been loaded")
else
print(player.Name.."No data found! generating..")
end
end
local function saveData(player)
--if RunService:IsStudio() then return end
local Data = {}
for _, stat in ipairs(player.leaderstats:GetChildren()) do
if not stat:IsA("Folder") then
Data[stat.Name] = stat.Value
end
end
local s, e = pcall(function()
DataStore:SetAsync('UserId'..player.UserId, Data)
end)
if s then
print(player.Name.."Data has been saved")
else
warn (player.Name.."Data failed to save"..e)
end
end
everything I’ve seen on datastore2 makes to process of creating and saving data much more manual and tedious, am I missing something?
Datastore2 prevents data loss more than the Datastore service does. Also, your SaveData function should have two extra args (since you’re connecting the PlayerRemoved event to the PlayerAdded event.)
I am aware of the advantages of datastore2, I am saying that from the examples and threads I have seen that the whole procces seems more manual and tedious. I mean like all the variables are created manually rather then just a few simple lines of code.
local function loadStarterData(Player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = Player
local Inventory = Instance.new("Folder")
Inventory.Name = "Inventory"
Inventory.Parent = leaderstats
for statname, statvalue in pairs(Data) do
if type(statvalue) == 'number' then
local intvalue = Instance.new("IntValue")
intvalue.Name = statname
intvalue.Value = statvalue
intvalue.Parent = leaderstats
else if type(statvalue) == 'boolean' then
local intvalue = Instance.new("BoolValue")
intvalue.Name = statname
intvalue.Value = statvalue
intvalue.Parent = Inventory
end
end
end
end
same with loading
if Data then
for statname, statvalue in pairs(Data) do
if type(statvalue) == "number" then
player.leaderstats[statname].Value = statvalue
else if type(statvalue) == "boolean" then
player.leaderstats.Inventory[statname].Value = statvalue
end
end
end
I have seen NOTHING like this for datastore2 so I am sorta stuck.
Then you should do some testing with Datastore2. Please read the rest of the reply too, since it’s likely important to help you.
Yes, I read the whole reply. I tried those two functions and I even tried modifying your suggestion when it failed.
and I also fixed my little mistake but got the same results
Could you send me the modified version of your PlayerAdded/Removing scripts?
Players.PlayerAdded:Connect(function(player)
playersavetable[player] = tick()
loadStarterData(player)
loadData(player)
local leaderstats = player:WaitForChild("leaderstats")
local Inventory = leaderstats:WaitForChild("Inventory")
Players.PlayerRemoving:Connect(function(vplayer)
if vplayer == player then
saveData(vplayer)
end
end)
end)
The modification was the one line of code as I mentioned earlier.
You forgot to add something. This is what you should’ve done:
Players.PlayerAdded:Connect(function(player)
playersavetable[player] = tick()
loadStarterData(player)
loadData(player)
local leaderstats = player:WaitForChild("leaderstats")
local Inventory = leaderstats:WaitForChild("Inventory")
Players.PlayerRemoving:Connect(function(vplayer)
if vplayer == player then
saveData(vplayer, leaderstats)
end
end)
end)
This would go with a modified save data function.
local function saveData(player, leaderstats)
--if RunService:IsStudio() then return end
local Data = {}
leaderstats = leaderstats or player.leaderstats
for _, stat in ipairs(leaderstats:GetChildren()) do
if not stat:IsA("Folder") then
Data[stat.Name] = stat.Value
end
end
local s, e = pcall(function()
DataStore:SetAsync('UserId'..player.UserId, Data)
end)
if s then
print(player.Name.."Data has been saved")
else
warn (player.Name.."Data failed to save"..e)
end
end
Why are you nesting the PlayerRemoving
listener inside PlayerAdded
’s? These connections are gonna build up and cause a memory leak. I linked my tutorial, which might help with player’s data not saving.
local function loadData(player, leaderstats)
local Data
local s, e = pcall(function()
Data = DataStore:GetAsync('UserId'..player.UserId)
end)
if s then
print (player.Name.."Data loaded")
else
print(player.Name.."Data failed to load")
end
should I add the “,leaderstats)” to the loadData as well?
The data you are trying to save will be loaded normally. You don’t need to add that if you’re trying to fix the data saving.
Oh, and I forgot to mention, you need to disconnect your PlayerRemoving event.
Players.PlayerAdded:Connect(function(player)
playersavetable[player] = tick()
loadStarterData(player)
loadData(player)
local leaderstats = player:WaitForChild("leaderstats")
local Inventory = leaderstats:WaitForChild("Inventory")
local RemoveEvent
RemoveEvent = Players.PlayerRemoving:Connect(function(vplayer)
if vplayer == player then
saveData(vplayer, leaderstats)
RemoveEvent:Disconnect()
end
end)
end)
also, do read incapaxx’s tutorial, it can help
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local DataStore = DataStoreService:GetDataStore("ss")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Data = {
Strength = 0;
Rebirths = 0;
Cash = 1000;
Speed = 20;
Items = 0;
Inventory = {}
}
local playersavetable = {};
local function loadStarterData(Player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = Player
local Inventory = Instance.new("Folder")
Inventory.Name = "Inventory"
Inventory.Parent = leaderstats
for statname, statvalue in pairs(Data) do
if type(statvalue) == 'number' then
local intvalue = Instance.new("IntValue")
intvalue.Name = statname
intvalue.Value = statvalue
intvalue.Parent = leaderstats
else if type(statvalue) == 'boolean' then
local intvalue = Instance.new("BoolValue")
intvalue.Name = statname
intvalue.Value = statvalue
intvalue.Parent = Inventory
end
end
end
end
local function loadData(player)
local Data
local s, e = pcall(function()
Data = DataStore:GetAsync('UserId'..player.UserId)
end)
if s then
print (player.Name.."Data loaded")
else
print(player.Name.."Data failed to load")
end
if Data then
for statname, statvalue in pairs(Data) do
if type(statvalue) == "number" then
player.leaderstats[statname].Value = statvalue
else if type(statvalue) == "boolean" then
player.leaderstats.Inventory[statname].Value = statvalue
end
end
end
print(player.Name.."Data has been loaded")
else
print(player.Name.."No data found! generating..")
end
end
local function saveData(player, leaderstats)
--if RunService:IsStudio() then return end
local Data = {}
leaderstats = leaderstats or player.leaderstats
for _, stat in ipairs(leaderstats:GetChildren()) do
if not stat:IsA("Folder") then
Data[stat.Name] = stat.Value
end
end
local s, e = pcall(function()
DataStore:SetAsync('UserId'..player.UserId, Data)
end)
if s then
print(player.Name.."Data has been saved")
else
warn (player.Name.."Data failed to save"..e)
end
end
ReplicatedStorage.SaveEvent.OnServerEvent:Connect(function(player)
saveData(player)
end)
Players.PlayerAdded:Connect(function(player)
playersavetable[player] = tick()
loadStarterData(player)
loadData(player)
local leaderstats = player:WaitForChild("leaderstats")
local Inventory = leaderstats:WaitForChild("Inventory")
local RemoveEvent
RemoveEvent = Players.PlayerRemoving:Connect(function(vplayer)
if vplayer == player then
saveData(vplayer, leaderstats)
RemoveEvent:Disconnect()
end
end)
end)
This is the updated script, having the same results as before as far as I can see. I will check incapaxx’s tutorial
Ok, this is the function I came up with
game:BindToClose(function()
for _, client in ipairs(Players:GetPlayers()) do
spawn(function()
saveData(client)
end)
end
end)
I have added this function to my script at the end of it.
EDIT: I posted to soon lol, the inventory still isn’t saving sigh
EDIT 2: One error that may be unrelated in the output is " [ InsertService cannot be used to load assets from the client]"
Your original code is working fine? lol… ( Just tested it ) and also I would never let the client tell the server when to save data if I were you. ( I’m talking about the remoteevent )
Instead have a loop that saves data every 60 seconds or so, save data when player leaves and on BindToClose.
I also quickly cleaned up the code if you want I could give you that ( though I am still doing exactly what you’re doing like using SetAsync which is kind of bad… and I am still using your variable naming kindish )
I think your problem is that you’re not updating the values from the server but from the client, maybe you should consider looking at your value changing code and use RemoteEvents and such if you’re not already doing that, or you’re just testing on studio without the game being published/ not having the “Enable Studio Access to API Services” on.
You can find that inside Game Settings → Options