Datastore difficulties

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.

1 Like
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.

Here it is once again

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

1 Like

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]"

1 Like

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
image