I have a shop where when an item is equipped, a StringValue inside of the player called EquippedSword’s value gets changed to the name of the sword. When a round starts, the game checks the value of EquippedSword and gives the player the sword that corresponds to the value.
My question is, is it possible for exploiters to change the value of this StringValue manually with exploits so the game gives them a sword? Let me know if this is in the wrong category too, I was unsure of where to put it.
Anything done on the client stays on the client. Never trust anything the client sends to the server.
Like @SomeFedoraGuy said, if they change the value, it will only change for them. As long as you are not reading their locally edited value (through RemoteEvent or RemoteFunction) you have nothing to worry about. Change the value on the server, and read it on the server. They can do whatever they want to it on their client, but it won’t replicate.
Since the server does the value checking, then no. I suppose the server directly reads using .Value, so if you don’t have any Remotes sending the value data then you’re good.
Alright. When someone equips a sword it’s actually handled through a RemoteEvent since the purchase script is client sided, however I watched some videos and exploit-proofed it as much as I could so exploiters can’t change it.
But yes, the server directly checks the value of EquippedSword and doesn’t use any RemoteEvent’s or RemoteFunction’s. Thanks alot!
This is the RemoteEvent listener script, the RemoteEvent gets fired whenever a player clicks buy. I watched some tutorials and videos - not the best scripter, is this secure enough?
Is “sword” a string? You should probably typecheck that because exploiters can pass the wrong data type and force an error.
You can use type() to get the main classes, and typeof() to include Roblox classes. I’ll just use typeof().
swordPurchaseEvent.OnServerEvent:Connect(function(player, sword)
if typeof(sword) ~= "string" or not swordPrices[sword] then return nil end
--rest of code goes here
end)
We don’t need to worry about the wrong data type for the second statement because if the first one returns false Lua doesn’t even bother checking the second statement.
Thanks alot, I’ll be sure to add that in. I also had another question regarding this.
When the player confirms the purchase, that’s when the RemoteEvent is fired. They confirm the purchase by pressing a button, the client-sided script checks the players leaderstats to see if they have enough and then fires the remote, if they don’t have enough it prints “Not enough”.
However, wouldn’t an exploiter just be able to change their leaderstats locally and then be able to purchase the sword? Do you have any recommendations on how to check the players server-sided leaderstats instead of client-sided which they can change, would I need to use another RemoteEvent? Thanks. A segment of the purchase script is below.
local function buyItem()
clickSound:Play()
script.Parent.Parent.Parent.Visible = false
local confirmationUI = player.PlayerGui["User Interface"].UserUI.SwordConfirmationUI
confirmationUI.SwordName.Text = script.Parent.Name
if confirmationUI then
confirmationUI.Visible = true
confirmationUI.GemsCost.Text = gemCost
confirmationUI.SwordImage.Image = script.Parent.ImageLabel.Image
local yesButton = confirmationUI:FindFirstChild("YesButton")
if yesButton then
yesButton.MouseButton1Click:Connect(function()
if player.leaderstats.Gems.Value >= gemCost then
swordPurchasedEvent:FireServer(gemCost, sword)
confirmationUI.Visible = false
equipButton.Visible = true
TweenUI()
game.ReplicatedStorage.GameSounds.PurchaseSound:Play()
else
print("Not enough gems!")
end
end)
end
You’re already checking the value on the server, so that’s fine! It’s the same with the StringValue. Any changes on the client stay on the client. As long as you aren’t reading the value from the client for the whole purchase, and checking that server-side, you should be fine.
I actually tried it out for myself, it seems like if the player has 0 gems (client or server) the purchase doesn’t go through, however I changed my Gems value on the client side and the script seemed to make the purchase go through, even though I only had enough Gems on the client side. Maybe I made some mistake, let me know if I did.
However, I added a RemoteFunction that checks if the player has enough Gems on the server side before going through, otherwise it prints not enough gems. This is what I adjusted the script too and it seems to be working fine now and still prints “Not enough gems” even if the player changes their leaderstats on the client side and only goes through if they have enough on the server.
LocalScript
local function buyItem()
clickSound:Play()
script.Parent.Parent.Parent.Visible = false
local confirmationUI = player.PlayerGui["User Interface"].UserUI.SwordConfirmationUI
confirmationUI.SwordName.Text = script.Parent.Name
if confirmationUI then
confirmationUI.Visible = true
confirmationUI.GemsCost.Text = gemCost
confirmationUI.SwordImage.Image = script.Parent.ImageLabel.Image
local yesButton = confirmationUI:FindFirstChild("YesButton")
if yesButton then
yesButton.MouseButton1Click:Connect(function()
local checkGems = game.ReplicatedStorage.Functions.CheckGems:InvokeServer(gemCost)
if checkGems then
swordPurchasedEvent:FireServer(gemCost, sword)
confirmationUI.Visible = false
equipButton.Visible = true
TweenUI()
game.ReplicatedStorage.GameSounds.PurchaseSound:Play()
else
print("Not enough gems")
end
end)
end
local noButton = confirmationUI:FindFirstChild("NoButton")
if noButton then
noButton.MouseButton1Click:Connect(function()
confirmationUI.Visible = false
clickSound:Play()
script.Parent.Parent.Parent.Visible = true
end)
end
end
end
Server-Side Script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local checkGemFunction = ReplicatedStorage.Functions.CheckGems
checkGemFunction.OnServerInvoke = function(player, gemCost)
if player.leaderstats.Gems.Value >= gemCost then
return true
else
return false
end
end
Yeah, it looks good, but you shouldn’t need a RemoteFunction. Are you sure you have nothing else that might edit values sent from the client? Because if checking on the server doesn’t work, checking on the client is very exploitable. Do you still have the if statement on the server?
I’m actually running into another issue now. The script that deducts the gems actually thinks that the sword name is the price now for some reason.
When I try to purchase Spartan Sword in-game, I added some debug and the script says “No sword found called: 500”, which is the price of the Spartan Sword. Any idea why this might be happening? This is the script;
Adding onto what you said, do you mind elaborating a bit? What do you mean by do I still have if statements on the server? If by “on the server” you mean the listener script that deducts the gems, I’ve pasted that above for the other issue so you can take a look at that too. Thanks alot.