Data saving is a nightmare

I’m currently using ProfileService and I’m still having issues with saving data.

I’m not sure if this is sessionlocking but I’m editing some values in the emotes list below but the keys are assigned to a certain value defined previously. I can’t edit or set the values in keys I’ve created.

local Emotes = {
	["Rock On"] = {false,false,"http://www.roblox.com/asset/?id=17694308547","NORMAL_EMOTE",true},
	["Clap"] = {false,false,"http://www.roblox.com/asset/?id=17696748004","NORMAL_EMOTE",false},
	["Cool"] = {false,false,"http://www.roblox.com/asset/?id=17743679003","ACCESSORY_EMOTE",false}
}

return Emotes

If I try to create new keys, I just have more data clutter that is stored in my game in which I’ll have to keep resetting keys just to get the game to work.

Above I created copies with exclamation marks for the keys that didn’t work. Only cool worked because it is the most recent emote I created.

Data script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Assets = ReplicatedStorage.Assets
local Remotes = ReplicatedStorage.Remotes

local EmotesList = require(script.EmotesList)
local Leaderboards = require(script.Leaderboards)

local function create_abilities()
	local tab_ = {}
	for _,ability in Assets.Shop:GetChildren() do
		if not ability:IsA("ModuleScript") then
			tab_[ability.Name] = {false,0}
		end
	end
	return tab_
end

function deepCopy(t)
	local copy = {}
	for k, v in pairs(t) do
		if type(v) == "table" then
			copy[k] = deepCopy(v)
		else
			copy[k] = v
		end
	end
	return copy
end

local function create_emotes()
	local tab_ = {}
	for emote,value in pairs(EmotesList) do	
		tab_[emote] = value
	end 
	return tab_
end

local PlayerDataHandler = {}

function PlayerDataHandler:Leaderstats(player)
	local folder = Instance.new("Folder")
	folder.Name = "leaderstats"
	local wins = Instance.new("IntValue")
	wins.Name = "Wins"
	wins.Value = self:Get(player,"Wins")
	local kills = Instance.new("IntValue")
	kills.Name = "Kills"
	kills.Value = self:Get(player,"Kills")
	local money = Instance.new("IntValue")
	money.Name = "Money"

	money.Changed:Connect(function(value)
		self:Set(player,"Money",value)
	end)

	money.Parent = player
	wins.Parent = folder
	kills.Parent = folder
	folder.Parent = player
end

local function battlepass()
	local tab_ = {}
	for i = 1,100 do
		tab_[i] = false
	end
	return tab_
end

local dataTemplate = {
	Money = 0,
	Wins = 0,
	Equipped = "",
	Kills = 0,
	Abilities = create_abilities(),
	
	Level = 1,
	Experience = 0,
	
	Emotes = create_emotes(),
	EmoteProgress = 2,
	
	Season = 1,
	Battlepass = battlepass(),
	
	Gamepasses = {
		["DoubleEmotes"] = false,
		["ExtraBattlepassReward"] = false
	}
}
local ProfileService = require(script.Parent.ProfileService)
local Players = game:GetService("Players")
local ProfileStore = ProfileService.GetProfileStore(
	"PlayerProfile",
	dataTemplate
)

local Profiles = {}

local function playerAdded(player)
	local profile = ProfileStore:LoadProfileAsync("Player_"..player.UserId)
	if profile then
		profile:AddUserId(player.UserId)
		profile:Reconcile()
		
		profile:ListenToRelease(function()
			Profiles[player] = nil
			player:Kick()
		end)
		
		if not player:IsDescendantOf(Players) then
			profile:Release()
		else
			Profiles[player] = profile
			PlayerDataHandler:Leaderstats(player)
		end
	else
		player:Kick()
	end
	task.spawn(function()
		while true do
			Leaderboards:Set(player,"Kills",PlayerDataHandler:Get(player,"Kills"))
			Leaderboards:Set(player,"Wins",PlayerDataHandler:Get(player,"Wins"))
			task.wait(50)
		end
	end)
	PlayerDataHandler:UpdateSeason(player)
end

function PlayerDataHandler:UpdateSeason(player)
	local Season = ServerStorage.Season.Value
	if PlayerDataHandler:Get(player,"Season") < Season then
		print("Reset: " .. player.Name)
		PlayerDataHandler:Set(player,"Season",Season)
		PlayerDataHandler:Set(player,"Level",0)
		PlayerDataHandler:Set(player,"Experience",0)
		PlayerDataHandler:Set(player,"EmoteProgress",2)
		PlayerDataHandler:Set(player,"Battlepass",battlepass())
	end
end

function PlayerDataHandler:Init()
	for _,player in Players:GetPlayers() do
		task.spawn(playerAdded,player)
	end
	Players.PlayerAdded:Connect(playerAdded)

	Players.PlayerRemoving:Connect(function(player)
		if Profiles[player] then
			Profiles[player]:Release()
		end
	end)
	task.spawn(function()
		while true do
			Leaderboards:Refresh("Kills")
			Leaderboards:Refresh("Wins")
			task.wait(50)
		end
	end)
end

local function getProfile(player)
	assert(Profiles[player],string.format("Profile does not exist for %s",player.UserId))
	return Profiles[player]
end

function PlayerDataHandler:Get(player,key)
	local profile = getProfile(player)
	assert(profile.Data[key],string.format("Data does not exist for key: %s",key))
	return profile.Data[key]
end

function PlayerDataHandler:Set(player,key,value)
	local profile = getProfile(player)
	assert(profile.Data[key],string.format("Data does not exist for key: %s",key))
	assert(type(profile.Data[key]) == type(value))
	profile.Data[key] = value
	if key == "Money" or key == "Level" or key == "Experience" then
		Remotes.ToClient:FireClient(player,"UpdateCounters",PlayerDataHandler:Get(player,"Experience"),PlayerDataHandler:Get(player,"Level"),PlayerDataHandler:Get(player,"Money"))
	end
end

function PlayerDataHandler:Update(player,key,callback)
	local profile = getProfile(player)
	local oldData = self:Get(player,key)
	local newData = callback(oldData)
	self:Set(player,key,newData)
end

return PlayerDataHandler

I used to be just like you, when I was newer to scripting. Much newer, I used profileservice because I couldn’t grasp my head around data and what not. After going back through and using the documentation’s tutorial on datastores it became super easy. I now just use regular datastoreservice rather than needing to use a module like profileservice. You’ll grasp your head around it one day too. Here’s a small tutorial on datastoreservice I made if you’re struggling:

I would learn how to create an effective datastore but it would take too much time to replicate the Taj Mahal for Session locking.

I’m just trying to understand how to save data with profile service so I don’t have to take months to learn sessionlocking.

This is something that confused me when using profileservice too, I thought you had to save data. But it automatically saves data for you within a period of time.

Edit: If you wanna save it earlier that in the time period it saves for you, you can do that. But it’s a bit more complicated as you’d have to go into the profileservice module and edit to your liking. But you can just identify where setasync is being called.