Save your player data with ProfileService! (DataStore Module)

Oh wait nvm, Mock does load existing data. Thanks, that’ll do the trick.

1 Like

I was wrong, it does not load saved data. There should be a variation of .Mock that allows loading existing profiles without saving them.

1 Like

I recommend profilestore:ViewProfileAsync() + .Mock

There are many different types of testing environments developers could need and I don’t want to clutter the API with those niche options. Personally I would see a “preset testing save” as a more ideal testing tool.

Regardless, any of such features can be implemented with 1 or 2 existing ProfileService method calls combined.

7 Likes

The only downside to ProfileService is that it may yield profile loading up to 15 seconds (occasionally) when players are rapidly rejoining the game. It has to do that if it notices the last server hasn’t finished saving data - it’s exactly the scenario that opens an item duping loophole if not treated properly. (Try releasing the profile before teleporting between places in a game universe)

From a practical perspective there is no such thing as Roblox-caused DataStore “data loss” -you lose data by mixing up save keys, somehow ending up treating old keys as empty and overwriting them with a fresh save or even corrupting data if you’re practicing the dark and generally useless arts of custom serialization.

ProfileService shouldn’t skip on old keys, but the rest is up to you.

4 Likes

Oh lovely, because the downside of DataStore2 is that if I want to remove keys due to GDPR reasons, there could be thousands of them and it could take hours just to clear an individuals data. I had to mess around with DataStore2 so that I can alternate people’s data remotely (override myself as the other person) as well as add a system that supports force loading of default data whilst not saving it.

Is there a way to override myself so I can check another players data even when they are not in the same game?

By the looks of it, the Mock stuff kinda provides a way to make it so that every time a player joins, it’s default data? And doesn’t override their “real” data? Of course this is for testing/development purposes. It would be painstaking if data ALWAYS saves during the development process of a game.

I think you’ll find answers to your questions quickly as you skim through the original post & official documentations.

.Mock mocks normal ProfileService functionality without performing any requests to the DataStore.

2 Likes

This is the first time I have ever gotten this problem


function DataService:Get(player, DataWanted)
    
    local profile = Profiles[player]
    if profile ~= nil then
        return profile.Data[DataWanted]
    else
        wait()
        warn("loaded fast")
        return profile.Data[DataWanted]
    end
    
end


--// Client

function MakeFlavour()
    local DataTable = PlayerService:FetchData("Shop")
    for i,v in pairs(ShopModule["Flavours"]) do
        local newClone = CloneFolder.Frame:Clone()
        newClone.Name = tostring(i)
        newClone.Icon.Image = v["Image"]
        if table.find(DataTable["FlavourOwned"], tostring(i)) then
            newClone.Check.Visible = true
        end
        newClone.Parent = FlavourFrame
        newClone.Visible = true
    end
end


function ShopController:Start()
    LayoutUtil = self.Shared.ListModule
    ShopModule = self.Shared.ShopModule
    PlayerService = self.Services.PlayerService
    local p = LayoutUtil.new(PopcornFrame)
    local f = LayoutUtil.new(FlavourFrame)
    local r = LayoutUtil.new(RankFrame)
    ChoiceAnimation()
    MakeFlavour()
    Rotate()
end

Is there a way to prevent this from erroring? This Happens a few times


local ProfileTemplate = {
    Popcorn = 0,
    Coins = 0,
    Shop = {
        FlavourOwned = {"Watermelon"},
        PopcornOwned = {"Popcorn"},
        RankOwned = {"Noob"},
        FlavourEquipped = "Watermelon",
        PopcornEquipped = "Popcorn",
        RankEquipped = "Noob",
    },
}

1 Like

ViewProfileAsync is exactly what I was looking for, thank you.

3 Likes

Are you leaving anything out in the first snippet? if profile is nil, then it won’t magically be non-nil if you add in a wait.

Would it be wise to store Profile.Data in a variable? I cached DataStore2 before and it caused HUGE data reverts when I released my game, luckily it was only around 4/5k players at the time, but I fixed it before it reached 20k+.

For readability sake, is what I’m doing okay?:

EDIT: Instead of using ProfileCache[Plr], I’m using Plr.Name as the index, since the Plr (Player) object my not be easily accessible.

2 Likes

This question is similar to what @StrategicPlayZ asked. Can I have multiple profiles per player running at once? I’m making a game with a build slot save system, if each slot is saved in the same key technically the player can surpass the 4 mb limit (I know it’s unlikely because 4 mb is huge.)

My idea is to have a different profile for each save slot. Then load and release that profile when they load their builds.

So in the end I would have 1 profile saving all their other data (money etc.) and 1 profile saving their current build. Allowing them to make virtually endless save slots because each one is in it’s own key.

I’m asking this to get a better understanding of how and when to make profiles with ProfileService, will this surpass the datastore call limit? Can I make more profiles than there are players and it will even out the datastore calls?

ProfileService creates a little over 2 UpdateAsync calls per minute per active profile (Don’t forget to release your profiles).

I always advise using one profile per player whenever possible due to data being rapidly accessible in a single profile. You may, however, use more than one profile for individual players - pay attention to your DataStore call budget.

Thank you, that’s exactly what I wanted to know.

So my idea should work then because I have 8 players in a server. So it will be making roughly 32 calls per minute and the budget will be 140 calls per minute.

Hello, I have a question. Is it possible to check after loading a profile whether it is the first time that the profile has been loaded or not? I want to do something to players who are strictly only playing my game for the first time.

You could try to use ProfileStore:ViewProfileAsync(profile_key). Basically, if you know what the key would be, for example, profile_1111, for that user specifically then when you do local viewProfile = ProfileStore:ViewProfileAsync(profile_1111), viewProfile.Data or viewProfile.MetaData equals nil because the profile hasn’t been created.

If you’re looking at the Basic Usage example, under the PlayerAdded funcion you see the code below. If you change :LoadProfileAsync to :ViewProfileAsync then change profile to profile.Data you would be able to determine if it’s a new user or not. (profile.Data ~= nil is old user // profile.Data == nil is new user) After that, you would be able to run your custom code as long as you eventually load the actual profile.

Note, I’m not sure if you have to release the ViewProfileAsync loaded profile, but I don’t think so. You also want to remove “ForceLoad”. The API might help you a bit.

local profile = GameProfileStore:LoadProfileAsync(
    "Player_" .. player.UserId,
    "ForceLoad"
)
if profile ~= nil then

Just flag your profiles with a value when you first load them. If the profile already has that flag then it means it was already loaded before. I recommend using Profile:SetMetaTag() for this.

1 Like

This is the solution I will use. I feel as though it is a bit excessive though because you have to make a datastore call when I feel like you should just be able to get whether it is new data from LoadProfileAsync

The problem with this solution is that I already have existing player data and I did not set a tag for whether their profile has been loaded for the first time or not.

The rationale that loleris has for not natively supporting serialization is that data should always be in a savable state.

2 Likes

I don’t think this would be a good idea for a feature.

The point behind ProfileService is that it’s very versatile because you can write your own Interfaces. Creating any feature would just result in bugs because for every game the way that data is saved, got, manipulated, etc is very different.

Instead of creating a feature, you should instead write your own deserializer and serializer within your data interface.

1 Like