Save your player data with ProfileService! (DataStore Module)

Trying to figure out how to use Reconcile() so whenever I update the saveStructure it’ll automatically fill in the missing values.

The wiki does not clarify enough and is pretty vague on how to “implement” it. I’ve been calling it after release but I’m not sure if I’m using it right.

if profile ~= nil then
	profile:ListenToRelease(function()
		cachedProfiles[player] = nil
		player:Kick("Profile has been loaded. Please rejoin.")
	end)
	
	if player:IsDescendantOf(Players) then
		cachedProfiles[player] = profile
		PlayerDataLoaded(player)
	else
		profile:Release()
		profile:Reconcile() --????
	end
else
	player:Kick("Unable to load saved data. Please rejoin.")
end
2 Likes

See the example code in the original post - Reconcile() is called as soon as a profile is loaded.

2 Likes

Hello, I am getting the issue of persistent but random 15 second load times of data between universe teleports. This happens less with 1-2 players, but more with 3+ so this issue was easier to try and reproduce for me.

I checked the documentation and it said to make sure that :Release() is being called, and I did, so I used Discord logs to see if they were ended up being released or not. Here are some screenshots:


I know this is most likely an issue with my code, just wondering if there are things that might cause this that I’m missing or doing wrong. Thank you!

1 Like

That was expected behaviour - try releasing before a teleport request.

ProfileService will wait until a profile is released by the last session and it could be the case that your teleports are racing the .PlayerRemoving triggered releases.

4 Likes

This almost seems too good to be true! I haven’t used it yet, but I’m exploring my Datastore options. Surely there’s a downside to this? I’ve been using Datastore2 for a while now, and data loss has been very minimal and even so - it would only revert back to a previous save of data loss were to occur.

How does Profile handle situations of data loss? How does it react to when Roblox is down and data stores cannot be accessed? How does it fair with games with multiple places that you teleport between in quick succession?

Is there a way to disable saving for testing purposes? I’d like to test any structural changes I make to my saved data without running the risk of a corrupt version being saved.

1 Like

im pretty sure there is a mock feature.

https://madstudioroblox.github.io/ProfileService/api/#profilestoremock

I’d like to load existing saved data. Mock can only load a new profile, not an existing one.

1 Like

you could possibly load in a mock and a live one and copy the live data to mock.

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?