UPDATED MODULE CALLED “ProfileStore” HAS BEEN RELEASED - Click for more info
Mad Studio cross-promo:
Check out ReplicaService for state replication / networking!
If you’re curious about how Madwork is written, give MadLife - code_guidebook a read!
Consider donating R$ to the creator of ProfileService (Click here) if you find this resource helpful!
Madwork - ProfileService
ProfileService is a stand-alone ModuleScript that specialises in loading and auto-saving
DataStore profiles.
It’s documented:
ProfileService wiki
It’s open source:
Roblox library
Watch while you eat a pizza completely healthy meal on the couch - YouTube tutorials:
Thorough ProfileService tutorial by @M_nzter
ProfileService in 10 minutes by @CheekySquid
ProfileService tutorial playlist by @okeanskiy
Session-locking explained and savable leaderstats by @EncodedLua
(Will add new tutorials as they come)
A DataStore Profile
(Later referred to as just Profile
) is a set of data which is meant to be loaded up
only once inside a Roblox server and then written to and read from locally on that server
(With no delays associated with talking with the DataStore every time data changes) whilst being
periodically auto-saved and saved immediately once after the server finishes working with the Profile
.
The benefits of using ProfileService for your game’s profiles are:
-
This is my personal module that I intensively support - any bugs you find are also my bugs - In the very unlikely (astronomically unlikely ) case you do find a bug I’ll usually fix it in around 2 - 3 days if you can give me a decent report.
-
Easy to learn, and eventually forget - ProfileService does not give you any data getter or setter functions. It gives you the freedom to write your own data interface.
-
Built for massive scalability - low resource footprint, no excessive type checking. Great for 100+ player servers. ProfileService automatically spreads the DataStore API calls evenly within the auto-save loop timeframe.
-
Already does the things you wouldn’t dare script yourself (but should) - session-locking is essential to keeping your data protected from multiple server editing - this is a potential cause of item loss or item duplication loopholes. ProfileService offers a very comprehensive and short API for handling session-locking yourself or just letting ProfileService do it automatically for you.
-
Future-proof - with features like
MetaTags
andGlobalUpdates
, you will always be able to add new functionality to your profiles without headaches. -
Made for ambitious projects - ProfileService is a profile object abstraction detached from the
Player
instance - this allows the developer to create profiles for entities other than players, such as: group-owned houses, savable multiplayer game instances, etc.
ProfileService is part of the Madwork framework
Developed by loleris
Example code:
-- ProfileTemplate table is what empty profiles will default to.
-- Updating the template will not include missing template values
-- in existing player profiles!
local ProfileTemplate = {
Cash = 0,
Items = {},
LogInTimes = 0,
}
----- Loaded Modules -----
local ProfileService = require(game.ServerScriptService.ProfileService)
----- Private Variables -----
local Players = game:GetService("Players")
local ProfileStore = ProfileService.GetProfileStore(
"PlayerData",
ProfileTemplate
)
local Profiles = {} -- [player] = profile
----- Private Functions -----
local function GiveCash(profile, amount)
-- If "Cash" was not defined in the ProfileTemplate at game launch,
-- you will have to perform the following:
if profile.Data.Cash == nil then
profile.Data.Cash = 0
end
-- Increment the "Cash" value:
profile.Data.Cash = profile.Data.Cash + amount
end
local function DoSomethingWithALoadedProfile(player, profile)
profile.Data.LogInTimes = profile.Data.LogInTimes + 1
print(player.Name .. " has logged in " .. tostring(profile.Data.LogInTimes)
.. " time" .. ((profile.Data.LogInTimes > 1) and "s" or ""))
GiveCash(profile, 100)
print(player.Name .. " owns " .. tostring(profile.Data.Cash) .. " now!")
end
local function PlayerAdded(player)
local profile = ProfileStore:LoadProfileAsync("Player_" .. player.UserId)
if profile ~= nil then
profile:AddUserId(player.UserId) -- GDPR compliance
profile:Reconcile() -- Fill in missing variables from ProfileTemplate (optional)
profile:ListenToRelease(function()
Profiles[player] = nil
-- The profile could've been loaded on another Roblox server:
player:Kick()
end)
if player:IsDescendantOf(Players) == true then
Profiles[player] = profile
-- A profile has been successfully loaded:
DoSomethingWithALoadedProfile(player, profile)
else
-- Player left before the profile loaded:
profile:Release()
end
else
-- The profile couldn't be loaded possibly due to other
-- Roblox servers trying to load this profile at the same time:
player:Kick()
end
end
----- Initialize -----
-- In case Players have joined the server earlier than this script ran:
for _, player in ipairs(Players:GetPlayers()) do
task.spawn(PlayerAdded, player)
end
----- Connections -----
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(function(player)
local profile = Profiles[player]
if profile ~= nil then
profile:Release()
end
end)
ProfileService is server-side only - you’ll need replication code to pass Profile.Data
to clients. ReplicaService was designed for this job!