How to save player attributes

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    I want to save player attributes with SetAsync (boolean mostly) that specify if a player owns a specific vehicle. So when the player rejoins the game, or loses their vehicle they can go to a spawn point and if they own the vehicle it will spawn back.
  2. What is the issue? Include screenshots / videos if possible!
    The attribute changes in game but the script I’m using to save them doesn’t work correctly, so when the player rejoins their attribute resets to default (false). It seems to load the attribute on PlayerAdded but the value does not change on PlayerRemoving when the save is happening, nor anytime else so far as I can tell. This is the script for the Attributes saving, I put this in ServerScriptService:

local players = game:GetService(“Players”)
local datastoreService = game:GetService(“DataStoreService”)
local datastore = datastoreService:GetDataStore(“PlayerAttributes”)

local AttributeList = {
SportsCar = {“boolean”,“false”},
SUV = {“boolean”,“false”}
}

local FromStringFunctions = {
number = tonumber,
boolean = function(var)
if var == “true” then
return true
else
return false
end
end
}

local ToStringFunctions = {
number = tostring,
boolean = function(var)
if var then
return “true”
else
return “false”
end
end,
}

players.PlayerAdded:Connect(function(Player :Player)
local Data
xpcall(function()
Data = datastore:GetAsync(Player.UserId)
end, function(err)
Player:Kick(“Error Loading Credentials”…err)
end)
if not Data then
Data ={}
end
for index, value in pairs(AttributeList) do
if not Data[index] then
Data[index] = FromStringFunctionsvalue[1]
end
end
for index,value in pairs(Data) do
Player:SetAttribute(index,value)
print(“Attributes Set”)
local atts = Player:GetAttributes()
local att = Player:GetAttribute(“SportsCar”)
print(att)
end
end)

players.PlayerRemoving:Connect(function(Player :Player)
local Data = {}
for index,value in pairs(AttributeList) do
Data[index] = Player:GetAttribute(index)
end

local Success = true
local N = 0
repeat
if not Success then
wait(0.3)
end
N += 1
local err
Success,err = pcall(function()
datastore:SetAsync(Player.UserId,Data)
end)
if err then
print(err)
end
until Success or N >= 5
end)

game:BindToClose(function()
wait(3)
end)

and this is the script inside the part which changes the attribute:

function sportscarPickup (partTouching)
if partTouching.Parent:FindFirstChild(“Humanoid”) then
local player = game.Players:GetPlayerFromCharacter (partTouching.Parent)
local human = partTouching.Parent:FindFirstChild(“Humanoid”)
if player.leaderstats.Coins.Value >= 10000 then
player.leaderstats.Coins.Value = player.leaderstats.Coins.Value - 10000
local hasCar = human:SetAttribute(“SportsCar”, true)
print ("Spent Coins: " … player.leaderstats.Coins.Value)
local newSportscar = game.ReplicatedStorage.SportsCar:Clone()
newSportscar.Parent = game.Workspace
script.Parent:Destroy()
end

end
end
script.Parent.Touched:Connect (sportscarPickup)

Both are Server scripts.

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I have looked for solutions on here but haven’t found exactly what I’m looking for. I have tried adding several prints to find out what is happening. It’s strange because the attribute changes to true in game and when tested in studio and the car spawns in as it should but in the saving script the attribute does not seem to change from the default value all the way through, every print statement returns the attribute as false ;

players.PlayerAdded:Connect(function(Player :Player)
	
	local Data 
	xpcall(function()
		Data = datastore:GetAsync(Player.UserId)
	end, function(err)
		Player:Kick("Error Loading Credentials"..err)
	end)
	if not Data then
		Data ={}
	end
	for index, value in pairs(AttributeList) do
		if not Data[index] then
			Data[index] = FromStringFunctions[value[1]](value[2])
		end
	end
	for index,value in pairs(Data) do
		Player:SetAttribute(index,value)
		print("Attributes Set")
		local atts =  Player:GetAttributes()
		local att = Player:GetAttribute("SportsCar")
		print(att)
		
		
		
	end
end)



players.PlayerRemoving:Connect(function(Player :Player)
	
	
	local Data = {}
	for index,value in pairs(AttributeList) do
		Data[index] = Player:GetAttribute(index)
		
	end

	
	local Success = true
	local N = 0
	repeat 
		if not Success then
			wait(0.3) 
		end
		N += 1
		local err
			Success,err = pcall(function()
			datastore:SetAsync(Player.UserId,Data)
			print("Attributes Set")
			local atts =  Player:GetAttributes()
		local att = Player:GetAttribute("SportsCar")
		print(att)
		end)
		if err then
			print(err)
		end
	until Success or N >= 5 
end)

I also tried changing the script to take the attributes from the humanoid instead of player which is what it is being set to when the player purchases the car but this didn’t work either, nor did trying to get the AttributeChangedSignal and set the Attribute manually inside the saving script;

local players = game:GetService("Players")
local datastoreService = game:GetService("DataStoreService")
local datastore = datastoreService:GetDataStore("PlayerAttributes")

local AttributeList = {
SportsCar = {"boolean","false"},	
SUV = {"boolean","false"}	
}


local FromStringFunctions = {
	number = tonumber,
	boolean = function(var)
		if var == "true" then
			return true
		else
			return false
		end
	end
}

local ToStringFunctions = {
	number = tostring,
	boolean = function(var)
		if var then
			return "true"
		else
			return "false"
		end
	end,
}

players.PlayerAdded:Connect(function(Player :Player)
	wait(5)
	local char = Player.Character
	local human = char:FindFirstChild("Humanoid")
	local Data 
	xpcall(function()
		Data = datastore:GetAsync(Player.UserId)
	end, function(err)
		Player:Kick("Error Loading Credentials"..err)
	end)
	if not Data then
		Data ={}
	end
	for index, value in pairs(AttributeList) do
		if not Data[index] then
			Data[index] = FromStringFunctions[value[1]](value[2])
		end
	end
	for index,value in pairs(Data) do
		Player:SetAttribute(index,value)
		print("Attributes Set")
		local atts =  human:GetAttributes()
		local att = human:GetAttribute("SportsCar")
		print(att)
		human:GetAttributeChangedSignal("SportsCar"):Connect(function()
			wait(2)
			human:SetAttribute("SportsCar", true)
			wait(4)
			print("Property changed"..tostring(att))
		end)
		
		
	end
end)



players.PlayerRemoving:Connect(function(Player :Player)
	
	local char = Player.Character
	local human = char:FindFirstChild("Humanoid")
	local Data = {}
	for index,value in pairs(AttributeList) do
		Data[index] = human:GetAttribute(index)
		
	end

	
	local Success = true
	local N = 0
	repeat 
		if not Success then
			wait(0.3) 
		end
		N += 1
		local err
			Success,err = pcall(function()
			datastore:SetAsync(Player.UserId,Data)
			print("Attributes Set")
			local atts =  human:GetAttributes()
			local att = human:GetAttribute("SportsCar")
			print(att)
		end)
		if err then
			print(err)
		end
	until Success or N >= 5 
end)

game:BindToClose(function()
	wait(3)
end)

I’m not sure what I’m doing wrong with it but any help will be appreciated, Thanks in advance.

Simply use SetAsync() to save the value of the attribute, then when the player joins use GetAsync() to set the attribute’s value.

1 Like

I am using SetAsync in the PlayerRemoving and GetAsync in the PlayerAdded. I don’t think theres an issue with the SetAsync because the print statement returns the attribute as false even after the AttributeChangedSignal and prior to PlayerRemoving.

And also make sure that when your normal server script changes the attribute value in the game, it actually changes it, it happened to me, with an IntValue that I was changing the value inside the game and it didn’t work. Sometimes scripts don’t change values for some reason

It seems like something like that is happening because when I’m checking the attribute with print() in the Save script it’s returning false every time. But I don’t get why when tested in studio I can see the attribute is set to true on the Player after the car is purchased, also the script inside the part which spawns the car(if owned) is responding to the attribute change. So it doesn’t spawn the car before the player has purchased it but afterwards it does, even though at the same time the save script is returning the attribute as false… It’s as if the attribute that’s set at the point of purchase and the attribute that the save script is looking for are two seperate attributes, even though they’re named the same “SportsCar”. This is the script inside the part that spawns the vehicle;

local players = game:GetService(“Players”)
local debounce = false

script.Parent.Touched:Connect(function(hit)

if hit then
local human = hit.Parent:FindFirstChild(“Humanoid”)
local hascar = human:GetAttribute(“SportsCar”)
if hascar == true and not debounce then
print(“player has sports car”)
debounce = true
local newCar = game.ReplicatedStorage.SportsCar:Clone()
newCar:PivotTo(script.Parent.CFrame)
newCar.Parent = game.Workspace
print(“car transported”)
wait(35)
debounce = false
end
end

end)

Ok I fixed it. With some more playing around I realised that if I changed the script in the purchase part to set the attribute to the player not the humanoid then the attribute change is picked up and saving them with SetAsync works. Thanks to you both for the input :+1:t4:

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.