Best way to go about storing in game player profiles?

I am working on a game where each player profile is stored as a table in a server-sided module script.

For example

user1234 = {
Gold = 10;
Level = 5
}

However I’m unsure about using events every time a value from the player’s profile is required client-side. How do you go about storing player profiles efficiently?

Ideally you should index it only once during session and from that point only apply write operation on server side to player’s data.
I suggest you to have 2 independent counters on server and on client and simulate it along with game’s mechanics on each end.

1 Like

Unsure who’s asking for what here.

If the client is asking the server for data, cache it and use an incremental counter on the table to reduce the bandwidth in future get calls if it hasn’t changed.

If the server is sending data to the client, only send changed keys (except on the first one because the client has no data).

1 Like

Apologies, I’m a bit confused by this - is it possible you could give me an example / link me to a resource? Thank you :slight_smile:

Edit: It’s the client asking for server data. Server to server / client to server, I don’t need to worry about right now

Heres a little possible solution for client asking server then, I have no idea if this works but the fundamental idea is there

-- server
ReplicatedStorage.GetPlayerProfile.OnServerInvoke:Connect(function(p: Player, cacheCounter: number)
  local d, counter = profileService.get(p)
  if cacheCounter < 0 or counter > cacheCounter then
    return d, counter
  end
  return nil
end)
-- on client
local COUNTER = -1
local CACHE

local functiton requestData()
  local newData, newCounter = ReplicatedStorage.GetPlayerProfile:InvokeServer(COUNTER)
  if newData then
    CACHE, COUNTER = newData, newCounter
  end

  return CACHE
end

You increment the counter on the server when new data gets set to the profile.

are you basically wanting this?

Personally, what I do, is create folders for each player’s “profile” and store it in a folder under ReplicatedStorage – from there, I have the server set the values (as should be) and have connections listening to when the Value properties are changed. That’s a basic rundown, it’s a bit more elaborate than that but hope this info helps! :slight_smile:

1 Like

When handling player profiles in Roblox, it is usually best to load the profile once when the player joins and keep it server-side for the entire session! This way the server holds the authoritative copy and you avoid constant client requests…

You can implement a simple caching system with a counter so the client only receives updated values when something actually changes. This reduces bandwidth and keeps the game responsive, especially for games with many players. For persistence, you can save the profiles to DataStoreService or use a community tool like ProfileService (Save your player data with ProfileService! (DataStore Module)) which makes handling saves, loads and data versioning much easier!


I would Store profiles in server-side tables, track changes with a counter and send updates only when needed.

--  a simple table
local PlayerProfiles = {}

game.Players.PlayerAdded:Connect(function(player)
    -- initialize profile
    PlayerProfiles[player.UserId] = {Gold = 0, Level = 1}
end)

game.Players.PlayerRemoving:Connect(function(player)
    -- clean up thing
    PlayerProfiles[player.UserId] = nil
end)

This keeps server load low, prevents exploits and ensures the client sees accurate data!!

-- profileservice usage
local ProfileService = require(game.ServerScriptService.ProfileService)

local profileStore = ProfileService.GetProfileStore("PlayerProfiles", {
    Gold = 0,
    Level = 1
})

game.Players.PlayerAdded:Connect(function(player)
    local profile = profileStore:LoadProfileAsync(player.UserId)
    if profile then
        profile:ListenToRelease(function()
            player:Kick("Profile released")
        end)
        player:SetAttribute("Gold", profile.Data.Gold)
        player:SetAttribute("Level", profile.Data.Level)
    end
end)

game.Players.PlayerRemoving:Connect(function(player)
    local profile = profileStore:GetProfile(player.UserId)
    if profile then
        profile:Release()
    end
end)

And then use DataStoreService or ProfileService to save player progress. Increment version numbers when your data structure changes so updates remain compatible and only store or send the parts of the profile that actually changed.

-- simple datastore usage
local DataStoreService = game:GetService("DataStoreService")
local ProfileStore = DataStoreService:GetDataStore("PlayerProfiles")

game.Players.PlayerAdded:Connect(function(player)
    local profile
    local success, err = pcall(function()
        profile = ProfileStore:GetAsync(player.UserId)
    end)
    if not success or not profile then
        profile = {Gold = 0, Level = 1} -- default values
    end
    player:SetAttribute("Gold", profile.Gold)
    player:SetAttribute("Level", profile.Level)
end)

game.Players.PlayerRemoving:Connect(function(player)
    local profile = {Gold = player:GetAttribute("Gold"), Level = player:GetAttribute("Level")}
    pcall(function()

For more info the official DataStore best practices and ProfileService tutorial are great resources to understand how these systems work!!

Hope this helped!

1 Like

It’s better if you use ProfileStore. ProfileService is still stable but ProfileStore is newer and better.

Both ProfileService and ProfileStore are solid choices. ProfileService is stable and well-documented, while ProfileStore is (like you said) newer and has some “modern” improvements.

Which one to use really depends on your game and what features you need, either can work well if implemented correctly!!!

It’s better if you use ProfileStore. It’s the same thing but better. Since this person still hasn’t done none then.. wouldn’t it be better if you go for the better version? ProfileService is stable and working, yes, but at some point it won’t.

I see what you mean about ProfileStore being newer and having some “improvements” but both ProfileService and ProfileStore are good options depending on your needs!!! ProfileService is very stable, widely used and has a lot of community tutorials and examples, which makes it easier to implement features like caching, versioning and safe data releases. ProfileStore might have some modern updates but in practice either system works well as long as you handle data correctly.

It really comes down to which one fits your workflow and which documentation or examples you feel more comfortable following!!!


Just because you personally think ProfileStore is better dosent mean its automatically the best choice for everyone and deffently not the best choice for every game! It really depends on your game and workflow and not you said its better 3 times! Both ProfileService and ProfileStore are good options and each has its strengths.

! they can both work well if implemented properly !


Even if ProfileService runs into issues at some point you or other developers can usually fix it or updates from the creator may address it! Stability isnt just about which system is newer, its also about how well you implement it, handle edge cases and adapt when things change.

I had this exact problem a week or two ago, see the conversation in

where a module for this (Replica) is recommended and its functionality explained (so I made my own implementation)

The gist is, agnostic to the DataStore wrapper you’re using, whenever the server updates a value in the player’s data, fire a remote event to the player that updates that player’s copy of their profile data.

I didn’t like using instances in ReplicatedStorage because it’s simply less efficient and doesn’t work with tables.

I am just saying this because ProfileStore gets updated and ProfileService not. Instead of having to fix new errors (specially with data stores because they’re a pain), the owner of ProfileStore fixes it.

Yes, you can fix these errors, but it’s better to get one that updates.

Imagine you work at an office and you get offered 2 TV’s for your workplace at the same price, both are the exact same televisions but one is 10 years old and doesn’t get software updates, and the other one is 1 year old and has 6 more years of updates to go. You’re obviously gonna get the second one, even if you could fix the issues that may rise from the first television. Why? Because you don’t have to do anything. You can continue working without having to stop to fix those issues.

I’m not forcing to use ProfileStore, but it’s indeed a better option, specially when you haven’t started the data store part of your game.

ProfileStore and ProfileService are almost the exact same thing. The difference between them is so minimal it doesn’t really matter.

1 Like

This is the funniest explanation I’ve read on DevForum yet…

Obviously you can keep ProfileService if you already have it implemented, but if you’re just starting to create the data saving parts of your game, then the best candidate is ProfileStore.

Hard disagree.

Do not use ProfileService in new work. The two APIs are entirely backwards compatible, and the creator has made it clear that ProfileService will not get new updates or bug fixes and has not for the last 4 years. Whether other developers fix these bugs is irrelevant because the creator doesn’t merge them.

2 Likes

Why? I am trying to make you understand that ProfileService is outdated and no longer supported, and that you should never use it for new projects

You’re making sound like a war crime to use ProfileService instead of ProfileStore. Yes, it is recommended to use ProfileStore, and I agree you should use ProfileStore, but the 2 will work perfectly fine even in a new project and have minimal differences.

Right, so why ever recommend the use the outdated-of-4-years version opposed to the new one? There’s no reason to suggest that “either is fine” if it means one might unnecessarily use ProfileService.

I don’t know about war crime, though; all I did was make it abundantly clear that ProfileStore = ProfileService + bug fixes + developer support. If anything the heading I included by the developer of both modules was more insistent than I was.

1 Like