Working with the Replication Boundary

Introduction

One of many areas of game development whether online or offline is security and this is one area that I see a lot of developers struggling with in scripting support. Along side security, there are also some cases where a developer doesn’t know where to handle the replication which can lead to a security flaw or performance issue.


Background

• What is Replication ?

the action or process of reproducing or duplicating
– Merriam-Webster Dictionary

or simply copying.

In terms of Roblox, this is the process of making the server copy information to the client or vice versa.

• What is the Replication Boundary ?

Similar to the previously known “filtering enabled”, is the boundary which prevents unauthorized client changes to replicate to the server.

Actions made by the client will no longer freely replicate to the server. Instead, RemoteEvents and RemoteFunctions need to be used by the client to ‘request’ the server preforms certain actions on its behalf.
– Developer Hub

Take note of the word “request”. This is a very important concept I will go into detail later.

• What are Remotes ?

This is the bridge between the client and the server or vice versa. This allows you to form mediums of communication between the boundary and formally transmit data.

There are 2 types of Remotes:

- Remote Event

This is the basic bridge which allows basic transmission of data.

From a client’s perspective, you can just send data to the server and expect no formal response.

From a server’s perspective, you can just send data to one, or all clients and expect no formal response.

- Remote Function

This is the slightly advanced bridge which allows you to return the result of the transmission (did the server run into an error during processing? etc.) If you do not need to know whether the server ran into an error, use Remote Events.

From a client’s perspective, you can just send data to the server and expect a response. (Something went wrong, invalid data etc.)

From a server’s perspective, you can just send data to one, or all clients and expect a response.


Creating a Network Model

A network model/diagram is the flow of your data transmissions. This allows you to track how the data reaches the other side. Creating a visual diagram is optional, but it helps developers new to handling replication.

An example of a basic visual network model/diagram can be seen below:


You can use any photo editing program, notepad, notebook or just a simple piece of paper to make your network model.


Remote Event or Remote Function ?

As previously stated, if you need the result of the process after transmitting data, use a remote function. Remote functions are good for user experience (UX) since you are letting the player know what’s going on in the background.

“Did something even happen?”
“Why is my request taking so long?”
“Did my data surely save?”

But there are cases where you shouldn’t need a result. Let’s say you have a script that moves the player’s head when that player moves their camera. Unless you can implement an anti error spam or fail-safe, if something goes wrong in this system it would surely be a pain to dismiss endless error prompts.


Handling Data Transmission

• Transmission Rates

A common misconception is “If I fire a remote every 0.1 second, will it be bad?”

No. Spamming remotes doesn’t cause lag. In fact firing a remote every frame wouldn’t have a significant impact (remotes have a built in cool down anyway) . What you should be concerned about is what the server does after getting the request. If the process is going to take a lot of processing power, that is bad and you should implement a debounce client and server side.

In the first place, there isn’t any documentation about remote data transmission rates.

• What Data Should be Transmitted

I’ll mention this as early as now. As much as possible, make all your systems server sided. Avoid client intervention.

Of course, sending large amounts of data will impact transmission speed. It’s like downloading a file off the internet.

When transmitting data, do not send data that the client or server won’t use at all. Save resources even though it won’t have a significant impact.

Do note that remotes do not like mixed tables. The remote will omit the dictionary and your data will be incomplete.

local Dictionary = {
    ["Name"] = "wevetments",
    ["Age"] = 500
}

local Table = {
    [1] = "wevetments",
    [2] = 500
}

local MixedTable = {
    ["Name"] = "wevetments", --this is omitted
    [2] = 500
}

• The Request Concept

Request is different from Command

Request considers if that request can be fulfilled, a command is something that must be done at all means.

The client only requests the server to do something. This means the client should never have authority over the server.

What is an example of a command?

|Local Script|
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RemoteEvent = ReplicatedStorage:WaitForChild("AddMoney")

RemoteEvent:FireServer(999999999)
|Server Script|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local RemoteEvent = ReplicatedStorage:WaitForChild("AddMoney")

RemoteEvent.OnServerEvent:Connect(function(Player, Amount)
    local Leaderstats = Player.leaderstats
    local MoneyStat = Leaderstats.Money
    MoneyStat.Value = MoneyStat.Value + Amount
end

Boom you just crashed the market of your own game. Say goodbye to dev-ex-ing your hard work.

What is an example of a request?

|Local Script|
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RemoteFunction = ReplicatedStorage:WaitForChild("PurchaseWeapon")

local Request = RemoteFunction:InvokeServer(5)
if Request then
    Prompt("You have purchased this weapon.")
else
    Prompt("You do not have enough money.")
end
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")

local RemoteEvent = ReplicatedStorage:WaitForChild("PurchaseWeapon")
local Weapons = ServerStorage.Weapons

local function ProcessRequest(Player, WeaponId)
    local Leaderstats = Player.leaderstats
    local MoneyStat = Leaderstats.Money
    local Backpack = Player.Backpack
    local SelectedWeapon = Weapons[WeaponId]

    if MoneyStat.Value >= SelectedWeapon.Price then
        WeaponClone = SelectedWeapon:Clone()
        WeaponClone.Parent = Backpack
        return true
    else
        return false
    end
end

PurchaseWeapon.OnServerInvoke = ProcessRequest

Take note that in the event you do rely on client input/data, that should never have a game breaking impact such as completely pasuing the game if a player doesn’t respond.

• Verifying Transmitted Data

There are some cases where you do have to get client input. This is the most important factor when working with the replication boundary, data verification.

Rule of Thumb - Always assume data transmitted from the client has been tampered with.

All data coming from the client must go through sanity checks. Sanity checks are authentication measures to ensure that the data received can be trusted.

An example of a sanity check:
if PlayerMoney >= ItemPrice
This is a very simple example of a sanity check and I won’t be able to cover all the sanity checks needed for every system.

If you cannot verify every bit of client data transmitted, do not let the client transmit data. You are risking security here if you fail to verify all the data.

A more complex example of a sanity check:

local Objects = ServerStorage.Objects

local function ProcessClientInput(Player, PlacementData)
    local Leaderstats = Player.leaderstats
    local Money = Leaderstats.Money
    local HousePlot = workspace.Plots[Player.Name]

    if Money.Value >=  Objects[PlacementData.ObjectName].Price and HousePlot then
        if PlacementData.X <= HousePlot.MaxXPosition.Value and PlacementData.Z <= HousePlot.MaxZPosition.Value and PlacementData.Y <= HousePlot.MaxYPosition.Value and then
            PlaceObject(PlacementData.ObjectName, PlacementData.X, PlacementData.Z, PlacementData.Y, PlacementData.RotX, PlacementData.RotZ, PlacementData.RotY)
            return true
        else
            return "You cannot place outside your plot!"
        end
    else
        return "You do not have enough money or do not own a plot!"
    end
end

This is a sanity check for a placement system. Yes, your weapon against exploits are if statements.

• Where to Handle a Request

An issue I’ve seen lately is, while the logic of security is correct, the execution is wrong and may impact performance to a degree.

The Server Handles

  • Computation
  • Authentication/Security
  • Data Saving
  • Data Routing

The Client/s Handles

  • Local computation
  • User Interfaces (UIs)
  • Explosions
  • Part Physics
  • Tweening
  • Colors
  • and all the alike

An example of bad request handling:

|Server Script|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(Player)
    if IsAdmin(Player) then
        local PlayerGui = Player:WaitForChild("PlayerGui")
        local AdminUI = ServerStorage.AdminPanel:Clone()
        AdminUI.Parent = PlayerGui
    end
end)

An example of good request handling is to simply put your admin panel in starter gui and let the server tell the client if they are an admin.
image

|Server Script|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local RemoteEvent = ReplicatedStorage.EnableAdminPanel

Players.PlayerAdded:Connect(function(Player)
    if IsAdmin(Player) then
        RemoteEvent:FireClient(Player)
    end
end)
|Local Script|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local RemoteEvent = ReplicatedStorage.EnableAdminPanel
local Player = Players.LocalPlayer
local PlayerGui = Player:WaitForChild("PlayerGui")
local AdminPanel = PlayerGui:WaitForChild("AdminPanel")

RemoteEvent.OnClientEvent:Connect(function()
    AdminPanel.Enabled = true
end)

“But wait just there wevetments, can’t exploiters enable the admin panel locally since it’s in their player gui?”

Yes, they can enable it, but who said they can use it? Go back to the Request Concept. Never let the client have authority over the server. All admin features still have to go through sanity checks as requests.

• Security vs User Experience

If there is room to improve the experience your players get, then go ahead and do so, but never ever sacrifice security for UX. If the process is slow, that’s not a reason to remove security checks.

You can trick your players into thinking that their request was quickly processed by simulating an estimated outcome locally.

An estimated outcome is a predicted result of what the server will return after transmission. You can see this mostly in building games such as Welcome to Bloxburg. Did you notice how placing walls and furniture happens so quickly? But in the background, this actually takes about a second or more for you to see that wall you tried to place.

Without Estimated Outcome

With Estimated Outcome

The indicator on the left shows when my mouse button was pressed. You can notice a slight delay after I click without the estimated outcome. I slowed the videos down for a better observation.

• Undocumented Replication Flaws

To see a list of undocumented replication behaviors, check out this post.

As early as now, implement ways to combat these behaviors.


Glossary

Authentication

  • A method of running data through a series of checks to determine if the data can be trusted.

Client

  • Refers to a player’s machine/device.

Command

  • An action that must be executed at all means regardless of authority.

Computation

  • The phase where data is being processed.

Data

  • Refers to the information used for processing.

Data Saving

  • The process where data is saved either externally via HttpService or with DataStoreService.

Data Routing

  • The process of determining which client should get which data.

Debounce

  • A system that prevents simultaneous/redundant calls.

Dictionary

  • A table that makes use of strings as indexes.

Estimated Outcome

  • A prediction of what the server is to return as a result to the client.

Exploit

  • An injected script that takes advantage of security flaws in order to gain personal benefit or cause system wide malfunction.

Explosion

  • The effect caused by the explosion object.

Filtering Enabled

  • A discontinued feature that serves no purpose. Previously allowed developers to enable or disable the replication boundary.

Input

  • Refers to the information used for processing, particularly from a client.

Local Script

  • A code container that can only execute the code it contains on a client machine.

Mixed Table

  • A table that makes use of strings and numbers as indexes.

Network

  • The infratructure that allows the transmission of data from client to server or vice versa.

Network Model/Diagram

  • A visual representation of the flow of data from one machine to another.

Remote

  • An object (either RemoteEvent or RemoteFunction) that allows data to be transmitted through the replication boundary.

Replication

  • The act/process of transmitting data.

Replication Boundary

  • The digital barrier that prevents unauthorized client changes from replicating to the server.

Request

  • A call to the server or client which takes authority into account.

Resources

  • The available computational power used to either save or process
    data.

Sanity Check

  • A method of running data through a series of checks to determine if the data can be trusted.

Server

  • Refers to the machine that the game instance is being hosted on.

Server Script

  • A code container that can only execute the code it contains on a server machine.

Table

  • A collection of data that can be formed with curly brackets {}.

Transmission

  • The process of transferring data from one machine to another.

Transmission Rates

  • The frequency of data transmitted per remote.

Tweening

  • A visual effect handled by TweenService.

User Experience

  • A design that enables players to have a meaningful and effortless experience.

Verification

  • A method of running data through a series of checks to determine if the data can be trusted.

References

https://developer.roblox.com/en-us/api-reference/class/RemoteEvent
https://developer.roblox.com/en-us/api-reference/class/RemoteFunction
https://developer.roblox.com/en-us/api-reference/property/Workspace/FilteringEnabled
https://devforum.roblox.com/t/working-with-remotes-and-the-client-server-boundary/453080


I’m planning to create place files with examples completely based from this post with more in-depth explanations, I’ll post them if I can somehow get to finish them all :stuck_out_tongue:

63 Likes

This was a well-written post that I’m sure many people will benefit from. :clap: Even as of today people think of “filtering enabled” as a bad thing, and it’s important to know that it’s there to help you, the developer. Because of the filtering enabled change, many have just sneaked in a fix so that the game works, without even considering to do any sanity checks.

With this post I hope more people become aware of how to properly implement a good network setup between the client and server so that we don’t see more of the “addMoney:FireServer(999999”.

6 Likes

Overall, a good tutorial, but I have a few nitpicks.

Just to clarify, a table is anything defined with {}, regardless of the keys. This is just an array (in the first quote) or a dictionary/array/mixed table (second quote). You can use any data-type as a key in a table, not just numbers/strings. For example:

local t = {}
t[t] = t

As a side-note: don’t rely on a RemoteFunction on the server (:InvokeClient) without taking into account that the client can yield you forever. Also, at one point it was faster to just fire a RemoteEvent back and forth, but I’m not sure if that’s true anymore. (This was mentioned later.)

This is really a nitpick, and I’m sorry in advance, but FilteringEnabled is technically not its name.

6 Likes

Very very thorough and useful thread, thanks a lot! This will definitely be useful to me in my personal development endeavors.

1 Like

Well written post but there are actually some cases of games that need security flaws to work well for UX.
Most of the time you can get away with some delays or estimation but two games that come to mind are fast paced responsive melee combat games like Mordhau or Chivalry. Those two games are established games outside of roblox but still suffer from these issues. But they’re given better tools to deter client side exploits :confused:

I know , kind of a niche example, and it’s not good practice to think this way - but with games like that there unfortunately needs to be a higher level of authority on the client. That being said you can still add server sanity checks because the server still needs to validate and deal damage which can be delayed, but you may have to painfully allow exploiters to always parry an attack. (Unless you want to get into client “security” through obscurity but that’s another nightmare and probably not worth it)

5 Likes