Replicate your states with ReplicaService! (Networking system)

Oh ok that clears it up for sure. Thank you! So in my code when I am choosing a map. I create a new replica each time. Should I be doing that or should I be doing something more along the lines of:

self.Replica:SetValue({ "Map" }, map_instance)

If your game supports multiple maps, you would use multiple replicas. If your game supports one map you use one replica. Same pattern for the rest of replica implementations.

1 Like

I am really confused about how to use this alongside my ProfileService data system.
The only thing I am attempting to perform is to stream values from the player’s profile. Which includes a table, and a couple of number values.
I am very keen on having constant loops running on the server/client, so what is the best way to have checks or things that are alike

Calling the value directly as a remote sorta thing
or a .Changed type of thing so everytime a change occurs in a value it is replicated??

Overall I find ReplicaService very confusing, any help?

Hi
I want to use ReplicaService together with ProfileService to display a specific player’s stats on a GUI. The player can select which player’s stats they want to see, so ideally I would want to only connect to one player’s stats and then disconnect when a new player is selected, is this possible?

EDIT: nvm, I found out you can simply :Disconnect() the connection.

I’ve been really struggling with how to use ReplicaService alongside my current ProfileService set-up.
Is anyone able to help tutor or teach me how to use this within my game? If you want to contact or help me, send me a message. Thanks

I made a tutorial for this!

What are the performance difference of having one Replica with a lot of nested tables vs a Replica for each table?

This is kind of like the less-or-more-remote-events question.

What I’m thinking is it will save a little on network to have more Replicas (that way you don’t have to be so specific when replicating the path), but maybe takes up more resources/memory? Or maybe there’s more overhead when creating the Replicas?

What I would really like is a better understanding on how the computer and memory works in regards to creating objects.

Sorry if my question is unclear.

For the most part you’ll have to test the impact yourself. Theoretically, since replicas are passive, the performance impact is the same regardless of replica count and will only depend on the mutators used and parameters passed.

Custom “Write” mutators are indexed by integers and can have small network footprints for large state changes.

You should not neglect the convenience of built-in mutators for non ultra massive implementations. Built-in mutators have to replicate the value path every time they’re called, but in most cases this impact is negligible.

1 Like

So beside for the increase in network performance when using more replicas (or custom mutators) is creating more replicas going to use up more memory, or is it such a small amount I shouldn’t worry about the memory usage of 1 passive replica vs 100 passive replicas?

Theoretically as in the more replicas = more memory used up but the difference is negligible that it shouldn’t be thought about?

You can use thousands of replicas without noticing much impact in your memory.

2 Likes

When I try ReplicaService on a live server and leave and connect again I get the error: Token for replica class “replica name here” was already created.

Replication works as expected in Game and I destroy the replica when the profile is released.

profile:ListenToRelease(function()				
	PLAYER_REPLICA[player]:Destroy()
	PLAYER_REPLICA[player] = nil
		
	Profiles[player] = nil
	player:Kick()
end)

I create my replica like this:

PLAYER_REPLICA[player] = ReplicaService.NewReplica({
	ClassToken = ReplicaService.NewClassToken("PlayerReplica_"..player.UserId),
	Data = profile.Data,
	Replication = player,
})

I can see that ListenToRelease fire when I use a Print there.
Any help on this issue would be greatly appreciated.

1 Like

@RobloxWobot @loleris

It seems like the backend of the ReplicaService is causing the Replica:Destroy() function to not properly removing the Replica from the CreatedClassTokens table. I found a fix which is going to the DestroyReplicaAndDescendantsRecursive function or line 373 in the ReplicaService module script and I added this line of code which solves the issue of erroring that this class token has been created upon calling ReplicaService.NewReplica() with the same name for the Replica ClassToken, and destroying the replica and creating the same replica with the same ClassToken name.

This is the forked version of the DestroyReplicaAndDescendantsRecursive function:

local function DestroyReplicaAndDescendantsRecursive(replica, not_first_in_stack)
	-- Scan children replicas:
	for _, child in ipairs(replica.Children) do
		DestroyReplicaAndDescendantsRecursive(child, true)
	end

	local id = replica.Id
	-- Clear replica entry:
	Replicas[id] = nil
	-- Cleanup:
	replica._maid:Cleanup()
	-- Remove _creation_data entry:
	replica._creation_data[tostring(id)] = nil
	-- Clear from children table of top parent replica:
	if not_first_in_stack ~= true then -- ehhhh... Yeah.
		if replica.Parent ~= nil then
			local children = replica.Parent.Children
			table.remove(children, table.find(children, replica))
		else
			TopLevelReplicas[id] = nil
		end
	end
	CreatedClassTokens[replica.Class] = nil	
	-- Swap metatables:
	setmetatable(replica, LockReplicaMethods)
end

Only difference is this line:

CreatedClassTokens[replica.Class] = nil	

Hope this fixes an issue.

3 Likes

Wow, that appears to be working, thank you!
It appears you found a bug and patched it?

This fix could be worth patching the original Replicaservice with. I can’t be the only one running in to this problem? I simply don’t know if I coded something strange or the problem was only with Replicaservice. If the problem was Replicaservice then more people must have ran into this bug before.

I sure do!

Regarding if anyone has faced this before, I think there are many people found it when they playtest it with multiple players.

So what did we learn from it?

Always playtest with multiple people.

1 Like

Why even use Replica.Class as a data carrier for UserId when you also have Replica.Tag and Replica.Data. Your solution is the equivalent of literally giving every Player instace in a roblox game a different ClassName.

Class tokens should be declared outside of replica creation and reused every time a replica representing that class is defined.

In your scenario the class token has to be called just “Player” and be set to it’s own variable OUTSIDE of ReplicaService.NewReplica(). Afterwards you pass that same variable to the ClassToken parameter.

I’ll correct my minimal example to have the class token declared outside the .NewReplica() function to avoid confusion.

3 Likes

Check the parent of the character model when .CharacterAdded fires - If the model is not inside the DataModel, the instance reference will not replicate to clients.

Love the module.
I’m using it in combination with ProfileService and after getting into it it is really intuitive.
As someone mentioned before, a ReplicaRemoved listener would be useful.

Does ProfileService save all the metadata from nested replicas or does it skip the metadata?

ProfileService has no ReplicaService-related integrations and will attempt to save Profile.Data members as they are.

Profile.Data should only contain references to Replica.Data tables instead of Replica objects themselves. Consequently, it’s impractical to nest replicas for profiles as you’ll probably end up replicating some data twice through multiple replicas, but you can have a flat hierarchy of multiple replicas per profile.

2 Likes

How does :SetValues and :ListenToChange work?

I currently have this which from my understanding is the data you wanna send/replicate to the client

	local Replica = RS.NewReplica({
		ClassToken = RS.NewClassToken("Stats"),
		Data = {
			FP = Profile.Data.FP, --(fighter points) basically indicates your skill and where you are in your rank
			Rank = Profile.Data.Rank, --rank name
			Wins = Profile.Data.Wins,
			Loses = Profile.Data.Loses
		},
		Replication = Player,
	})

And then I tried testing how to change values with :SetValues I basically just copied the code from the docs, but kept getting an error saying “attempt to index nil with ‘FP’” I’m guessing it has something to do with {“Data”} so not sure should go here.

	Replica:SetValues({"Data"},{ 
		FP = 6000,
		Rank = "Master",
		Wins = 100,
		Loses = 200,
	})

And then I also tried testing :ListenToChanges, but I just have no idea what this means nvm I realized this is supposed to be on the client I was using it the wrong way

Replica:ListenToChange(path, listener) --> [ScriptConnection]
--   listener   [function] (new_value, old_value)

Sorry for the poorly written post my 2am brain is like half working