Hello!
I have a pretty nifty shop purchase script, but I want to streamline it further to put more of the work on the Server as opposed to the Client/Local. I've created a few Remote Events that seem to be working fine, but I'm not sure I'm utilizing them properly for max efficiency. Essentially, my goal here is to make sure the shop purchase script works properly, and never over/double charges the player, or sends their Coins into the negative. I'd had that happen previously, randomly, when I was relying entirely on the Client/Local doing the function, and that is not ideal for the player experience.
Here are my Remote Events inside the Script under ServerScriptStorage:
--//Get Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--//EventFolder
local Events = ReplicatedStorage:WaitForChild("Events")
--//Remote Events
local CoinsPurchase = Events:WaitForChild("CoinsPurchase")
local ItemOwned = Events:WaitForChild("ItemOwned")
local CanAffordItem = Events:WaitForChild("CanAffordItem")
-- PURCHASE WITH COINS --
CoinsPurchase.OnServerEvent:Connect(function(Player, Value, Amount)
if Player.leaderstats.Coins.Value >= Amount then
print('Server says player has enough Coins for the purchase.')
Player.leaderstats.Coins.Value = Player.leaderstats.Coins.Value - Amount
print(Value)
Value.Value = true
else
print("Player does not have enough Coins for the purchase.")
end
end)
--CHECK IF ITEM OWNED --
ItemOwned.OnServerEvent:Connect(function(Player,Value)
if Value.Value == false then
print('Player does not own this item.')
else
print('Player does own item')
end
end)
--CHECK IF PLAYER CAN AFFORD ITEM --
CanAffordItem.OnServerEvent:Connect(function(Player,Value, Amount)
if Player.leaderstats.Coins.Value >= Amount then
print('Player CAN afford Item')
else
print('Player Can NOT afford Item')
end
end)
And this is a slightly abbreviated version of my shop purchase Local Script. When the Item01 button is clicked, it makes visible the BuyButton script and fills in the variables:
--//Get Services
local RS = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local Player = Players.LocalPlayer
local currencyCOINS = Player.leaderstats.Coins
--//EventFolder
local Events = RS:WaitForChild("Events")
--//Events
local ToolPurchase = Events:WaitForChild("ToolPurchase")
local CoinsPurchase = Events:WaitForChild("CoinsPurchase")
local ItemOwned = Events:WaitForChild("ItemOwned")
local CanAffordItem = Events:WaitForChild("CanAffordItem")
--//SHOP FRAME
local PurchaseFrame = Frame.PurchaseFrame
local BuyButton = PurchaseFrame.BuyButton
local ItemDesc = PurchaseFrame.ItemDesc
local Price = PurchaseFrame.Price
local ItemTrackName = RS:WaitForChild("ItemTrackNameShop")
local ItemPurchased = RS:WaitForChild("ItemPurchased")
local ItemPriceAmount = RS:WaitForChild("ItemPriceAmount")
BuyButton.MouseButton1Click:Connect(function()
MenuClickSound:Play()
local amount = Cost
local Value = ItemPurchased.Value
ItemDesc.Text = 'Purchase '..ItemTrackName.Value..'?'
Price.Text = ItemPriceAmount.Value
ItemOwned:FireServer(Player.Inventory[Value]) -- Checks if the Item is owned.
CanAffordItem:FireServer(Player.Inventory[Value], amount) -- Checks if the Player can afford the Item.
if Player.Inventory[Value].Value == true then
ItemDesc.Text = 'You have already purchased this Item!'
wait(3)
else
if Player.leaderstats.Coins.Value >= amount then
CoinsPurchase:FireServer(Player.Inventory[Value], amount)
wait(1)
PurchaseClickSound:Play()
ItemDesc.Text = 'Purchase Complete for '..ItemTrackName.Value..'! Equip this Item from your Inventory!'
wait(3)
PurchaseFrame.Visible = false
ItemDesc.Text = 'Purchase '..ItemTrackName.Value..'?'
wait(1)
else
local coinsrequired = amount - currencyCOINS.Value
ItemDesc.Text = 'You need '.. coinsrequired ..' more Coins to buy this item!'
wait(2)
ItemDesc.Text = 'Purchase '..ItemTrackName.Value..'?'
wait(5)
PurchaseFrame.Visible = false
end
end
end)--Closes Purchase Frame
Item01.MouseButton1Click:Connect(function()
PurchaseFrame.Visible = true
ItemTrackName.Value = 'Item01'
ItemDesc.Text = 'Purchase Item01?'
ItemPurchased.Value = 'Item01'
Cost = 10
ItemPriceAmount.Value = Cost
end)
As you can see in the shop purchase script above, I first run the Remote Events to see if the Player already owns the item, and then if they can afford the item, before following it up with the local/client determining the same thing, and then the Remote Event fires that actually grants the Item, and deducts the amount/cost of the Item.
Am I using the Remote Events correctly here? Can I improve upon it further?
Thank you!

2 comments
One problem I noticed is the code gives the Client too much power. An example is the CanAffordItem event. Since it sends the required amount from the Client, it can be manipulated to send the required amount to 0, making everything free - yes there is a conditional prior, but even that can be changed to work around it. Solution: Store all amounts and make checks on Server
Thank you for your response and insight! Could you possibly provide some example code? I think I'm following you here, but I really want to make sure I get this process exact. :)