Replica - Server to client state replication (Module)

Im sorry i cannot help you idk how your code works, the only thing i can tell you is that i made profile load when new ready player signal fires instead of player added

is there a way to reuse a token and not have the old replica anymore

im trying to use the set method but i keep on getting this problem, i’ve ensured the replica exists but i get this problem and honestly have no idea how to fix it, any help would be appreciated

1 Like

hello how am i supposed to use subscribe
i made
replica.NewReadyPlayer:Connect(function()
print(“ok”)
replica:Subscribe
end)
but it does nothing and the print is not printed
if someone can help me thanks

What is the proper way to destroy a Replica and allow its respective token to be used again? If I make a replica for player data and use their UserId, things fall apart if they rejoin the same server:

It seems like this would be the appropriate addition:

image

Which allows the user to destroy their token when needed:

image

But im not too sure how this would work with the ‘locked’ functionality. Why would a replica even need to be locked after being destroyed? If im destroying a replica im assuming it’s being garbage collected and shouldn’t be used anymore.

As per ReplicaService documentation for tokens:

Tokens are meant to be identifiers for a collection of replicas, not for the individual objects they’re representing. The idea was that there might be cases where multiple people might be working on the same project and may end up using identical identifiers for player or other data - tokens completely prevent this from happening.

It might’ve been my mistake only providing examples where the Token is created wrapped inside the Replica creation function - In a lot of cases you’d actually need to define the token outside of Replica creation and store a reference to that token so you could create multiple Replicas with the same token.

You can additionally identify a Replica to represent a Player by storing an identifier in:

  • .Tags - e.g. Tags = {UserId = 2312310, ...} - This is preferable, because Tags becomes immutable after a Replica is created. Tags was specifically made for storing identifiers like that.
  • .Data - e.g. Data = {UserId = 2312310, ...}

Example:

-- Server
local Token = Replica.Token("PlayerData")

...

local replica = Replica.New({
   Token = Token,
   Tags = {UserId = player.UserId},
   Data = {},
})
replica:Subscribe(player)
-- Client
Replica.OnNew("PlayerData", function(replica)
   if replica.Tags.UserId == game.Players.LocalPlayer.UserId then
      -- This replica is representing local player's data
   end
end)

(In addition you can skip the replica.Tags.UserId == game.Players.LocalPlayer.UserId check if you’re only ever replicating a single “PlayerData” replica to a single player who’s also the owner of that data. There might still be cases where you might want to replicate everyone’s data to everyone)

3 Likes

I dont really understand this module, as there arent any tutorials, but im having an error on line 347 of the replica server module, the pointer is nil, self has functions instead of tables. Did i do something wrong? i just copied the example

Woops, turns out i was doing :Set on the actual module instead of the player’s replica, i guess that makes sense but i really wish these modules had any sort of proper documentation

1 Like

I would love to see a exsample on how to use Replica With ProfileStore. where was an exsample in ReplicaService on how to use ReplicaService with ProfileService

1 Like

I would also appreciate a more in-depth tutorial for getting Replica set up. I’m trying to follow bits and pieces of examples but it’s becoming quite difficult to understand what my unique situation requires me to do with it. I’m patiently waiting for the documentation to be completed. Thank you for the awesome modules!

5 Likes

OnSet doesn’t disconnect upon the script it was called is destroyed. You may say OnSet shouldn’t “really” be used in cases the script it was used can be destroyed, but there may be cases we want to do that, such as making a money indicator UI and making it update automatically. In some games contexts, the UI for money may have ResetOnSpawn. But either way it’s just convenient to have it disconnect automatically, I don’t even know why it doesn’t anyway.

I used the follow to test it out. the profile manager is just to get the Replica object for the local player.
Upon script destroyed, neither Destroying nor AncentryChanged event fired.
so they have no way to let us clean up through them.
but within the OnSet connection itself, we can check if script.Parent == nil and then clean up manually.

I am not sure if Replica Module can incorporate this checking and clean up though.

local ProfileManager = require(game:GetService("ReplicatedStorage").PlayerProfile.ProfileManagerClient)

local LocalPlayer = game:GetService("Players").LocalPlayer

local replica = ProfileManager.GetReplica()
print("!!! TestDestroy Client", LocalPlayer, replica)

local connections = {}
local function CleanUp()
	print("!!! TestDestroy CleanUp")
	for _, connection in connections do
		connection:Disconnect()
	end
	table.clear(connections)
end

table.insert(connections, replica:OnSet({"Coins"}, function(newValue: number, oldValue: number)
	print("!!! TestDestroy OnSet handler: Coins", newValue, "<-", oldValue, script.Parent)
	if script.Parent == nil then CleanUp() end
end))

script.Destroying:Once(function()
	print("!!! TestDestroy Destroying")
	CleanUp()
end)

script.AncestryChanged:Once(function(child, parent)
	print(`!!! TestDestroy AncestryChanged to {parent}`)
	CleanUp()
end)

task.wait(1)
print("!!! TestDestroy Destroy()")
script:Destroy()

Hello, I switched to this module but i cannot find the ArraySet method.

you can simply use Set with a path containing the index
replica:Set({ “Array”, 2 }, value)

1 Like

I have the same issue, no idea what’s causing it and it seems to happen randomly.

Fixed, the problem was that i had my own wrapper function for replica:OnSet, but my function was calling the listener instead of passing it (therefore passing its return value instead of the function itself).

1 Like

Has anyone made the server side listeners for replica? I saw there was something like that for the original.

3 Likes

Any updates on documentation yet? Or did anyone make tutorials?

1 Like

How do I detect when a Replica is destroyed? There are no ways of determining if it is destroyed.

Attribute Component

function Methods:RemoveModifier(identifier)
	local state = self.ActiveModifiers[identifier]
	if not state then return end
	
	if state.Timer then state.Timer:Stop() end
	
	local attr = state.Modifier.Attribute
	if self.Attributes[attr] then
		self:UpdateActiveModifiers(attr)
	end
	
	self.ActiveModifiers[identifier] = nil
	self.Replica:Set({"Attribute", "Modifier", identifier}, nil)
end

function Methods:Clear()
	for identifier, _ in self.ActiveModifiers do
		self:RemoveModifier(identifier)
	end
end

function Methods:Destroy()
	self:Clear() -- clear all active modifiers
	
	for _, signal: Signal.Signal<> in (self.Signal) do
		signal:Destroy()
	end
	
	self = nil
end

Enemy Object

function Methods:Destroy()
	self.Signal.Destroyed:Fire()
	
	-- Destroy components
	for v, component in (self) do
		if (typeof(component) ~= "table") then continue end
		if not component.Destroy or (typeof(component.Destroy) ~= "function") then continue end
		
		--self.Replica.Maid:Add(function()
		--	component:Destroy()
		--end)
		
		component:Destroy()
	end
	
	-- Cleanup signals
	for _, signal in (self.Signal) do
		signal:Destroy()
	end
	
	-- Destroy replica safely
	if self.Replica then
		self.Replica:Destroy()
	end
	
	self = nil
end

I was wondering how i could use NewClassToken as whenever I try to do anything I get the error 20:05:38.652 ServerScriptService.Modules.Replica.ReplicaServer:263: [ReplicaServer]: Token “Enemy” duplicate - Server - ReplicaServer:263

I’m using this to create multiple enemies on the server and do some rendering on the client. Any suggestions?

Create the replica token outside of the replica creation function and keep reusing the same token