Save StringValues in a DataStore

I have a DataStore, that has StringValues.

The StringValue is an achievement unlocked by the player.

  • The StringValue’s name is the name of the achievement.
  • The StringValue’s value is the description of the achievement.

But I don’t understand how I can save both the name and the value in a single DataStore. Can someone please help me implement it?

Here’s the ModuleScript I guess. It’s a bit long and MUST be required by the server:

local functions = {}

local dss = game:GetService("DataStoreService")
local da = dss:GetDataStore("PlayerAchievemtUnlockedStorage")

local remote = script.Parent:WaitForChild("AchievementsRemoteEvent")

function functions.LoadPlayerAchievements(plr:Player)
	if not game:GetService("RunService"):IsServer() then return end
	if plr.Parent == game.Players and not plr:FindFirstChild("ACHIEVEMENTS FOLDER") then
		local plrData
		local DataDescription
		local success, err
		
		while not success do
			success, err = pcall(function()
				plrData = da:GetAsync(plr.UserId) or {}
			end)
			
			if not success then
				warn(err)
			end	
			task.wait(0.5)
		end

		if success then
			local newFolder = Instance.new("Folder")
			newFolder.Name = "ACHIEVEMENTS FOLDER"
			newFolder.Parent = plr
			for _, achName in pairs(plrData) do -- this is where the name is gonna insert, but I also need the value
				local newAchievementValue = Instance.new("StringValue")
				newAchievementValue.Name = achName
				newAchievementValue.Parent = newFolder
			end
		end
	end
end

function functions.SavePlayerAchievements(plr:Player)
	if not game:GetService("RunService"):IsServer() then return end	
	if plr.Parent == game.Players and plr:FindFirstChild("ACHIEVEMENTS FOLDER") then	
		local plrAchievements = {}
		for _, achievementValue in pairs(plr["ACHIEVEMENTS FOLDER"]:GetChildren()) do
			table.insert(plrAchievements, achievementValue.Name)
			
			-- this is the part where it saves the name, but I dunno how to save the value with it too...
		end
		
		local success, err
		
		while not success do
			success, err = pcall(function()		
				return da:SetAsync(plr.UserId, plrAchievements)
			end)
			task.wait(0.1)
		end
	end
end

function functions.AwardAchievement(plr:Player, achName:string, Desc:string)
	if not game:GetService("RunService"):IsServer() then return end
	if plr.Parent == game.Players and plr:FindFirstChild("ACHIEVEMENTS FOLDER") and not plr["ACHIEVEMENTS FOLDER"]:FindFirstChild(achName) then	
		remote:FireClient(plr, "text", achName, Desc)
		local newAchievementValue = Instance.new("StringValue")
		newAchievementValue.Name = achName
		newAchievementValue.Parent = plr["ACHIEVEMENTS FOLDER"]
		newAchievementValue.Value = Desc
	end
end

return functions

Instead of doing

table.insert(plrAchievements, achievementValue.Name)

you can try doing this instead when saving your data

plrAchievements[achievementValue.Name] = achievementValue.Value

with this, you will be able to store both the name and the value of the achievement. When loading data, all you need is a for loop to get both the values

for achievementName,achievementValue in pairs(plrAchievements) do
    --code
end
2 Likes

Can you please explain what you did there? Did you merge both the name and the description?

you can add things to a table in 2 ways. The first way is by using table.insert() and the second is by setting the key manually. when you use table.insert, the key gets set automatically. If I were to write down a table made with table.insert(), it would look something like this:

local myTable = {
[1] = "Item1",
[2] = 7,
[3] = "ItemDescription"
}

You notice how each item in the table is indexed by a number? Thats called a key. When you want to build something more complex, you can set the key directly so your table turns out like so:

local myTable = {
["Item1"] = "Description of item1",
["Item2"] = "Description of item2"
}

using item names as the key is probably not the best example, because there cant be 2 keys with the same name in one array. So for an inventory you would probably use table.insert(), however in your case, all achievement names should be different, thus not causing any problems. If you need any further clarification just let me know!

After implementing what you suggested, something is going wrong. I don’t know if it’s either loading or saving, but the unlocked achievements are gone on every join.

Sorry for the late response! It might be because of the loading. Try printing the saved data every time the data gets saved, and when the data gets loaded. If you have the datastore editor plugin, check if the data is being saved through that.

I used that, and yes, the data is being saved and loaded normally. I checked it with “print” and everything is there, but for some reason the achievements are still not applied in the “ACHIEVEMENTS FOLDER”. Can you please help troubleshoot this? Here’s the updated module.

local functions = {}

local dss = game:GetService("DataStoreService")
local da = dss:GetDataStore("PlayerAchievementsUnlockedStorageData")

local remote = script.Parent:WaitForChild("AchievementsRemoteEvent")

function functions.LoadPlayerAchievements(plr:Player)
	if not game:GetService("RunService"):IsServer() then return end
	if plr.Parent == game.Players and not plr:FindFirstChild("ACHIEVEMENTS FOLDER") then
		local plrAchievement
		local success, err
		
		while not success do
			success, err = pcall(function()
				plrAchievement = da:GetAsync(plr.UserId) or {}
			end)
			
			if not success then
				warn(err)
			end	
			task.wait(0.5)
		end

		if success then
			print(plrAchievement)
			local newFolder = Instance.new("Folder")
			newFolder.Name = "ACHIEVEMENTS FOLDER"
			newFolder.Parent = plr
			for achievementName, achievementValue in pairs(plrAchievement) do
				local newAchievementValue = Instance.new("StringValue")
				newAchievementValue.Name = achievementName
				newAchievementValue.Value = achievementValue
			end
		end
	end
end

function functions.SavePlayerAchievements(plr:Player)
	if not game:GetService("RunService"):IsServer() then return end	
	if plr.Parent == game.Players and plr:FindFirstChild("ACHIEVEMENTS FOLDER") then	
		local plrAchievements = {}
		for achievementName, achievementValue in pairs(plr["ACHIEVEMENTS FOLDER"]:GetChildren()) do
			plrAchievements[achievementValue.Name] = achievementValue.Value
		end
		
		local success, err
		
		while not success do
			success, err = pcall(function()
				print(plrAchievements)
				return da:SetAsync(plr.UserId, plrAchievements)
			end)
			task.wait(0.1)
		end
	end
end

function functions.AwardAchievement(plr:Player, achName:string, Desc:string)
	if not game:GetService("RunService"):IsServer() then return end
	if plr.Parent == game.Players and plr:FindFirstChild("ACHIEVEMENTS FOLDER") and not plr["ACHIEVEMENTS FOLDER"]:FindFirstChild(achName) then	
		remote:FireClient(plr, "text", achName, Desc)
		local newAchievementValue = Instance.new("StringValue")
		newAchievementValue.Name = achName
		newAchievementValue.Parent = plr["ACHIEVEMENTS FOLDER"]
		newAchievementValue.Value = Desc
	end
end

return functions

Oh, silly me, I forgot to parent the StringValues to the folder. Thank you so much for your help!

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