Issue with leaderstats being in the wrong order

This is both of the scripts that creates the leaderstats:


local ReplicatedStorage = game:GetService("ReplicatedStorage")
local BadgeService = game:GetService("BadgeService")
local Players = game:GetService("Players")

--────────────────────────── << MODULES >> ────────────────────────────

-- Empty

--────────────────────────── << VARIABLES >> ──────────────────────────

local module = {}

--────────────────────────── << FUNCTIONS >> ──────────────────────────

-- Check for Badge whenever Kills change
local function setupBadgeListener(player, killsValue, smashesValue)
	killsValue.Changed:Connect(function(newVal)
		if newVal >= 250 then
			local success, hasBadge = pcall(function()
				return BadgeService:UserHasBadgeAsync(player.UserId, 2312892374675752)
			end)

			if success and not hasBadge then
				pcall(function()
					BadgeService:AwardBadgeAsync(player.UserId, 2312892374675752)

					local badgeInfo = BadgeService:GetBadgeInfoAsync(2312892374675752)
					local badgeImageId = "rbxassetid://" .. badgeInfo.IconImageId
					local BadgeName = badgeInfo.Name

					local NotificationRemote = ReplicatedStorage:WaitForChild("RemoteEvents"):WaitForChild("Notification")

					NotificationRemote:FireClient(player, "Badge", "You got the " .. BadgeName .. " badge!", 15, badgeImageId)
				end)
			end
		end

		if newVal >= 500 then
			local success, hasBadge = pcall(function()
				return BadgeService:UserHasBadgeAsync(player.UserId, 4442479432930128)
			end)

			if success and not hasBadge then
				pcall(function()
					BadgeService:AwardBadgeAsync(player.UserId, 4442479432930128)

					local badgeInfo = BadgeService:GetBadgeInfoAsync(4442479432930128)
					local badgeImageId = "rbxassetid://" .. badgeInfo.IconImageId
					local BadgeName = badgeInfo.Name

					local NotificationRemote = ReplicatedStorage:WaitForChild("RemoteEvents"):WaitForChild("Notification")

					NotificationRemote:FireClient(player, "Badge", "You got the " .. BadgeName .. " badge!", 15, badgeImageId)
				end)
			end
		end

		if newVal >= 1000 then
			local success, hasBadge = pcall(function()
				return BadgeService:UserHasBadgeAsync(player.UserId, 425818117264007)
			end)

			if not success or hasBadge then return end
			pcall(function()
				BadgeService:AwardBadgeAsync(player.UserId, 425818117264007)

				local badgeInfo = BadgeService:GetBadgeInfoAsync(425818117264007)
				local badgeImageId = "rbxassetid://" .. badgeInfo.IconImageId
				local BadgeName = badgeInfo.Name

				local NotificationRemote = ReplicatedStorage:WaitForChild("RemoteEvents"):WaitForChild("Notification")

				NotificationRemote:FireClient(player, "Badge", "You got the " .. BadgeName .. " badge!", 15, badgeImageId)
			end)
		end
	end)

	smashesValue.Changed:Connect(function(newVal)
		if newVal >= 1000 then
			local success, hasBadge = pcall(function()
				return BadgeService:UserHasBadgeAsync(player.UserId, 3435276838241438)
			end)

			if success and not hasBadge then
				pcall(function()
					BadgeService:AwardBadgeAsync(player.UserId, 3435276838241438)

					local badgeInfo = BadgeService:GetBadgeInfoAsync(3435276838241438)
					local badgeImageId = "rbxassetid://" .. badgeInfo.IconImageId
					local BadgeName = badgeInfo.Name

					local NotificationRemote = ReplicatedStorage:WaitForChild("RemoteEvents"):WaitForChild("Notification")

					NotificationRemote:FireClient(player, "Badge", "You got the " .. BadgeName .. " badge!", 15, badgeImageId)
				end)
			end
		end

		if newVal >= 10000 then
			local success, hasBadge = pcall(function()
				return BadgeService:UserHasBadgeAsync(player.UserId, 3277737759460434)
			end)

			if success and not hasBadge then
				pcall(function()
					BadgeService:AwardBadgeAsync(player.UserId, 3277737759460434)

					local badgeInfo = BadgeService:GetBadgeInfoAsync(3277737759460434)
					local badgeImageId = "rbxassetid://" .. badgeInfo.IconImageId
					local BadgeName = badgeInfo.Name

					local NotificationRemote = ReplicatedStorage:WaitForChild("RemoteEvents"):WaitForChild("Notification")

					NotificationRemote:FireClient(player, "Badge", "You got the " .. BadgeName .. " badge!", 15, badgeImageId)
				end)
			end
		end

		if newVal >= 100000 then
			local success, hasBadge = pcall(function()
				return BadgeService:UserHasBadgeAsync(player.UserId, 3046178914164323)
			end)

			if success and not hasBadge then
				pcall(function()
					BadgeService:AwardBadgeAsync(player.UserId, 3046178914164323)

					local badgeInfo = BadgeService:GetBadgeInfoAsync(3046178914164323)
					local badgeImageId = "rbxassetid://" .. badgeInfo.IconImageId
					local BadgeName = badgeInfo.Name

					local NotificationRemote = ReplicatedStorage:WaitForChild("RemoteEvents"):WaitForChild("Notification")

					NotificationRemote:FireClient(player, "Badge", "You got the " .. BadgeName .. " badge!", 15, badgeImageId)
				end)
			end
		end
	end)
end

local function createLeaderstats(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"

	local Smashes = Instance.new("IntValue")
	Smashes.Name = "Smashes"
	Smashes.Value = 0
	Smashes.Parent = leaderstats

	local Kills = Instance.new("IntValue")
	Kills.Name = "Kills"
	Kills.Value = 0
	Kills.Parent = leaderstats

	local Hammer = Instance.new("StringValue")
	Hammer.Name = "Hammer"
	Hammer.Value = "Default"
	Hammer.Parent = leaderstats
	
	leaderstats.Parent = player

	setupBadgeListener(player, Kills, Smashes)

	return leaderstats
end

function module:SetStats(Player : Player, NewData : {})
	local leaderstats = Player:FindFirstChild("leaderstats")
	if not leaderstats then leaderstats = createLeaderstats(Player) end

	leaderstats["Smashes"].Value = NewData["Smashes"]
	leaderstats.Kills.Value = NewData["Kills"]
	leaderstats.Hammer.Value = NewData["Hammer"]

	return leaderstats["Smashes"].Value, leaderstats.Hammer.Value, leaderstats.Kills.Value
end

return module


And this is the serverscript that calls that module:

--────────────────────────── << SERVICES >> ───────────────────────────

local ServerScriptService = game:GetService("ServerScriptService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

--────────────────────────── << MODULES >> ────────────────────────────

local ProfileStore = require( ServerScriptService.Utilities.ProfileStore )
local BadgeLoader = require( ServerScriptService.PlayerScripts.BadgeLoader )
local PlayerData = require(ServerStorage.Data.PlayerData )

--────────────────────────── << VARIABLES >> ──────────────────────────

local DATA_STORE_KEY = RunService:IsStudio() and "Development" or "HammerWars_Stats_V1"
local Template = { 
	Smashes = 0,
	Kills = 0,
	Hammer = "Default",
	Settings = {
		["HealthBar"] = true,
		["BackgroundMusic"] = true,
		["DeathSounds"] = true,
		["PlaySFX"] = true,
		["EquipRadio"] = false,
		["HammerShake"] = false,
		["KillstreakMusic"] = true,
	},
}

local Profiles = {}
local PlayerStore = ProfileStore.New(DATA_STORE_KEY, Template)

--────────────────────────── << FUNCTIONS >> ──────────────────────────

local function setupLeaderstats(player, data)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	-- Create Smashes
	local smashes = Instance.new("IntValue")
	smashes.Name = "Smashes"
	smashes.Value = data.Smashes
	smashes.Parent = leaderstats

	-- LIVE SYNC: Update profile data immediately when value changes
	smashes.Changed:Connect(function(newVal)
		data.Smashes = newVal
	end)

	-- Create Kills
	local kills = Instance.new("IntValue")
	kills.Name = "Kills"
	kills.Value = data.Kills
	kills.Parent = leaderstats

	kills.Changed:Connect(function(newVal)
		data.Kills = newVal
	end)

	-- Create Hammer String
	local hammer = Instance.new("StringValue")
	hammer.Name = "Hammer"
	hammer.Value = data.Hammer
	hammer.Parent = leaderstats

	hammer.Changed:Connect(function(newVal)
		data.Hammer = newVal
	end)
end

local function onJoin(player)
	local profile = PlayerStore:StartSessionAsync(tostring(player.UserId), {
		Cancel = function()
			return player.Parent ~= Players
		end
	})

	if profile ~= nil then
		profile:AddUserId(player.UserId)
		profile:Reconcile()

		-- Critical: If the session ends unexpectedly (kick/error), clean up
		profile.OnSessionEnd:Connect(function()
			Profiles[player.UserId] = nil
			PlayerData[player.UserId] = nil
			player:Kick("Data Session Ended")
		end)

		if player.Parent == Players then
			Profiles[player.UserId] = profile
			PlayerData[player.UserId] = profile.Data -- Shared reference

			-- Initialize Leaderstats and Badges
			setupLeaderstats(player, profile.Data)
			BadgeLoader:SetStats(player, profile.Data)
		else
			profile:EndSession()
		end
	else
		player:Kick("Failed to load data (DataStore Error)")
	end
end

local function createInstances(ply : Player)
	-- Settings values that aren't in leaderstats but might need to exist
	local DeathSounds = Instance.new("BoolValue")
	DeathSounds.Name = "DeathSounds"
	DeathSounds.Value = PlayerData[ply.UserId] and PlayerData[ply.UserId].Settings.DeathSounds or true
	DeathSounds.Parent = ply

	local PlaySFX = Instance.new("BoolValue")
	PlaySFX.Name = "PlaySFX"
	PlaySFX.Value = PlayerData[ply.UserId] and PlayerData[ply.UserId].Settings.PlaySFX or true
	PlaySFX.Parent = ply
end

--────────────────────────── << SIGNALS >> ──────────────────────────

Players.PlayerAdded:Connect(function(ply)
	onJoin(ply)
	createInstances(ply)
end)

Players.PlayerRemoving:Connect(function(ply)
	local profile = Profiles[ply.UserId]
	if profile then
		-- No need to scrape leaderstats here; the .Changed events handled it!
		profile:EndSession()
	end
end)

-- Early joiner check
for _, player in Players:GetPlayers() do
	task.spawn(onJoin, player)
end

--────────────────────────── << SERVER SHUTDOWN >> ──────────────────────────

game:BindToClose(function()
	print("Server shutting down, releasing profiles...")

	for _, ply in ipairs(Players:GetPlayers()) do
		local profile = Profiles[ply.UserId]
		if profile then
			profile:EndSession()
		end
	end

	task.wait(RunService:IsStudio() and 2 or 5)
end)

When 1 player joins, the smashes is first, the kills are second, and the hammer is last. but when 1 more player joins, then the hammer is first and the smashes are last. why? i need the smashes. first.
1 Like

Hey! To post on the scripting support category you’re supposed to answer the 3 questions

  1. What do you want to achieve? Keep it simple and clear!
  2. What is the issue? Include screenshots / videos if possible!
  3. What solutions have you tried so far? Did you look for solutions on the Creator Hub

which this post doesn’t, please edit your post or make a new post that answers these 3 questions. And just personal preference, don’t put two seperate scripts in the same code block. Read more at About the Scripting Support category

1 Like

Test it in an empty place by removing the data part and leaving only the leaderstats, see if it still happens, maybe because ProfileStore? idk

And you can add NumberValue “Priority” for priority, I have no idea when they added order in leaderstats lol
https://create.roblox.com/docs/en-us/players/leaderboards#order-stats

i added this but its not doing anything

local isPrimary = Instance.new(“BoolValue”)
isPrimary.Name = “IsPrimary”
isPrimary.Value = true
isPrimary.Parent = Smashes

1 Like

Can you show a screenshot so I can see what order they are in?

I believe it could be because roblox stores leaderstats alphabetically.

This could help:

how can i override the alphabetical issue? and also it happens when 2 players join. if only 1 player joins, its good

You can’t, however roblox normally does this

IntValue first, then StringValue

fixed it with this instead

priority was the solution but just in a different way than i did