Frequently Asked Questions (FAQ)
Here are answers to some common questions you might have while using DataReplicator.
When should I use :RegisterKey() vs. :Create()?
Think of it this way: Register first, Create later.
:RegisterKey()is like making a reservation. You should use it as early as possible (e.g., when a player joins) to tellDataReplicator"this key is valid and will exist eventually." This is crucial for preventing race conditions where the client might ask for data before it's finished loading from a DataStore. It also hardens your security, as any request for a non-registered key is flagged as suspicious.
Best Practice: Call :RegisterKey() for all of a player's potential data keys inside the Players.PlayerAdded event. Then, call :Create() inside your asynchronous data-loading logic once you have the data from your DataStore.
Why use this instead of just putting values in ReplicatedStorage?
While placing ValueObjects in ReplicatedStorage is simple, it comes with significant drawbacks that DataReplicator is designed to solve:
Security: ReplicatedStorage is universally readable.
DataReplicatorgives you granular control over who can access what data using the authorization callback and even allows you to fully encrypt the data's content.Performance:
DataReplicatorintelligently batches all updates into a single remote call per tick. Replicating dozens ofValueObjectsindividually can create significantly more network traffic.Advanced Features:
DataReplicatorprovides powerful, out-of-the-box features that you would otherwise have to build yourself, such as delta compression, update prioritization, and an intelligent rate limiter.Scalability: It's a structured system designed to handle complex data flows gracefully, making your project easier to manage as it grows.
Is the Transport Encryption slow? When should I use it?
The encryption is extremely fast, but it's not free. It uses ChaCha20-Poly1305, a modern cryptographic cipher known for its exceptional performance, especially on platforms without dedicated AES hardware.
However, there is still a small CPU cost for every encryption and decryption operation.
Rule of thumb:
Use Encryption (
{ Encrypted = true }) for: Any data that is sensitive or could give an unfair advantage to an exploiter if they could read it. This includes player currency, inventory contents, stats, private settings, or any administrative data.Don't Use Encryption for: Data that is public knowledge and changes very frequently, where performance is the absolute highest priority. For example, the replicated position of non-player characters in a lobby might not need encryption.
My :WaitForData() call is timing out. What's wrong?
A timeout in :WaitForData() almost always means the server never created or updated the data for that key, or the client was not authorized to receive it. Here's how to debug:
Check your Authorization Callback: Add a print() inside your
:SetAuthorizationCallbackfunction. Is it being called for the player and key in question? Is it returningtrue? If it returnsfalseornil, the server will silently deny the request, and:WaitForDatawill never receive anything.Check for Race Conditions: Are you sure your data is created before the client calls
:WaitForData? If your server is loading data from a DataStore, it might take a few seconds. Use:RegisterKey()when the player joins to let the system know the key is valid, even while the data is loading.
Can I replicate non-serializable types like Instances or functions?
No. Just like the built-in RemoteEvent and RemoteFunction, DataReplicator can only replicate data types that Roblox can serialize. This includes standard Lua types (string, number, boolean, table) and most Roblox data types (Vector3, CFrame, Color3, etc.).
You cannot replicate Instances directly.
Best Practice: If you need to reference an Instance, replicate a unique identifier for it instead. For example, instead of sending a player's Character model, send the player.UserId. The client can then use the UserId to find the character model in the Workspace. This is more secure and efficient.
-- Server: Don't do this!
-- DataReplicator:Update("PlayerCharacter", player.Character) -- This will fail.
-- Server: Do this instead!
DataReplicator:Update("PlayerCharacter", player.UserId)
-- Client:
local characterUserId = DataReplicator:GetCached("PlayerCharacter")
local characterPlayer = Players:GetPlayerByUserId(characterUserId)
local characterModel = characterPlayer and characterPlayer.CharacterLast updated