Introducing, StatSerivce!
Earlier today, I released a simple leaderstats wrapper called LeaderstatsService. This module recieved some awesome feedback, so I’ve decided to remake it trying to include some of the best suggestions there were.
So, here we have StatService! StatService automatically creates leaderstats and saves them.
I can do this myself easily! Why do I need a stupid module??
It’s for beginners, the module code is pretty easy to read too, so it’s a learning resource I suppose.
This module uses the Open-Sourced ProfileService, so this module is required for this module too.
Let’s get started with a simple documentation.
:Create(Player, Name, Class, Value)
Returns [Instance]
Example usage:
StatService:Create(plr, "Cash", "IntValue", 0)
Don’t worry about the value argument, the module will automatically overwrite it if the user has any saved data for that stat.
:Delete(Player, Name)
Returns nil
Example usage:
StatService:Delete(plr, "Cash")
This function will automatically destroy the leaderstat & clear the players data linked with this stat.
:SetValue(Player, Name, Value)
Returns nil
Example usage:
StatService:SetValue(plr, "Cash", 100)
This function will automatically edit the Leaderstats and the saved data accordingly.
Installation
Code
. . .
--// Configurations
local ProfileService = "Path to Profile Service" --// https://madstudioroblox.github.io/ProfileService/
local DatabaseName = "hhZBG4x9i8" --// Make this random to prevent this script from messing with other-
-- -datastores.
--// Data Saving Code
--// DON'T EDIT BELOW UNLESS YOU KNOW WHAT YOU'RE DOING!!!
-- Services
local PS = require(ProfileService)
local ProfileTemplate = {}
local Profiles = {}
local Database = PS.GetProfileStore(
DatabaseName,
ProfileTemplate
)
local Players = game:GetService("Players")
-- Local Functions
local function playerAdded(player)
print("⌛ Loading "..player.Name.."'s data")
local profile = Database:LoadProfileAsync("StatService-"..player.UserId)
if profile ~= nil then
profile:AddUserId(player.UserId)
profile:ListenToRelease(function()
Profiles[player] = nil
player:Kick("❌ Data became unsecure")
end)
if player:IsDescendantOf(Players) then
Profiles[player] = profile
print("✅ Loaded "..player.Name.."'s data: ", profile.Data)
else
profile:Release()
end
else
--// Failed to load data
player:Kick("❌ Data failed to load.")
end
end
local function createlsFolder(player)
if not player:FindFirstChild("leaderstats") then
local fld = Instance.new("Folder")
fld.Name = "leaderstats"
fld.Parent = player
return fld
else
return player["leaderstats"]
end
end
local function GetPlayerProfileAsync(player) --> [Profile] / nil
-- Yields until a Profile linked to a player is loaded or the player leaves
local profile = Profiles[player]
while profile == nil and player:IsDescendantOf(Players) == true do
task.wait()
profile = Profiles[player]
end
return profile
end
-- Initialize
for _, player in ipairs(Players:GetPlayers()) do
task.spawn(playerAdded, player)
end
Players.PlayerAdded:Connect(function(p)
playerAdded(p)
end)
Players.PlayerRemoving:Connect(function(player)
local profile = Profiles[player]
if profile ~= nil then
profile:Release()
end
end)
-- Module
local StatService = {}
function StatService:Create(player: Player, name: string, class: string, value: ValueBase)
if player:IsDescendantOf(Players) then
local leaderstats = createlsFolder(player)
local StatInstance = Instance.new(class)
StatInstance.Name = name
StatInstance.Value = value
StatInstance.Parent = leaderstats
print("✅ Created "..name.." for "..player.Name)
--// Save the new stat
local Profile = GetPlayerProfileAsync(player)
if Profile then
local Data = Profile.Data
if Data[name] then
--// This stat is saved! Try and load it.
local Stat = Data[name]
StatInstance.Value = Stat["Value"]
else
--// Stat is new
Data[name] = {
["Class"] = class,
["Name"] = name,
["Value"] = value
}
end
coroutine.wrap(function()
while task.wait() do
local d = GetPlayerProfileAsync(player)
if d == nil then continue end
if d.Data[name] == nil then continue end
StatInstance.Value = d.Data[name]["Value"]
end
end)()
return StatInstance
end
else
warn("❌ No valid player was specified!")
end
end
function StatService:Delete(player, name)
if player:IsDescendantOf(Players) then
if player:FindFirstChild("leaderstats") then
if player.leaderstats:FindFirstChild(name) then
player.leaderstats:FindFirstChild(name):Destroy()
local Profile = GetPlayerProfileAsync(player)
if Profile ~= nil then
Profile.Data[name] = nil
print("✅ Deleted \""..name.."\" from "..player.Name)
end
else
warn("❌ No leaderstat to delete.")
end
else
--no ls
warn("❌ No leaderstats for this profile found")
end
else
warn("❌ No valid player was specified!")
end
end
function StatService:SetValue(player, name, value)
if player then
if player:IsDescendantOf(Players) then
if player:FindFirstChild("leaderstats") then
if player.leaderstats:FindFirstChild(name) then
local Profile = GetPlayerProfileAsync(player)
if Profile ~= nil then
Profile.Data[name]["Value"] = value
print("✅ Set \""..name.."\" to "..value)
end
else
warn("❌ No leaderstat to edit.")
end
else
--no ls
warn("❌ No leaderstats for this profile found")
end
else
warn("❌ No valid player was specified!")
end
else
warn("❌ No valid player was specified!")
end
end
return StatService