I created a post on how to prevent overwritten data to prevent data regression. I learned that the way to do this was session locking, in which I found ProfileService was my best option(even though I had previously used ProfileService and had the same error I’m having now).
Leveling Module
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Data = require(script.Parent.PlayerDataHandler)
local Remotes = ReplicatedStorage.Remotes
local Bindables = ReplicatedStorage.Bindables
local Leveling = {}
Bindables.OnServer.Event:Connect(function(signal,player,amount)
if signal == "Gain" then
player:SetAttribute("Round_XP",player:GetAttribute("Round_XP") + amount)
Data:Update(player,"Experience",function(past)
return past + amount
end)
local xp = Data:Get(player,"Experience")
local level = Data:Get(player,"Level")
local progress = xp / (300 * level)
if progress >= 1 then
Data:Update(player,"Experience",function(past)
return past - (300 * level)
end)
Data:Update(player,"Level",function(past)
return past + 1
end)
progress = Data:Get(player,"Experience") / (300 * Data:Get(player,"Level"))
end
elseif signal == "UI" then
for _,player in Players:GetPlayers() do
if player:GetAttribute("Enabled") == false then return end
local level = Data:Get(player,"Level")
local experience = Data:Get(player,"Experience")
print(level)
Remotes.ToClient:FireClient(player,"LoadXP",experience,level,player:GetAttribute("Round_XP"))
player:SetAttribute("Round_XP",nil)
end
end
end)
return Leveling
ProfileDataHandler
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Assets = ReplicatedStorage.Assets
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
local function create_emotes()
local tab_ = {}
for emote,value in 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 dataTemplate = {
Money = 0,
Wins = 0,
Equipped = "",
Kills = 0,
Abilities = create_abilities(),
Level = 1,
Experience = 0,
Emotes = create_emotes(),
EmoteProgress = 2,
Gamepasses = {
["DoubleEmotes"] = 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)
print(PlayerDataHandler:Get(player,"Level"))
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
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
Both of these scripts I think are valid and I got the ProfileDataHandler from this video.
I don’t understand why I am getting nan despite the intialization for the dataTemplate key “Level” being 1(cannot be nan, only 0/0, and level only adds += 1). I also don’t understand how this would be happening if profileservice is handling the sessionlocking for me.
I assume though that this is happening because of a previous error I had when creating levels, and I did create a ProfileServiceHandler just like this one for my game in the past.
Any ideas?