Save your player data with ProfileService! (DataStore Module)

In a follow up to my previous post, I have decided to try using MessagingService to determine if the session is still alive. Any thoughts are gratefully appreciated.

Figured out the issue, it was not Profile Service as the source but rather another piece of code I had saving data combined with Profile Service taking up the key requests

No. ProfileService is NEVER a good idea for cross-server communication.

All it takes to make an item copy counter is to have a single key you UpdateAsync and increment the values with the copies created in a single server with some buffering so you’d call as infrequently as possible… And hope that it works. There is little data (to my knowledge) on DataStore keys being stable being written with thousands of UpdateAsync requests. It should certainly not be a feature you heavily rely on.

Okay thank you. I have resorted to using a memory story to update the copies quickly and then updating the dataStore when possible. If the copies are not found in the memory store I then check the dataStore for the backup.

how do i prevent data from being auto saved? I’m trying to make a system that requires the player to click an object to save their progress (like undertale), instead of automatically saving the progress.

@loleris Would this be a correct and efficient way to create Replicas on the client that will never be destroyed and need to be referenced at all times?

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ReplicaController = require(ReplicatedStorage.ReplicaController)

local REPLICA_NAMES = {
	"PlayerSession",
	"PlushieCopies",
	"SeasonPassRewards",
	"Leaderboards",
	"Matches"
}

local Replicas = {}
for i, replicaName in ipairs(REPLICA_NAMES) do
	ReplicaController.ReplicaOfClassCreated(replicaName, function(replica)
		Replicas[replicaName] = replica
		print("Replica: " .. replica:Identify())
	end)
end

ReplicaController.RequestData()
for i, replicaName in ipairs(REPLICA_NAMES) do
	while not Replicas[replicaName] do
		task.wait()
	end
end

return Replicas

I also noticed that you can do replica:ArrayInsert({}, value) but not replica:SetValue({}, value)

There’s DataStoreService, You can use that to save player data, but I don’t think that using datastore editors is useful as it doesn’t work on Event Room

how can you set async?
there isn’t something like DataStoreService.SetAsync

Instead of a function, you set the keys directly. If you’re looking for some sort of set async, then look at my resource because it uses ProfileService and features that function.

1 Like

how would I go about checking a users data from one server while they are in another server

You could just view their profile from the server you’re in

local ProfileService = require(Path.to.ProfileService)

local ProfileTemplate = {}
local UserId = 561097949 --The UserId of the player who's data you want to access

local ProfileStore = ProfileService.GetProfileStore("ExampleStore", ProfileTemplate)
local PlayerProfile = ProfileStore:ViewProfileAsync(tostring(UserId))
local PlayerData = PlayerProfile.Data

--Do whatever you want with PlayerData (you can't change it since the Profile is in view mode)
2 Likes

thanks ill try it out, my bad for not noticing there was a viewprofile function

Is profile service suitable for making stuff like a clan system?

I am getting [ProfileService]: Roblox API services available - data will be saved while testing in an unpublished empty baseplate.

Running this code in the command bar yields error 404 instead of error 403.

local status, message = pcall(function()
	game:GetService("DataStoreService"):GetDataStore("____PS"):SetAsync("____PS", os.time())
end)
print(status, message)

Output:

false HTTP 404 (Not Found)

Sorry if I’m just not using this correctly but I encountered an error with ProcessReciept.

I used your example code and modified it a bit to be inline with what I want to do.
My code:

Here’s a video that shows the problem:

The error doesn’t cause anything out of the ordinary, but I have a feeling they aren’t supposed to appear. I would appreciate if someone could help me fix it.

I don’t think you’ll be able to - I’ve seen these errors too. If you think that every Roblox log error is safe from humanly mistakes made by engine developers… Then yeah just return Enum.ProductPurchaseDecision.PurchaseGranted to ProcessReciept callback immediately and you won’t have any errors.

I personally interpreted the ProcessReciept system differently.

2 Likes

Im experiencing an issue where a replica containing the player profile does not get received by the client.
Both the client and server uses modules.

Server code:

local function playerAdded(player)
	local profile = profileStore:LoadProfileAsync("Player_"..player.UserId) -- get profile

	if profile ~= nil then
		profile:AddUserId(player.UserId) -- GDPR

		profile:Reconcile() -- add missing stuff

		profile:ListenToRelease(function() -- loaded on another server
			profiles[player].Replica:Destroy()
			profiles[player] = nil

			player:Kick("Your data might have been loaded on another server, please rejoin")
		end)

		if player:IsDescendantOf(players) == true then -- loaded and player didnt leave
			HandleData(player, profile) -- this just changes profile.Data before its sent to the client

			-- create profile object with replica
			local profileObject = {
				Profile = profile,
				Replica = replicaService.NewReplica({
					ClassToken = profileToken,
					Tags = {Player = player},
					Data = profile.Data,
					Replication = player,
				}),
				Player = player,
			}

			profiles[player] = profileObject
		else -- left before loaded
			profile:Release()
		end
	else -- could not load
		player:Kick("Could not load data, please rejoin")
	end
end

Client code:

local dataUtil = {}

local plr = game.Players.LocalPlayer

print(0)
local controller = require(game.ReplicatedStorage.UtilityModules.ReplicaService.ReplicaController)
print(.5)

controller.ReplicaOfClassCreated("PlayerProfile", function(replica)
	print(3)
	if replica.Tags.Player == plr then
		print("Data has been received!")
	end
end)

print(1)
controller.RequestData()
print(2)

return dataUtil

You can see I already added debug prints to the client side, and 0, 0.5, 1 and 2 get printed, but 3 does not. ([ReplicaController]: Initial data received does not get printed either)

What am I doing wrong here?

I tried making a repro for the issue but was unsuccessful.

EDIT: Looks like this was caused by mistakenly adding a ReplicaRemoteEvents folder into replicatedStorage beforehand, causing the module to make a duplicate. Whoops

Thank you for the solution.

Is it not possible to store instances inside tables, or what am I doing wrong?
I’m using a remoteFunction to get the data.ItemsSaved table into a local script, where I display the saved tools in a list and where I have the code to save the tools.

When saving a tool, the local script sends the Instance to the server script which is supposed to table.insert into the ItemsSaved the tool, but when doing that it seems it takes some time and then it errors, with the “Cannot store Dictionary in data store. Data stores can only accept valid UTF-8 characters.” error, and the game takes a long time to close too. I know the rest of the scripts are working because im also saving bank money and its working, even while studio testing the data stays (Studio api access is on) so what could be the issue here?

could it be that it only saves instances without their children?