Hi, I have a game where people can buy stuff with developer products. This is my script for it:
local MarketPlaceService = game:GetService("MarketplaceService")
MarketPlaceService.ProcessReceipt = function(receipt)
local Player = game.Players:GetPlayerByUserId(receipt.PlayerId)
local ProductId = receipt.ProductId
for _, temppowerup in pairs(game.ReplicatedStorage.TempPowerUps:GetChildren()) do
if temppowerup.Id.Value == ProductId then
TempPowerUp = temppowerup.Name
end
end
if TempPowerUp and Player.Character.Parent ~= nil and Player.Character.Humanoid ~= nil then
local TempPowerUp = Player.TempPowerUps:FindFirstChild(TempPowerUp)
print(TempPowerUp.Value)
TempPowerUp.Value = TempPowerUp .Value + 1
end
end
(Obviously a different script prompts the purchase)
My problem is a pretty weird one, basically lets say I buy a Temp powerup, and then I use it, when I buy another temppowerup, it gives me two, then the next time I use up my powerups and buy another one, it gives me three etc etc. It also prints more than I have, the first time I buy it it prints 0, like it should. Then the next time it prints 1, then the next time, after I’ve already used the powerup, it prints 2, even though I check in the explorer and it’s definitely 0. (I checked on the server btw)
Firstly, you shouldn’t be setting Functions as members of services, I actually didn’t even think that was possible. And you’re going to want to use something called DataStores, there’s some pretty good tutorials on the Wiki so I’ll direct you to them.
Basically, DataStores allow data to persist across play sessions, so you can store variables and recover them upon playing the game again.
But this isn’t across multiple play sessions, this was all in one session. I actually have a separate script that saves everything with DataStoreService.
Yes, but not directly. There’s a folder in my player called “TempPowerUps,” in it are a bunch of intvalues that are named each powerup. Ex: One of the intvalues is named “Invincibility,” and it’s value represents how much invincibility power-ups I have. There’s another folder in replicatedstorage also called TempPowerUps containing all the actual power-ups. I made it that it raises a player’s power-up’s value by 1 whenever the player buys that gamepass, and it decreases it by one when someone uses a power-up.
Firstly, this will not work as intended as soon as you start adding more Players to the mix. The best way around this is to transition all of those variables into local arrays.
Ex.
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
local PlayerData = {}
MarketplaceService.ProcessReceipt = function(receipt)
local Player = Players:GetPlayerByUserId(receipt.PlayerId)
local ProductId = receipt.ProductId
for name, value in next, PlayerData[receipt.PlayerId].PowerUps do
if value == ProductId then
if Player.Character and Player.Character:FindFirstChild("Humanoid") then
value = value + 1
print(value)
end
end
end
end
local function addPlayer(Player)
PlayerData[Player.UserId] = { -- Create a DataTable when a player joins
PowerUps = {
Invincibility = 0;
OtherPowerUp = 0;
};
Data0 = "String"; --Examples of other data being added
Data1 = 15;
}
end
Players.PlayerAdded:Connect(addPlayer)
for _,p in next,Players:GetPlayers() do
addPlayer(p)
end
Players.PlayerRemoving:Connect(function(Player)
PlayerData[Player.UserId] = nil
end)
This will make the variables more secure and protect them from exploiters, as well as creating a cleaner more refined system.
EDIT:
I added data-table removing when leaving to clean up data and prevent memory leaks.
For ProcessReceipt it is, this is how you’re supposed to use it. You define what ProcessReceipt is and Roblox will call that function internally, using the return value to determine if Roblox should queue the purchase for later or complete the transaction and send you the Robux.
This is the only case where this kind of thing can happen, IIRC. There might be other cases but they’re rather obscure and probably not used as often.
In any case: that bit I just mentioned is important. ProcessReceipt must return a ProductPurchaseDecision Enum, which all code samples so far have been missing. Check out documentation for reference on how to use this properly: