Im making a shop for my drawing game but my problem is, when the user buy’s it. It reduced a token at they’re datastore. But what happen is, instead of reducing the value once, it reduced twice. But other problem is, it works with other’s even though its on a single script/ line.
The script i made:
Server Script
game.ReplicatedStorage.ShopBuy.OnServerEvent:Connect(function(player,Price,FaceColorName,ShopType)
print(tonumber(Price))
if player.Economy.Tokens.Value > tonumber(Price) then
player.Economy.Tokens.Value = player.Economy.Tokens.Value - tonumber(Price)
if ShopType == "Face" then
player.FaceInventory:FindFirstChild(FaceColorName).Value = true
elseif ShopType == "Color" then
player.ColorInventory:FindFirstChild(FaceColorName).Value = true
end
game.ReplicatedStorage.ShopBuy:FireClient(player,ShopType)
else return
end
end)
Local script
while task.wait(1) do
for _, p in pairs(script.Parent:GetChildren()) do
if p:IsA("Frame") then
if game.Players.LocalPlayer.FaceInventory:FindFirstChild(p.Name).Value then
p.Purchase.Text = "BOUGHT"
end
local function Purchase()
local Price = p.Purchase.Text
local FaceColorName = p.Name
local ShopType = "Face"
if tonumber(Price) ~= nil then
game.ReplicatedStorage.ShopBuy:FireServer(Price,FaceColorName,ShopType)
local function Return(ShopType)
if ShopType == "Face" then
script.ClickSound:Play()
p.Purchase.Text = "BOUGHT"
end
end
game.ReplicatedStorage.ShopBuy.OnClientEvent:Connect(Return)
end
end
p.Purchase.MouseButton1Click:Connect(Purchase)
end
end
end
i don’t know why i did this on the script, i don’t remember my explanation lol but i made it while task.wait(1) do and I’m using for do so it keeps changing buttons. So when the user clicked the selected button in for do it will run the script. Sorry I’m not good at explanation
local rs = game:GetService("ReplicatedStorage")
local shopBuy = rs.ShopBuy
shopBuy.OnServerEvent:Connect(function(player, Price, FaceColorName, ShopType)
local Price = tonumber(Price)
if Price then
if player.Economy.Tokens.Value > Price then
player.Economy.Tokens.Value -= Price
if ShopType == "Face" then
player.FaceInventory:FindFirstChild(FaceColorName).Value = true
elseif ShopType == "Color" then
player.ColorInventory:FindFirstChild(FaceColorName).Value = true
end
else
return
end
end
end)
Server script looks fine, added some slight changes.
local rs = game:GetService("ReplicatedStorage")
local shopBuy = rs.ShopBuy
local player = game.Players.LocalPlayer
local parent = script.Parent
local sound = script.ClickSound
for _, p in ipairs(parent:GetChildren()) do
if p:IsA("Frame") then
if player.FaceInventory:FindFirstChild(p.Name).Value then
p.Purchase.Text = "BOUGHT"
end
p.Purchase.MouseButton1Click:Connect(function()
local Price = p.Purchase.Text
local FaceColorName = p.Name
local ShopType = "Face"
local Price = tonumber(Price)
if Price then
if player.Economy.Tokens.Value > Price then
shopBuy:FireServer(Price,FaceColorName,ShopType)
if ShopType == "Face" then
sound:Play()
p.Purchase.Text = "BOUGHT"
end
end
end
end)
end
end
Attempt at fixing the server script.
You don’t need the FireClient()/OnClientEvent bit.
Maybe the event is firing twice because of the detection to fire the event on the client. The best way to fix this is to use a debounce system. Here is an example.
local debounce = false
game.ReplicatedStorage.ShopBuy.OnServerEvent:Connect(function(player,Price,FaceColorName,ShopType)
print(tonumber(Price))
if player.Economy.Tokens.Value > tonumber(Price) and debounce == false then
debounce = true
player.Economy.Tokens.Value = player.Economy.Tokens.Value - tonumber(Price)
if ShopType == "Face" then
player.FaceInventory:FindFirstChild(FaceColorName).Value = true
elseif ShopType == "Color" then
player.ColorInventory:FindFirstChild(FaceColorName).Value = true
end
game.ReplicatedStorage.ShopBuy:FireClient(player,ShopType)
wait(1) --How long until you can press the button again.
debounce = false
else return
end
end)
I found out your script had a huge vulnerability where exploiters could buy stuff for free. I tried fixing everything I could and completely removed the while loop(now the script is signal based):
--Server Script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--script wont run until it finds the remote
local ShopBuy = ReplicatedStorage:WaitForChild("ShopBuy", math.huge)
--always store item prices on the server so exploiters can't manipulate them
local Prices = {
["Faces"] = {
["Item1"] = 10,
["Item2"] = 20
--etc.
}
}
function GetPrice(ShopType, itemName)
local category = Prices[ShopType]
if not category then return nil end
local item = category[itemName]
return item
end
ShopBuy.OnServerEvent:Connect(function(player, ShopType, itemName)
local Economy = player:FindFirstChild("Economy")
local Tokens = Economy:FindFirstChild("Tokens")
local itemPrice = GetPrice(ShopType, itemName)
if itemPrice and Tokens.Value >= itemPrice then
local Success, Error = pcall(function()
if ShopType == "Face" then
player.FaceInventory:FindFirstChild(itemName).Value = true
elseif ShopType == "Color" then
player.ColorInventory:FindFirstChild(itemName).Value = true
end
end)
if Success then
--only remove coins if the request is a success
Tokens.Value -= itemPrice
ShopBuy:FireClient(player, ShopType)
end
else
--if they passed the client check, without the amount of currency needed, we got em
player:Kick("Exploiting!")
end
end)
--Local script
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Player = Players.LocalPlayer
local inventory = Player:WaitForChild("FaceInventory")
local frames = script.Parent
function GetFrame(item)
local frame = frames:FindFirstChild(item.Name)
if frame and frame:IsA("Frame") then return frame end
end
function Purchase(frame)
local Price = tonumber(frame.Purchase.Text)
local FaceColorName = frame.Name
local ShopType = "Face"
local Economy = Player:FindFirstChild("Economy")
if not Economy then return end
local Tokens = Economy:FindFirstChild("Tokens")
if not Tokens then return end
--this is a sanity check, if a user fires the remote without enough currency, they are exploiting.
if Price and Tokens.Value >= Price then
ReplicatedStorage.ShopBuy:FireServer(ShopType, FaceColorName)
local connection
connection = ReplicatedStorage.ShopBuy.OnClientEvent:Connect(function()
script.ClickSound:Play()
frame.Purchase.Text = "BOUGHT"
--always disconnect signals which you want to use once.
connection:Disconnect()
end)
end
end
function stateChanged(frame, state)
if frame and state then
frame.Purchase.Text = "BOUGHT"
end
end
for i, face in pairs(inventory) do
local frame = GetFrame(face)
stateChanged(frame, face.Value)
face.Changed:Connect(function()
stateChanged(frame, face.Value)
end)
frame.Purchase.Activated:Connect(function()
Purchase(frame)
end)
end
Also this may break the script responsible for other types of items(if that’s the case you could try rewriting the other script or fusing it with this one).
This works but it is still reduced twice. My previous script was 50 > 10 but your script makes it 50 > 30. The price is only 10 tokens but it reduced 20
That’s the point. The debounce is on the server so once a player touches it, nobody in the server will be able to buy for a second, then debounce will be false and everything will work again.