Hello. I am a UGC creator and wanted to make a free item in my game where when a player has enough in- game currency, they can purchase an item.
this is how the script works.
local storage = game:GetService("ReplicatedStorage")
local re = storage.RemoteEvent
local players = game:GetService("Players")
local player = players.LocalPlayer or players.PlayerAdded:Wait()
local item = script.Parent
local value = 40000
local id = 14147323794 -- ID
item.MouseButton1Click:Connect(function()
if player.leaderstats.FairyDust.Value >= value then
re:FireServer(value)
game:GetService("MarketplaceService"):PromptPurchase(player, id)
value = 0
else
item.Text = "Insuffiecetn Funds"
task.wait(2)
item.Text = "Purchase"
end
end)
I was just wondering if there was a way to make this more secure? is it already secure enough? i dont want a bunch of exploiers getting the free item.
I was originally going to reply with updated security, but taking another look at this, it doesn’t seem like any form of security will really help in this case as exploiters can just call MarketplaceService:PromptPurchase() from their own scripts.
The only time you can add security to this kind of script is if the product in question is a Limited
It is a limited. I just want to make it more secure. Sadly the only way we can do it is with a purchase prompt ( the id isnt i was just testing it for now but i will change to limited later)
For starters, this won’t work because the PromptPurchase call for UGC must occur on the server. The reasoning? So exploiters can’t bypass the in-game tasks associated with winning the UGC item.
The correct way to award UGC items is using remotes and checking if the user has the requirements(and also awarding the item) on the server:
--Server(Script in ServerScriptService)
local MarketplaceService = game:GetService("MarketplaceService")
local remote = Instance.new("RemoteEvent", game.ReplicatedStorage)
remote.Name = "AwardUGC"
local price = 40000
local ugcID = 14147323794
--This function removes the price of the UGC from the user if the purchase is successful
MarketplaceService.PromptPurchaseFinished:Connect(function(player, id, isPurchased)
if not isPurchased or id ~= ugcID then return end
player.leaderstats.FairyDust.Value -= price
end)
remote.OnServerEvent:Connect(function(player)
--Why check on the server? So exploiters can't bypass it
if player.leaderstats.FairyDust.Value >= price then
MarketplaceService:PromptPurchase(player, ugcID)
else
--because they bypassed the client price check
warn(player.Name.." may be exploiting!")
end
end)
--Client(LocalScript inside some UI)
local remote: RemoteEvent = game.ReplicatedStorage:WaitForChild("AwardUGC")
local player = game.Players.LocalPlayer
local item: TextButton = script.Parent
local price = 40000 --make sure this is same for server as well
item.MouseButton1Click:Connect(function()
if player.leaderstats.FairyDust.Value >= price then
remote:FireServer()
else
item.Text = "Insufficient Funds"
task.wait(2)
item.Text = "Purchase"
end
end)
Roblox patched this case specifically for UGC limiteds:
“past exploits” here is just a more fancy way to say exploiters were running PromptPurchase for a limited on the client because there was no limitation.
local storage = game:GetService("ReplicatedStorage")
local re = storage.RemoteEvent
local players = game:GetService("Players")
local player = players.LocalPlayer or players.PlayerAdded:Wait()
local item = script.Parent
local value = 40000
local id = 14147323794 -- ID
item.MouseButton1Click:Connect(function()
re:FireServer()
end)
and in ServerScript:
local storage = game:GetService("ReplicatedStorage")
local re = storage.RemoteEvent
local players = game:GetService("Players")
local item = script.Parent
local value = 40000
local id = 14147323794 -- ID
re.OnServerEvent:Connect(function(player)
if player.leaderstats.FairyDust.Value >= value then
game:GetService("MarketplaceService"):PromptPurchase(player, id)
value = 0
else
item.Text = "Insuffiecetn Funds"
task.wait(2)
item.Text = "Purchase"
end
end)