I suggest using fewer tokens. New tokens shouldn’t be created dynamically and OnNew shouldn’t really have to be called in scenarios where you’d have to disconnect the OnNew listener. However you won’t really have a huge impact using several OnNew connections at a time - if you just make one for every possible token (while not using tokens for carrying unique identifiers) you’re going to use a negligible amount of resources.
If you’re creating a single lookup of replicas then you’re basically not using the token system in which case you’re then better off using a single token for all game replicas and putting any identifiers in Tags.
Tokens are only a type of identifier for replicas - you can also identify replicas through Tags all the same, so if you wish you can bypass the token system (use just one token for everything, preferably just one letter or empty string) and not really get any performance impact imo.
Howdy! Great work you’ve made loleris, love it!
I am using profilestore to store player data and was wondering what the best possible way to send data to the client would be?
Currently, I create a new Replica after the player has loaded like this
local playerReplica = replicaModule.New({
Token = replicaModule.Token(`data-{player.UserId}`),
Data = {
data = managerModule:returnProfile(player)
}
})
playerReplica:Subscribe(player)
Asking because I saw you said more tokens isn’t good and I’d like to use the system the best I can.
Is there any better way you’d suggest I do so? Thank you, and have a lovely day
Tokens are not meant to carry unique data like player id’s - A user id should be defined in the Tags argument when creating the replica. Using the Replica module for player data in your codebase should look something like this:
-- Keep your dependency variable name short to keep your code tidy!
local Replica = require(game.ReplicatedStorage.ReplicaClient)
-- Define tokens outside of replica constructor, at the top of your script
-- if you want to use the token many times.
local PlayerToken = Replica.Token("PlayerData")
-- Lua is letter case sensitive; "Replica" and "replica" will always
-- refer to different variables. If you're only creating one replica
-- in any context, just set it to a variable called "replica".
local replica = Replica.New({
Token = PlayerToken,
Tags = {UserId = player.UserId},
Data = managerModule:returnProfile(player),
})
replica:Subscribe(player)
I’ve made a solution for that case! It also yields, not the best code but you should give it a try.
--\\ Modules //--
local Replica = require(game:GetService("ReplicatedStorage"):WaitForChild("ReplicaClient"))
--\\ Code //--
local Replicas = {}
function Replicas.GetReplica(Token : string)
if Replicas[Token] then return Replicas[Token] end
local conn
conn = Replica.OnNew(Token, function(replica)
Replicas[Token] = replica
conn:Disconnect()
end)
repeat task.wait() until Replicas[Token]
return Replicas[Token]
end
Replica.RequestData()
return Replicas
Great module! Enjoying using it. I do have one question though. The module has the function :TableInsert(path, value, index?).
My problem is that this seems to only work with numerical indexes. In my game that I’m coding, some Data things are string indexes. For example :
Items = {
Inventory = {
-->> NOTE : If the Player doesn't have the item, it doesn't exist in their Data
-->> Acquiring the item is like creating/inserting a new value (with a custom index) in the Data
-->> It would be stupid to list out every item and set it to 0 as default
Item = 1 -->> Format : Name = Amount
} ;
};
As you can see, I’d like to insert a new (index, value) pair into the Player’s inventory whenever they acquire a new item they’ve never had before.
If I wasn’t clear on what I meant by the NOTE, here’s an example : If “Money” is a Data, it is permanent (always existing), as opposed to Items which can be newly acquired and deleted.
Is there any way I can use a form of :TableInsert() but with the index being a string? Much appreciated, thanks.
Use Replica:Set({“Items”, “Inventory”, “AnotherItem”}, 2).
Try to bring down the nested table amount to the minimum needed for cleanliness and network performance - The Replica module sends the whole path argument with every :Set() call.
I tried using Replica:Set() and it worked, however the major problem for me is that it doesn’t save. I’m using both ProfileStore and Replica to make my Data system.
Do I have to change it in the ProfileStore too or something? (I’m pretty sure Replica:Set() already sets things ServerSided) (I used to use the old ReplicaService so I’m transitioning to the updated one)
I’m not sure how to get it working, because it doesn’t save the items. (I’m assuming it’s because they’re newly added items)
On a sidenote : unfortunately, I can’t really avoid Nested Tables as much as I want to, otherwise everything would be an absolute mess and I hate unorganized tables. If there’s any method that avoids nested tables, let me know as I’m still inexperienced.
My least favorite part about this is how OnChange is meant to be handled.
-- Replica:
Replica:OnChange(function(type, path, index: number, id: string)
if path[1] == "Equipped" and path[2] == "Units" then
if type == "TableInsert" then
-- code
end
end
end)
-- ReplicaService:
Replica:ListenToArrayInsert({"Equipped", "Units"}, function(replica)
-- code
end)
ReplicaService makes this a lot more readable. Was there a specific reason for the jump? Having more control is good and all, but not being able to put the path in like in ReplicaService is annoying because… well, this doesn’t work:
Replica:OnChange(function(type, path, index: number, id: string)
if path == {"Equipped", "Units"} and type == "TableInsert" then
-- code
end
end)
We can easily write a shallowEqual function, but for every script we use replicas…?
Please consider making this workflow easier somehow!
I never knew about the ListenToArrayInsert method in the old ReplicaService module. However, this seems very useful for my case. You see, I have a Loadout table containing index (numbers) as key for the player’s tower. Each time I want to update, I would update the whole thing (the table) and in doing so I’d have to do replica:SetValue({"Loadout"}, newLoadoutTable). The way I would update is to get the loadout using replica.Data.Loadout and then modifying the table to the new updated loadout that I wanted and thus set it onward which to me, feels very tedious. And also for the client, I’d have to update the entire loadout as well over just one slot and another.
Since these don’t return an actual connection, how do we disconnect the listeners?
replica:OnSet({"Stats", "Test"}, function(new_value, old_value)
print(`Test has changed from {old_value} to {new_value}`)
end)
replica:OnChange(function(...)
print(...)
end)