Need Help Saving a Table's Data, and Applying it Into a Folder (8/16)

Hello, I have been working on a script that creates a table, loads one if it already exists (Gives default one if there isn’t), and saves the table when the player leaves the game. I have followed videos such as this one, however the game doesn’t save.

Here is the line that saves the data: (plrStats is the table containing the player’s information)

myDataStore:SetAsync(player.UserId.."-DATA", plrStats)

and this one loads the data:

	local data
	local success, errormessage = pcall(function()
		data = myDataStore:GetAsync(player.UserId.."-DATA")
	end)
	
	if success then
		print("Data Loaded Successfully")
	else
		warn("Error loading Save Data",errormessage)
	end
	
end)

There are no errors in the output, so I’m a little lost.

1 Like

You sure u enabled Studio Access To API Services?

1 Like

I completley forgot that API Services existed, however after enabling it, it still doesn’t work.

In my script I have a Folder in the Player with the same stats as the table, and when the game loads, it puts those stats into the folder so that they sync, and so I can access them outside of the script with the table. In the lines right before the data is saved, I set the table values to their respective folder value.

plrStats.inventory.weapons = {player.PlayerItems.Weapons}
		plrStats.Level = player.leaderstats.Level
		plrStats["Summon Tokens"].Value = player.PlayerItems["Summon Tokens"].Value
		myDataStore:SetAsync(player.UserId.."-DATA", plrStats)

Because you are saving the Folder, Not the values, I would like to see your full code.

Sure. Here is my full script:

local DataStoreService = game:GetService("DataStoreService")
local myDataStore = DataStoreService:GetDataStore("myDataStore")
local plrStats = {
	inventory = {
	weapons = {},
	materials = {}
	},
	
	["Credits"] = 0,
	["Summon Tokens"] = 0,
	["Legendary Pity"] = 0,
	["Epic Pity"] = 0,
	["Element"] = 0, --1(Fire),2(Water),3(Ice),4(Nature),5(Lightning),6(Earth),7(Light),8(Dark),9(Light),10(Psychic)
	
	--Customizable Stats
	["Skin Tone"] = "Default",
	["Hair"] = "None",
	["Hat"] = "None",
	["Shirt"] = "Default",
	["Pants"] = "Default",
	["Shoes"] = "Default",

	--Base Stats as Integers
	["Level"] = 1,
	["Base HP"] = 100,
	["Base Atk"] = 10,
	["Base Def"] = 0,
	["Base MP"] = 10,
	["Base Crit %"] = .05,
	["Base Crit Dmg"] = .5,
	["Base Shield DMG Resist"] = 0.00,

	--Added Stats
	["HP Bonus"] = 0, 
	["Atk Bonus"] = 0, 
	["Def Bonus"] = 0, 
	["MP Bonus"] = 0,
	["Crit% Bonus"] = 0.00,
	["Crit Dmg Bonus"] = 0.00,
	["Shield DMG Resist Bonus"] = 0,

	--Equipped Items	
	["Current Sword"] = 1,
	["Current Shield"] = 1


}


game.Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local displayLevel = Instance.new("IntValue")
	displayLevel.Name = "Level"
	displayLevel.Value = plrStats.Level
	displayLevel.Parent = leaderstats
	
	local folder = Instance.new("Folder")
	folder.Name = "PlayerItems"
	folder.Parent = player
	local weapons = Instance.new("Folder")
	weapons.Name = "Weapons"
	weapons.Parent = folder
	for i,v in ipairs(plrStats.inventory.weapons) do
		local vClone = v:Clone()
		v.Parent = weapons
	end
	
	local summonTokens = Instance.new("IntValue")
	summonTokens.Name = "Summon Tokens"
	summonTokens.Value = plrStats["Summon Tokens"]
	summonTokens.Parent = player.PlayerItems
	
	
	local data
	local success, errormessage = pcall(function()
		data = myDataStore:GetAsync(player.UserId.."-DATA")
	end)
	
	if success then
		print("Data Loaded Successfully")
		--credits.Value = data
	else
		warn("Error loading Save Data",errormessage)
	end
	
end)

game.Players.PlayerRemoving:Connect(function(player)
	local success, errormessage = pcall(function()		
		plrStats.inventory.weapons = {player.PlayerItems.Weapons}
		plrStats.Level = player.leaderstats.Level
		plrStats["Summon Tokens"].Value = player.PlayerItems["Summon Tokens"].Value
		myDataStore:SetAsync(player.UserId.."-DATA", plrStats)
	end)
	
	if success then
		print("Data Successfully Saved.")
	else
		warn("There was an Error Saving Data",errormessage)
	end
	
end)

In studio, PlayerRemoved rarely fires, so you have to also save the data in the game:BindToClose function.

1 Like

Should I replace the function, or just duplicate it and let it fire for both?

I have edited it however this still doesn’t work.

local DataStoreService = game:GetService("DataStoreService")
local myDataStore = DataStoreService:GetDataStore("myDataStore")
local plrStats = {
	inventory = {
	weapons = {},
	materials = {}
	},
	
	["Credits"] = 0,
	["Summon Tokens"] = 0,
	["Legendary Pity"] = 0,
	["Epic Pity"] = 0,
	["Element"] = 0, --1(Fire),2(Water),3(Ice),4(Nature),5(Lightning),6(Earth),7(Light),8(Dark),9(Light),10(Psychic)
	
	--Customizable Stats
	["Skin Tone"] = "Default",
	["Hair"] = "None",
	["Hat"] = "None",
	["Shirt"] = "Default",
	["Pants"] = "Default",
	["Shoes"] = "Default",

	--Base Stats as Integers
	["Level"] = 1,
	["Base HP"] = 100,
	["Base Atk"] = 10,
	["Base Def"] = 0,
	["Base MP"] = 10,
	["Base Crit %"] = .05,
	["Base Crit Dmg"] = .5,
	["Base Shield DMG Resist"] = 0.00,

	--Added Stats
	["HP Bonus"] = 0, 
	["Atk Bonus"] = 0, 
	["Def Bonus"] = 0, 
	["MP Bonus"] = 0,
	["Crit% Bonus"] = 0.00,
	["Crit Dmg Bonus"] = 0.00,
	["Shield DMG Resist Bonus"] = 0,

	--Equipped Items	
	["Current Sword"] = 1,
	["Current Shield"] = 1


}
local PLAYER

game.Players.PlayerAdded:Connect(function(player)
	PLAYER = player
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local displayLevel = Instance.new("IntValue")
	displayLevel.Name = "Level"
	displayLevel.Value = plrStats.Level
	displayLevel.Parent = leaderstats
	
	local folder = Instance.new("Folder")
	folder.Name = "PlayerItems"
	folder.Parent = player
	local weapons = Instance.new("Folder")
	weapons.Name = "Weapons"
	weapons.Parent = folder
	for i,v in ipairs(plrStats.inventory.weapons) do
		local vClone = v:Clone()
		v.Parent = weapons
	end
	
	local summonTokens = Instance.new("IntValue")
	summonTokens.Name = "Summon Tokens"
	summonTokens.Value = plrStats["Summon Tokens"]
	summonTokens.Parent = player.PlayerItems
	
	
	local data
	local success, errormessage = pcall(function()
		data = myDataStore:GetAsync(player.UserId.."-DATA")
	end)
	
	if success then
		print("Data Loaded Successfully")
		--credits.Value = data
	else
		warn("Error loading Save Data",errormessage)
	end
	
end)
local player = PLAYER
game:BindToClose(function()
	local success, errormessage = pcall(function()		
		plrStats.inventory.weapons = {player.PlayerItems.Weapons}
		plrStats.Level.Value = player.leaderstats.Level.Value
		plrStats["Summon Tokens"].Value = player.PlayerItems["Summon Tokens"].Value
		myDataStore:SetAsync(player.UserId.."-DATA", plrStats)
	end)
	
	if success then
		print("Data Successfully Saved.")
	else
		warn("There was an Error Saving Data",errormessage)
	end
	
end)

Now that the function that saves the data can no longer use the player parameter, I’m not sure if it’s working. I tried making a variable that defines the player in the PlayerAdded function with an external variable, allowing the player to use that variable by the time the BindToClose fires, however again, it still isn’t functioning.

In the BindToClose function, you have to loop through all the players in the game and save their data sequentially.

So would I do something like:

for i, v in pairs(game.Players) do
--Idk what to do after this

Do this

game:BindToClose(function()
for i, Player in pairs(game.Players:GetPlayers()) do
-- Add the saving for the player like in removed event
end
end)
local DataStoreService = game:GetService("DataStoreService")
local myDataStore = DataStoreService:GetDataStore("myDataStore")
local plrStats = {
	inventory = {
	weapons = {},
	materials = {}
	},
	
	["Credits"] = 0,
	["Summon Tokens"] = 0,
	["Legendary Pity"] = 0,
	["Epic Pity"] = 0,
	["Element"] = 0, --1(Fire),2(Water),3(Ice),4(Nature),5(Lightning),6(Earth),7(Light),8(Dark),9(Light),10(Psychic)
	
	--Customizable Stats
	["Skin Tone"] = "Default",
	["Hair"] = "None",
	["Hat"] = "None",
	["Shirt"] = "Default",
	["Pants"] = "Default",
	["Shoes"] = "Default",

	--Base Stats as Integers
	["Level"] = 1,
	["Base HP"] = 100,
	["Base Atk"] = 10,
	["Base Def"] = 0,
	["Base MP"] = 10,
	["Base Crit %"] = .05,
	["Base Crit Dmg"] = .5,
	["Base Shield DMG Resist"] = 0.00,

	--Added Stats
	["HP Bonus"] = 0, 
	["Atk Bonus"] = 0, 
	["Def Bonus"] = 0, 
	["MP Bonus"] = 0,
	["Crit% Bonus"] = 0.00,
	["Crit Dmg Bonus"] = 0.00,
	["Shield DMG Resist Bonus"] = 0,

	--Equipped Items	
	["Current Sword"] = 1,
	["Current Shield"] = 1


}
local PLAYER

game.Players.PlayerAdded:Connect(function(player)
	PLAYER = player
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local displayLevel = Instance.new("IntValue")
	displayLevel.Name = "Level"
	displayLevel.Value = plrStats.Level
	displayLevel.Parent = leaderstats
	
	local folder = Instance.new("Folder")
	folder.Name = "PlayerItems"
	folder.Parent = player
	local weapons = Instance.new("Folder")
	weapons.Name = "Weapons"
	weapons.Parent = folder
	for i,v in ipairs(plrStats.inventory.weapons) do
		local vClone = v:Clone()
		v.Parent = weapons
	end
	
	local summonTokens = Instance.new("IntValue")
	summonTokens.Name = "Summon Tokens"
	summonTokens.Value = plrStats["Summon Tokens"]
	summonTokens.Parent = player.PlayerItems
	
	
	local data
	local success, errormessage = pcall(function()
		data = myDataStore:GetAsync(player.UserId.."-DATA")
	end)
	
	if success then
		print("Data Loaded Successfully")
		--credits.Value = data
	else
		warn("Error loading Save Data",errormessage)
	end
	
end)
local player = PLAYER

game:BindToClose(function()
	for i, player in pairs(game.Players:GetPlayers()) do
		local success, errormessage = pcall(function()		
			plrStats.inventory.weapons = {player.PlayerItems.Weapons}
			plrStats.Level.Value = player.leaderstats.Level.Value
			plrStats["Summon Tokens"].Value = player.PlayerItems["Summon Tokens"].Value
			myDataStore:SetAsync(player.UserId.."-DATA", plrStats)
		end)

		if success then
			print("Data Successfully Saved.")
		else
			warn("There was an Error Saving Data",errormessage)
		end

	end
end)

It still does not work, and there is still no error information in the output.

Here is a version of my code without the merging of the folder and table data. Anything look wrong about it?

local DataStoreService = game:GetService("DataStoreService")
local myDataStore = DataStoreService:GetDataStore("myDataStore")
local plrStats = {
	inventory = {
	weapons = {},
	materials = {}
	},
	
	["Credits"] = 0,
	["Summon Tokens"] = 0,
	["Legendary Pity"] = 0,
	["Epic Pity"] = 0,
	["Element"] = 0, --1(Fire),2(Water),3(Ice),4(Nature),5(Lightning),6(Earth),7(Light),8(Dark),9(Light),10(Psychic)
	
	--Customizable Stats
	["Skin Tone"] = "Default",
	["Hair"] = "None",
	["Hat"] = "None",
	["Shirt"] = "Default",
	["Pants"] = "Default",
	["Shoes"] = "Default",

	--Base Stats as Integers
	["Level"] = 1,
	["Base HP"] = 100,
	["Base Atk"] = 10,
	["Base Def"] = 0,
	["Base MP"] = 10,
	["Base Crit %"] = .05,
	["Base Crit Dmg"] = .5,
	["Base Shield DMG Resist"] = 0.00,

	--Added Stats
	["HP Bonus"] = 0, 
	["Atk Bonus"] = 0, 
	["Def Bonus"] = 0, 
	["MP Bonus"] = 0,
	["Crit% Bonus"] = 0.00,
	["Crit Dmg Bonus"] = 0.00,
	["Shield DMG Resist Bonus"] = 0,

	--Equipped Items	
	["Current Sword"] = 1,
	["Current Shield"] = 1


}

game.Players.PlayerAdded:Connect(function(player)

	local data
	local success, errormessage = pcall(function()
		data = myDataStore:GetAsync(player.UserId.."-DATA")
	end)
	
	if success then
		print("Data Loaded Successfully")
		--credits.Value = data
	else
		warn("Error loading Save Data",errormessage)
	end
	
end)

function Save()
	for i, player in pairs(game.Players:GetPlayers()) do
		local success, errormessage = pcall(function()		
			myDataStore:SetAsync(player.UserId.."-DATA", plrStats)
		end)

		if success then
			print("Data Successfully Saved.")
		else
			warn("There was an Error Saving Data",errormessage)
		end

	end
end

game:BindToClose(function()
	Save()
end)

game.Players.PlayerRemoving:Connect(function()
	Save()
end)

does it work are you getting errors?

Also i wouldn’t suggest saving every players data when you call save else it will timeout the datastore depending on the number of players

You should only save data if the data has changed on a player you can do this by setting a global table in your script of PlayersToSave
then set the players name as key and when changed set true as value so PlayerToSave[Player.Name] = true
then check by doing

 if PlayerToSave[Player.Name] then
      -- call do save just this player here..
      PlayerToSave[Player.Name] = nil   -- remove it from table you know data is 
 end
updated for them    -- also need to do this on player removing

on your removed you need to save it per player if their data has chaned and needs save
also setup a while script with a wait in seconds to run through players and save the ones data that has changed say every 1 or 2 min so 60 or 120 seconds

game.Players.PlayerRemoving:Connect(function(Player)  -- use each player
	if PlayerToSave[Player.Name] then
		Save(Player)
		PlayerToSave[Player.Name] = nil  -- remove from changed table if there
	end
end)

local TimeBetweenAutoSave = 120   -- move this to top of script
while task.wait(TimeBetweenAutoSave) do  -- autosave loop
	for _, player in ipairs(game.Players) do  -- go through all players in game
		if PlayerToSave[Player.Name] then  -- if data has changed save it 
			Save(Player)
			PlayerToSave[Player.Name] = nil  -- remove from changed table if there
		end
	end
end

also you don’t need bindtoclose really if you have system like this

you should also setup a main table in this script to hold each players data and update it when the data changes that way even if the player leaves or server goes to crash you can read quick from that table and save it

How do I access the Player from the BindToClose function?

I have this:

game:BindToClose(function()
	Save()
	if PlayerToSave[Player.Name] then
		Save(Player)-- call do save just this player here..
		PlayerToSave[Player.Name] = nil   -- remove it from table you know data is updated for them    -- also need to do this on player removing
		end
end)

you really don’t need bind close if your using playerremoving

but you would use the exact code from the while loop as i have above

also had to edit the above it somehow removed the end from
if PlayerToSave[Player.Name] then

Something is still wrong, perhaps it was me trying to sync the values before the game saves them?

Note: The syncing happens in the Save function

local DataStoreService = game:GetService("DataStoreService")
local myDataStore = DataStoreService:GetDataStore("myDataStore")
local TimeBetweenAutoSave = 120
local PlayerToSave = _G.player



local plrStats = {
	inventory = {
	weapons = {},
	materials = {}
	},
	
	["Credits"] = 0,
	["Summon Tokens"] = 0,
	["Legendary Pity"] = 0,
	["Epic Pity"] = 0,
	["Element"] = 0, --1(Fire),2(Water),3(Ice),4(Nature),5(Lightning),6(Earth),7(Light),8(Dark),9(Light),10(Psychic)
	
	--Customizable Stats
	["Skin Tone"] = "Default",
	["Hair"] = "None",
	["Hat"] = "None",
	["Shirt"] = "Default",
	["Pants"] = "Default",
	["Shoes"] = "Default",

	--Base Stats as Integers
	["Level"] = 1,
	["Base HP"] = 100,
	["Base Atk"] = 10,
	["Base Def"] = 0,
	["Base MP"] = 10,
	["Base Crit %"] = .05,
	["Base Crit Dmg"] = .5,
	["Base Shield DMG Resist"] = 0.00,

	--Added Stats
	["HP Bonus"] = 0, 
	["Atk Bonus"] = 0, 
	["Def Bonus"] = 0, 
	["MP Bonus"] = 0,
	["Crit% Bonus"] = 0.00,
	["Crit Dmg Bonus"] = 0.00,
	["Shield DMG Resist Bonus"] = 0,

	--Equipped Items	
	["Current Sword"] = 1,
	["Current Shield"] = 1


}


game.Players.PlayerAdded:Connect(function(player)
	
	local Folder = Instance.new("Folder")
	Folder.Name = "Player Items"
	Folder.Parent = player
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	local level = Instance.new("IntValue")
	level.Name = "Level"
	level.Parent = leaderstats
	
	local data
	local success, errormessage = pcall(function()
		data = myDataStore:GetAsync(player.UserId.."-DATA")
	end)
	
	if success then
		print("Data Loaded Successfully")
		--credits.Value = data
	else
		warn("Error loading Save Data",errormessage)
	end
	
end)

function Save(Player)
	for i, Player in pairs(game.Players:GetPlayers()) do
		local success, errormessage = pcall(function()	
			plrStats.Level.Value = Player.leaderstats.Level.Value
			myDataStore:SetAsync(Player.UserId.."-DATA", plrStats)
		end)

		if success then
			print("Data Successfully Saved.")
		else
			warn("There was an Error Saving Data",errormessage)
		end

	end
end

game.Players.PlayerRemoving:Connect(function(Player)  -- use each player
	if PlayerToSave[Player.Name] then
		Save(Player)
		PlayerToSave[Player.Name] = nil  -- remove from changed table if there
	end
end)

local TimeBetweenAutoSave = 120   -- move this to top of script
while task.wait(TimeBetweenAutoSave) do  -- autosave loop
	for _, Player in ipairs(game.Players) do  -- go through all players in game
		if PlayerToSave[Player.Name] then  -- if data has changed save it 
			Save(Player)
			PlayerToSave[Player.Name] = nil  -- remove from changed table if there
		end
	end
end

what is this for?
it should be empty table like
local PlayerToSave = {}

again you shouldn’t use this part it loops through all players in the game you should only save 1 player at a time

also i think this maybe incorrectly done here if anything it should be
plrStats.Level = Player.leaderstats.Level.Value but then this is going to change your default plrStats table that was declared at the top of the script