Coding With Experimental Mode Off
Coding With Experimental Mode Off
Experimental Mode is a very important security feature, but it does present some development challenges when it is disabled. Since clients are not able make changes to the world that other players can see, it is important to make sure that both the clients and server are responsible for specific kinds of tasks. There are also some cases where a client needs to influence the game world and thus need to ask the server to make changes.
In general, the client should be responsible for getting player input and displaying information to that specific player. Tools and character controls respond to player input, and while they may result in changes on the server eventually, they should be initially handled on the client so players get quick and immediate feedback to their actions. Likewise, elements like menus, maps, or HUDs are traditionally displayed directly to the client, and as such should be managed by the client.
Running code on the client is accomplished by using a
LocalScript (a regular
Script object will not run on clients). Editing a LocalScript is the exact same as a regular Script. The primary difference between Scripts and LocalScripts is where LocalScripts will run. A LocalScript will only start execution if it is a descendant of one of the following places:
- A Player’s
- A Player’s
- A Player’s
- A Player’s
Tools (only when equipped by a Player)
Consider a game where the player is presented with a welcome screen when they join. This screen has a button to dismiss it so the player can start playing. To easily give this GUI system to each player, each of these elements are in a
ScreenGui inside of the
Dismissing this GUI when the button is clicked needs to executed by a LocalScript on the client. When a player joins the game, the GUI elements in StarterGui will be copied to that player’s PlayerGui. With Experimental Mode off, the server won’t be able to see these copies. Even if it could, input events fired from the client will not fire on the server with Experimental Mode off.
local Players = game:GetService("Players") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") local welcomeScreen = playerGui:WaitForChild("WelcomeScreen") local welcomeMessage = welcomeScreen:WaitForChild("WelcomeMessage") local startButton = welcomeMessage:WaitForChild("StartButton") local function onStartButtonClicked() welcomeMessage.Visible = false end startButton.MouseButton1Click:Connect(onStartButtonClicked)
The server represents the Roblox machine that is hosting the game and should be responsible for gameplay logic — basically anything a client cannot be trusted with. Saving player data, updating scores, creating parts — these are but a few types of actions the server should manage. There are several reasons for this. First, if the result of the code is to change the geometry of the world (e.g. creating or removing parts), running that code on the server makes sure that all players get updated automatically. Second, this prevents malicious users from ruining the game experience. If a player’s health or score is controlled exclusively by the server, then a hacker cannot change these values (at least not in a way that will affect other players).
Server code can be run with a
Script. With Experimental Mode off, a Script will only run in the following locations:
- A player’s
Code that affects gameplay should in general always run on the server. In this example, there is a healing pad that fully heals a character who steps on it. With this code running on the server, the health change will automatically replicate to all clients so every player sees the same game state. In addition, this cannot be changed by a hacker to do something else (such as hurt the player instead of heal).
local Players = game:GetService("Players") local healingPad = script.Parent local function onTouched(otherPart) local character = otherPart.Parent local humanoid = character:FindFirstChild("Humanoid") if humanoid then humanoid.Health = humanoid.MaxHealth end end healingPad.Touched:Connect(onTouched)
While clients and servers have individual responsibilities and types of tasks they need to perform, clients need to be able to ask the server to perform tasks that only the server can do. Likewise, the server needs to be able to notify individual clients about game information without updating every player at once. The easiest way to do this is to use
RemoteFunctions. These are objects that both Scripts and LocalScripts can use to communicate with one another.
In this example the client has a button that, when pressed, creates a new part in the world. The click of the button has to be handled by a LocalScript, as only the client can listen for input events. The code to create the part is handled by a Script on a server, as only the server can create parts that will be copied to everyone in the game.
-- LocalScript local ReplicatedStorage = game:GetService("ReplicatedStorage") local createPartButton = script.Parent local requestPartEvent = ReplicatedStorage:WaitForChild("RequestPart") local function onButtonClick() requestPartEvent:FireServer() end createPartButton.MouseButton1Down:Connect(onButtonClick) -- Script local ReplicatedStorage = game:GetService("ReplicatedStorage") local requestPartEvent = Instance.new("RemoteEvent") requestPartEvent.Name = "RequestPart" requestPartEvent.Parent = ReplicatedStorage local function onPartRequested(player) local part = Instance.new("Part") part.Position = Vector3.new(0, 20, 0) part.Parent = game.Workspace end requestPartEvent.OnServerEvent:Connect(onPartRequested)
When the button is clicked on the client, the
FireServer on a
RemoteEvent. The server meanwhile is listening for when this event fires. When the event fires, the Script creates the new part.
While using RemoteEvents and RemoteFunctions are the recommended way of client-server communication, they are not secure channels of communication, even with Experimental Mode turned off. It is possible for an exploiter to spoof an remote event firing or change the values that are passed in with the event. With that in mind, it is important to make sure that the server checks all requests that a client sends in to make sure the client is allowed to do whatever it is trying to.
For example, consider a game with a shop system. When the player wants to buy something, he or she will be interacting with an interface on the client. When they have selected what they want, the client will fire a remote event to the server to request the purchase. It is very important that the server is the one that checks if the player has enough money. This way the game can know for certain if the player is allowed to purchase what they want. If the check was instead done on the client instead of the server, malicious users could simply fire the event themselves, bypassing the check.
There are some Roblox instances that work around the Experimental Mode setting. For the most part, these are linked to player input or effects — things that a local player needs to see right away without waiting for the server to say that it is allowed. Others are in place for convenience.
There are two properties of
Humanoids that will replicate from a client to the server even if Experimental Mode is off:
Humanoid/Sit. These two properties are closely tied to player actions, so it is important that they happen right away for the client. Note that this only works for the humanoid in the local player’s
Player/Character. If a client instructs its character to jump or sit, it will do it right away and the server will be updated. If a client tries to update another player’s humanoid however, the instruction will be ignored.
Sound/Playing properties of
Sound will replicate from a client to the server. This means that if a particular client starts playing a sound, all the other clients will play it too. This behavior can be overridden using the
SoundService/RespectFilteringEnabled property. It is important to note that while the playback of sounds will replicate, other properties (such as the playback speed, pitch, volume, etc) will not replicate.
The playback of
AnimationTracks will replicate from a client to the server. If a client calls
AnimationTrack/Stop on an AnimationTrack, the animating model will animate on the client and replicate to the server.
While the properties of parts do not technically replicate with Experimental Mode off, there are cases where a client can move parts. If a part is owned by a client, either explicitly or automatically, if that client simulates physics (such as the part falling, hitting other objects, being moved by constraints, etc), then the resulting changes on that part will get copied to the server.
ClickDetectors are useful tools as most of their events will fire on both clients and the server, even with Experimental Mode off. The input events —
ClickDetector/MouseHoverLeave — all replicate when they fire.