Properly Validating User Input
This tutorial is aimed toward Roblox developers who have an understanding of FilteringEnabled and know the difference between the server and its clients.
Example to Work With
Let’s pretend you own a Roblox game that has a store GUI. Within the store interface, there are a variety of buttons that allow you to purchase items when you click on it. Keep this example in mind for the rest of this tutorial.
The Big Question
Here’s the big question: How do you check to see if the player has enough money for the item when they click on it?
The first logical conclusion is pretty straight-forward. Simply compare the amount of money the user has with how much the item costs. If the user has more or equal the amount of the item, allow the purchase. Otherwise, deny the purchase.
if (playerMoney >= itemPrice) then …
Now comes the next challenge. Should the check be done on the server (Script) or from the client (LocalScript)? Let’s look at the pros and cons of both, and then see what the best solution is.
Client-Side Validation
Pros:
- It’s fast. The client can run the comparison right away.
- Feedback. The client can inform the client on-screen about the results.
Cons:
- Cheating and exploiting. You run the risk of having clients change local data, such as manipulating the amount of money they have in-game
- Invisible. The server essentially has no idea that the purchase is taking place.
Server-Side Validation
Pros:
- It’s secure. The client cannot manipulate the server’s results
- Visible. The Server knows what the client is doing and can even inform other clients if needed.
Cons:
- It’s slower. Latency will always be involved with communication between a client and server.
Best Practice
So…should you use client-side validation or server-side validation? The answer is both! You should use both client- and server-side validation. When you do client-side validation, you can give quick results back to the user whether or not they can make a purchase. The server will then make the final validation before actually processing the purchase.
Here’s a simple example:
When a player clicks on a button to buy an item, the client will first check to see if he or she has enough money. If not, the client will display some sort of “Insufficient Funds” message. If the client does have enough money, the client will then ask the server to make the purchase (note that the client has no authority to actually make the purchase). The server will then check the player’s money amount from a location completely cut off from the clients (as to avoid any client manipulation). If the player has enough money, the server will make the transaction. The server will then tell the client whether or not the purchase was a success. The client will then act accordingly to display the final results to the player.
Code Example
Client:
-- Client:
function BuyItem()
-- If not enough money:
if (money < itemPrice) then
-- Show insufficient funds message
-- If enough money:
else
local purchaseSuccess = game.ReplicatedStorage.BuyItem:InvokeServer()
if (purchaseSuccess) then
-- Show success message
else
-- Show fail message
end
end
end
guiButton.MouseButton1Clicked:Connect(BuyItem)
Server:
-- Server:
function BuyItem(player)
-- Get player's money amount from somewhere secure:
local money = GetPlayerMoney(player)
-- If player has enough money, make purchase and return true:
if (money >= itemPrice) then
SetPlayerMoney(money - itemPrice)
return true
end
-- Player does not have enough money; return false:
return false
end
game.ReplicatedStorage.BuyItem.OnServerInvoke = BuyItem
If you have any feedback/correction/suggestions on this, please let me know.