Data storing/presenting using ProfileService and ReplicaService!

Did you make sure that the name of the Replica you’re listening to is as the same as the server’s creation of replica’s name?

If you’re referring to DataToken_ .. player.UserId, then yes I am.

You should look into whether your server code creates that replica in the first place. Also make sure your replication settings for that replica are set correctly.

This is currently how the replica is being made:

-- DataManager.lua
local DataReplica = ReplicaService.NewReplica({
        ClassToken = ReplicaService.NewClassToken("DataToken_" .. player.UserId),
        Data = profile.Data,
        Replication = "All" -- change later
    })
DataReplicas[player] = DataReplica

This runs in onPlayerJoin. I also have tested with the examples provided in the library and everything works correctly.

Add a print right before replica creation on the server-side

On another note:
ReplicaService.NewClassToken() can only be ran once during runtime for every class name - if your players rejoin the game this function will throw an error. User id’s should be put inside Replica.Tags.

1 Like

I think I’ll rewrite this part of the tutorial (naming the Replica).

Ah, it seems you were correct. The replica isn’t being created properly. I just assumed it was because I had checks to see if they were nil or not. This is currently how I am creating the replica and it still seems to not work:

local DataReplica = ReplicaService.NewReplica({
                ClassToken = ReplicaService.NewClassToken("DataToken_" .. randomId),
                Data = profile.Data,
                Tags = {
                    playerId = player.UserId,
                    Id = randomId
                },
                Replication = "All" -- change later
            })
DataReplicas[player] = DataReplica
1 Like

I wasn’t able to find anything on your page about an update, would you still recommend this tutorial?

I get this error when the player exits the game

image

I have deleted DataReplicas[player] = nil and did not get the error, I also tried deleting DataReplicas[player]:Destroy() and leaving the = nil and did not get the error either.
So, do I leave the = nil or :Destroy() or do I have to leave both?

Correct me if I’m wrong, but doesn’t this defeat the purpose of ReplicaSerivce?

If you’re re-setting the same table each time, you’re causing ReplicaService to send the full table of data each time, and not only the newest value.

I’ll elaborate with your example:

Server:

local PlayerProfile = {
    Money = {
        Coins = 1,
        Stars = 2
    }
}

function Manager.SetData(userID: number, path: string, value: any)
    local profile = Manager.Profiles[userID]
    if profile ~= nil then
        PlayerProfile.Money.Coins += 1
        profile.Replica:SetValue("Money", PlayerProfile.Money)
    end
end

Client

ReplicaController.ReplicaOfClassCreated(tostring(Player.UserId), function(replica)
    replica:ListenToChange({"Money"}, function(new_value)
        print(new_value)
    end)
end)

Output

{
  ["Coins"] = 2,
  ["Stars"] = 2
               } -  Client```
1 Like

How do I add multiple values? in my case 12. Because I added Health like this:

local CashProfileStore = ProfileService.GetProfileStore("CashProfileStore",{
	Cash = 100;
    Health = 100
})

Then I did this:

	DataReplica:SetValue({"Cash"}, newDataValue)
    DataReplica:SetValue({"Health"}, newDataValue)

The local script down below is the exact same you made. But whenever i click the part it increments +1 for both values Cash and Health. How is this possible when I never referenced health.

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local Player = Players.LocalPlayer
local PlayerGui = Player.PlayerGui
local CashTextLabel = PlayerGui:WaitForChild("ScreenGui",10).TextLabel

local ReplicaController = require(ReplicatedStorage:WaitForChild("ReplicaController"))

ReplicaController.ReplicaOfClassCreated("DataToken_"..Player.UserId,function(replica)  -- the ReplicaOfClassCreated function will pass in the requested replica token in the attached function's argument.
	replica:ListenToChange({"Cash"},function(newVal) -- listening for our cash value to change.
		print("Data changed!")
		-- your logic to show the updated value
		CashTextLabel.Text = "Cash: "..newVal
	end)
	CashTextLabel.Text = "Cash: "..replica.Data["Cash"] -- it is a good idea to also set the value outside the scope of Replica:ListenToChange() so that when the replica token's creation is finished, we will instantly present the data from Replica.Data
end)

ReplicaController.RequestData()

Thank you for the tutorial! It’s very simple and easy to follow. There is only one thing I noticed and would want to ask about though and I’m not sure if anybody else has pointed it out since there are a lot of replies to this page.

Correct me on this if necessary since I’ve only recently discovered ProfileService and ReplicaService (I’m also not sure if this was the case upon writing the tutorial); but is it still necessary to set the data in the Profile before setting the value in the Replica? The replica isn’t creating a deep copy of the table and in the examples and documentation they don’t set it beforehand. (The documentation also asks not to do this.)
I have omitted this in my version of the data manager as well, so I’m assuming this is just a personal thing. Outside that, this tutorial still holds merit to this day, cheers!

Thanks for this tutorial!

Just a general question,

function DataManager:SetData(player,dataToSet : string,newDataValue)
	local PlayerProfile = self:GetData(player)
    local DataReplica = DataReplicas[player]
	if PlayerProfile and DataReplica then
		local OldDataValueType = typeof(PlayerProfile.Data[dataToSet])
		
		if OldDataValueType == typeof(newDataValue) then
			PlayerProfile.Data[dataToSet] = newDataValue
            DataReplica:SetValue({"Cash"}, newDataValue)
		end
	end
end

On the line, DataReplica:SetValue({"Cash"}, newDataValue), do we have to make the first arg whatever value it has to be, just like how you did it, or can we set it to ‘dataToSet’, in my datastore it only works when I set it directly to the value I want it to change, (keep in mind I have multiple values in my table).

1 Like

The destroy is trying to destroy something that doesn’t exist anymore because you set the parent to nill.
so just get rid of one of them it shouldn’t make a difference.

2 Likes

Been using it (and did some DevForum reading) for a while and I believe this actually does defeat the purpose of ReplicaService. Re-setting a table with many keys and then sending it over to the client would practically bloat their ping even for a short moment. You shouldn’t be sending too much data to the client at any given frame. It is better to just define a path towards the value you want to update. It also cleans up the code in the Manager from:

function Manager.SetData(userID: number, path: string, value: any)
    local profile = Manager.Profiles[userID]
    if profile ~= nil then
        PlayerProfile[path] = value
        profile.Replica:SetValue(path, value)
    end
end

To:

function Manager.SetData(userID: number, path: table, value: any)
    local profile = Manager.Profiles[userID]
    if profile ~= nil then
        profile.Replica:SetValue(path, value)
    end
end

--------------------------------------------
-- In another script, presumably a script that adds one coin when touched

Manager.SetData(userID, {"Money", "Coins"}, PlayerProfile.Money.Coins + 1)

It’s also important to note that this is just a post for learning how to use ProfileService and ReplicaService and not code you can just nab off of the internet, and was made with beginners (like me) in mind. I implore anybody reading this to check out the official documentation since they do hold some valuable information (such as the subject of this reply!) that you may or may not need and/or know. Perhaps you could even use that knowledge (and hopefully wisdom) that you’ve gathered to refine the scripts using these modules.

2 Likes

Hi, how would you add a key-value pair into a dictionary using the mutators provided (SetValue, ArraySet, ArrayInsert)

I tried it in many different ways but couldn’t get it to work. One of my attempts was like this:

dataReplica:ArrayInsert(dictionary, key)
dataReplica:ArraySet(dictionary, key, value)
1 Like