Avatar Data Save Doesn't Save

The 2 values I want to save are player.ForSaving.Avatar.Shirt and then all that but with Pants. In the output it says unable to cast value to function. Btw, I have it so when a player changes their avatar, it also changes the values in the player. Thanks for any help! :stuck_out_tongue: Im not the best at scripting as you can probably tell

local DSS = game:GetService("DataStoreService")
local myDS = DSS:GetDataStore("myDataStore")
local PlayersToSave = {}
local TimeBetweenAutoSave = 60

function SavePlayersData(player)
	
	local key = "Player_"..player.UserId
	
	local Shirt = player.ForSaving.Avatar.Shirt.Value
	local Pants = player.ForSaving.Avatar.Pants.Value

	local success, errormessage = pcall(function()
		myDS:UpdateAsync(key, Shirt)
		myDS:UpdateAsync(key, Pants)
	end)

	if success then
		player.PlayerGui.MainGui.Saving.Visible = true -- This is just a little gui that tells the player if the save worked
		player.PlayerGui.MainGui.Saving.TextLabel.Text = ("Saved")
		print("Data save was successfull!")
		wait(3)
		player.PlayerGui.MainGui.Saving.Visible = false
	else
		player.PlayerGui.MainGui.Saving.Visible = true
		player.PlayerGui.MainGui.Saving.TextLabel.Text = ("Save failed")
		print("Data save failed...")
		warn(errormessage)
		wait(3)
		player.PlayerGui.MainGui.Saving.Visible = false
	end
end


game.Players.PlayerAdded:Connect(function(player)
	
	-- Make data save folder
	local ForSaving = Instance.new("Folder")
	ForSaving.Name = "ForSaving"
	ForSaving.Parent = player
	
	-- Make avatar save folder
	local AvatarFolder = Instance.new("Folder")
	AvatarFolder.Name = "Avatar"
	AvatarFolder.Parent = player.ForSaving
	
	-- Make shirt
	local Shirt = Instance.new("IntValue")
	Shirt.Name = "Shirt"
	Shirt.Parent = AvatarFolder
	
	-- Make pants
	local Pants = Instance.new("IntValue")
	Pants.Name = "Pants"
	Pants.Parent = AvatarFolder
	
	-- Player
	local key = "Player_"..player.UserId

	-- Load data
	local data
	local success, errormessage = pcall(function()
		data = myDS:GetAsync(key)
	end)

	if success then
		
		Shirt.Value = data or 0
		Pants.Value = data or 0
		print("Loaded data!")
		
	end


	Shirt.Changed:Connect(function()
		table.insert(PlayersToSave,player)
	end)
	
	Pants.Changed:Connect(function()
		table.insert(PlayersToSave,player)
	end)

end)

-- Save when leaving
game.Players.PlayerRemoving:Connect(function(player)
	if table.find(PlayersToSave,player) then
		SavePlayersData(player)
		table.remove(PlayersToSave,table.find(PlayersToSave,player))
	end
end)

-- Auto save
while true do
	wait(TimeBetweenAutoSave)
	spawn(function()
		for _, player in ipairs(PlayersToSave) do
			SavePlayersData(player)
			table.remove(PlayersToSave,table.find(PlayersToSave,player))
			print("Data auto saved!")
		end
	end)
end

UpdateAsync expects a function as its 2nd argument where you are expected to return the new value that should be in the key or nil to cancel the update, you gave it a value, did you mean to use SetAsync in this case?

1 Like

Ok, It said saved but in the output it also said “DataStore request was added to queue. If request queue fills, further requests will be dropped. Try sending fewer requests.Key = Player_117457868” Should I be worried?

Another thing, this script is made off of an old one that was used to save a coins value which was coins and it seems to be loading the coins value instead of the shirt/pants id?

-- Load data
	local data
	local success, errormessage = pcall(function()
		data = myDS:GetAsync(key)
	end)

	if success then
		
		Shirt.Value = data or 0
		Pants.Value = data or 0
		print("Loaded data!")
		
	end

Well looking through how you’re doing it, it could be that you autosave every minute or could be that you use SetAsync twice for the same key, meaning in all cases, you’ll only be saving what Pants contains, you should be Shirt and Pants into a table and save that into the Datastore

--To save
local tbl = {Shirt, Pants}
myDS:SetAsync(key, tbl)

--To receive
Shirt.Value = tbl[1]
Pants.Value = tbl[2]

Also what do you mean by loading the coins value?

1 Like

Oh, I messed that up, basically that script used to be a script for saving the players’ coins value but I removed that whole system and now I’m trying to get it to save avatar item ids and when it loads data its loading the coins I used to have which is 7 coins lol

Where do I put the To Save and To Receive?

To Save is where you want the datastore to save your data, To Receive is where you get your data from the datastore, just replace the saving/getting code with those

1 Like

Well your issue is that you alreayd called .Value and call it again later, but the tbl when the player joins is not how you’re meant to do that, tbl is the like the data variable, it contains the result of GetAsync

1 Like

The current script. Works almost all the way but data loads 0

local DSS = game:GetService("DataStoreService")
local myDS = DSS:GetDataStore("myDataStore")
local PlayersToSave = {}
local TimeBetweenAutoSave = 10 -- will change back, just for testing

function SavePlayersData(player)
	
	local key = "Player_"..player.UserId
	
	local Shirt = player.ForSaving.Avatar.Shirt.Value
	local Pants = player.ForSaving.Avatar.Pants.Value
	
	local success, errormessage = pcall(function()
		local tbl = {Shirt, Pants}
		myDS:SetAsync(key, tbl)
	end)

	if success then
		player.PlayerGui.MainGui.Saving.Visible = true
		player.PlayerGui.MainGui.Saving.TextLabel.Text = ("Saved")
		print("Data save was successfull!")
		wait(3)
		player.PlayerGui.MainGui.Saving.Visible = false
	else
		player.PlayerGui.MainGui.Saving.Visible = true
		player.PlayerGui.MainGui.Saving.TextLabel.Text = ("Save failed")
		print("Data save failed...")
		warn(errormessage)
		wait(3)
		player.PlayerGui.MainGui.Saving.Visible = false
		
	end
end


game.Players.PlayerAdded:Connect(function(player)
	
	local key = "Player_"..player.UserId
	
	-- Make data save folder
	local ForSaving = Instance.new("Folder")
	ForSaving.Name = "ForSaving"
	ForSaving.Parent = player
	
	-- Make avatar save folder
	local AvatarFolder = Instance.new("Folder")
	AvatarFolder.Name = "Avatar"
	AvatarFolder.Parent = player.ForSaving
	
	-- Make shirt
	local Shirt = Instance.new("IntValue")
	Shirt.Name = "Shirt"
	Shirt.Parent = AvatarFolder
	Shirt.Value = myDS:GetAsync(key, Shirt)
	
	-- Make pants
	local Pants = Instance.new("IntValue")
	Pants.Name = "Pants"
	Pants.Parent = AvatarFolder
	Pants.Value = myDS:GetAsync(key, Pants)
	
	-- Add values to table

	Shirt.Changed:Connect(function()
		table.insert(PlayersToSave,player)
	end)
	
	Pants.Changed:Connect(function()
		table.insert(PlayersToSave,player)
	end)

end)

-- Save when leaving
game.Players.PlayerRemoving:Connect(function(player)
	if table.find(PlayersToSave,player) then
		SavePlayersData(player)
		table.remove(PlayersToSave,table.find(PlayersToSave,player))
	end
end)

-- Auto save
while true do
	wait(TimeBetweenAutoSave)
	spawn(function()
		for _, player in ipairs(PlayersToSave) do
			SavePlayersData(player)
			table.remove(PlayersToSave,table.find(PlayersToSave,player))
			print("Data auto saved!")
		end
	end)
end

I decided to make individual scripts for each value lol. Maybe not the best idea, but for now it works. Here’s the script that saves just one of the values for anyone looking at this in the future. Thanks @EmbatTheHybrid for all the help! :upside_down_face:

local DSS = game:GetService("DataStoreService")
local myDS = DSS:GetDataStore("myDataStore")
local PlayersToSave = {}   -- setup a table to hold players that the data has changed on 
local TimeBetweenAutoSave = 60 -- the time between each autosave check if they are in table above to save

function SavePlayersData(player)  -- The save function itself call by remove and by autosave
	
	local key = "Player_"..player.UserId

	local data = player:FindFirstChild("Avatar").Shirt.Value
	
	local success, errormessage = pcall(function()
		myDS:UpdateAsync(key, data)
	end)
	
	if success then
		print("Success")
	else
		print("Error")
		warn(errormessage)
	end
end

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

	-- Player
	local key = "Player_"..player.UserId
	
	-- Load data
	local data -- Makes it work in the WHOLE script
	local success, errormessage = pcall(function()
		data = myDS:GetAsync(key)          -- !!!! if data has never been set this will return successful and data will be nil
	end)
	
	local Value = player.Avatar.Shirt
	
	if success then
		Value.Value = data or 0     -- !!!! this will set a default to the value incase they don't have any data which would be nil
		-- Set value = to the data
		print("Loaded!")
	end
	
	Value.Changed:Connect(function()   -- this catches the change of the value either add or subtracted any change to it will then add them to the autosave table for next loop to save the data
		table.insert(PlayersToSave,player)  -- add the player to this table because their data has changed
	end)
end)

-- Save when leaving
game.Players.PlayerRemoving:Connect(function(player)
	if table.find(PlayersToSave,player) then  -- if the players data changed they will be in this table so save their data on remove else don't   -- you can remove this and just do a save here if you want but may overload datastore
		SavePlayersData(player)  -- save when they leave if it can sometimes this doesn't save in studio
		table.remove(PlayersToSave,table.find(PlayersToSave,player))    -- after save then remove them from the table
	end
end)

-- Auto save
while true do   -- this autosave loop only saves player data if it has changed
	wait(TimeBetweenAutoSave)
	spawn(function()  -- Spawn incase of error
		for _, player in ipairs(PlayersToSave) do  -- go through all the players in the PlayersToSave because their data changed and save them
			SavePlayersData(player)  -- save when they leave if it can sometimes this doesn't save in studio
			table.remove(PlayersToSave,table.find(PlayersToSave,player))
			-- After save then remove player from table
			print("Auto saved!")
		end
	end)
end