Skip to content
deltascripts.
Blog

Build a Color-Change Button: Validate Roblox Requests on the Server

Connect a GUI button to a shared display block using a RemoteEvent, an allowed-color list and a per-player cooldown in your own Studio project.

By deltascriptsco-admin Published 5 min read
A color request passing through a server checkpoint before changing a display block

A GUI button can ask to change a shared object, but the server should decide whether that request is allowed. A small color display makes the distinction easy to see: the client sends a color name, and the server chooses the actual Color3 value from a short list.

This walkthrough is for a place you own in Roblox Studio. Every living player is deliberately allowed to change this one decorative display from anywhere in the place. It has no currency, rewards or saved progression. A shop, door or inventory action would need additional rules for ownership, distance and permission.

Create three clearly named objects

Insert an anchored Part into Workspace, rename it ColorDisplay and give it an obvious gray starting color. Put it where players can see it from the spawn area. In ReplicatedStorage, add a RemoteEvent named RequestDisplayColor.

In StarterGui, create a ScreenGui containing a TextButton. Give the button readable text such as Mint, a visible size and a position inside the screen. Add a LocalScript as a child of that button. The GUI is copied to a player’s PlayerGui during play.

ReplicatedStorage
  RequestDisplayColor (RemoteEvent)
Workspace
  ColorDisplay (Part, Anchored = true)
ServerScriptService
  DisplayController (Script)
StarterGui
  DisplayControls (ScreenGui)
    MintButton (TextButton)
      RequestColor (LocalScript)

Names matter because the scripts below look up these exact objects. For a refresher on where the two script types run, see Script, LocalScript and ModuleScript.

Let the button send a small request

Put this code into RequestColor, the LocalScript inside MintButton:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local request = ReplicatedStorage:WaitForChild("RequestDisplayColor")
local button = script.Parent

button.Activated:Connect(function()
    request:FireServer("mint")
end)

The button sends a short identifier. It does not choose a part to modify, supply a player identity or send a price. OnServerEvent receives the real sending Player as its first parameter; our color name follows that parameter.

There is no client cooldown in this version. That keeps the important rule visible in the server code. You can later add button feedback for convenience, but a disabled GUI button would not replace the server’s timing check.

Make the decision in a server Script

Create DisplayController in ServerScriptService and paste this code into it:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local request = ReplicatedStorage:WaitForChild("RequestDisplayColor")
local display = workspace:WaitForChild("ColorDisplay")
assert(display:IsA("BasePart"), "ColorDisplay must be a Part")

local allowedColors = {
    mint = Color3.fromRGB(130, 220, 175),
    blue = Color3.fromRGB(110, 170, 235),
    amber = Color3.fromRGB(245, 185, 85),
}

local lastRequestAt = {}
local COOLDOWN = 1

request.OnServerEvent:Connect(function(player, colorName)
    local now = os.clock()
    local previous = lastRequestAt[player]

    if previous and now - previous < COOLDOWN then
        return
    end
    lastRequestAt[player] = now

    if typeof(colorName) ~= "string" then
        return
    end

    local color = allowedColors[colorName]
    if not color then
        return
    end

    local character = player.Character
    local humanoid = character and character:FindFirstChildOfClass("Humanoid")
    if not humanoid or humanoid.Health <= 0 then
        return
    end

    display.Color = color
end)

Players.PlayerRemoving:Connect(function(player)
    lastRequestAt[player] = nil
end)

Notice what the server owns

The server holds the palette and a fixed reference to the display. The client cannot use this handler to select an unrelated part: the handler never accepts a target object. Unknown names have no entry in allowedColors, and non-string values are rejected before the lookup.

The timestamp is recorded before checking the name and character. That is intentional: an invalid request also uses the player’s one-second interval. Repeated requests inside that interval return early. The table has one entry per participating player, and PlayerRemoving clears that entry when the player leaves.

The character check implements the rule chosen for this example: only a living character may operate the display. It is separate from the cooldown. Passing a timing check does not establish a player’s right to perform an action.

A RemoteEvent delivers a message without waiting for an application response. Here, the visible result is the server’s replicated part color. If you later need a rejection message in the GUI, add an explicit feedback path; do not assume FireServer returns the handler’s decision.

Try valid and invalid cases in your own place

Start Play, find the gray display and press Mint. The expected result is the mint palette color. To add Blue and Amber buttons, duplicate the TextButton with its LocalScript, change each button’s label and replace only the requested string with "blue" or "amber".

A small request-validation checklist
CaseExpected result
A living player requests mint after the cooldownThe server changes the display to its mint value.
The request names purpleNo change: the name is outside the allowed palette.
The request contains the number 17No change: the argument has the wrong type.
Two requests from one player arrive less than a second apartThe second is rejected by the server cooldown.
Another living player requests amberThe shared display may become amber, under the deliberately open rule.

For the invalid cases, change the argument in your own LocalScript and start a fresh test. Keep an eye on server Output for errors. In a multi-client Studio test, inspect both clients to check that they see the server’s color change. Record which argument was sent and which color appeared; that makes a failed case much easier to trace.

Match the checks to the real action

This palette avoids accepting arbitrary numbers or complex objects. That is a useful simplification for learning, not a complete security design for every remote. A purchase handler must verify its server-owned price and available balance; a nearby interaction must verify location; a private control must verify who may use it.

The one-second interval is per player. Several players can still take turns changing the same display quickly. If that is undesirable, add a shared display cooldown as an additional product rule rather than pretending the per-player timer already provides one.

When expanding the example, change one behavior and compare the result with the previous version. The diff walkthrough helps keep those changes visible, and the Output guide helps locate a failed object lookup or unexpected type.

References

Comments 0

No approved comments yet. Start the discussion.

Every comment is reviewed by a moderator before publication. Enter only a nickname and your comment; no email or account is needed.

Privacy policy