Gear Gamepass Issues (Speed and Gravity coils)

Are you sure the coil can be given to you? (is ‘RequiresHandle’ on but the tool has no handle)

Can I see the location of everything necessary? All the trees of every instance location? Like screenshots of where the coil is located, and the scripts?

The code you’re running is not part of the event, put it inside the PlayerAdded function.

 local GamePassId = 19711172

 	game.Players.PlayerAdded:Connect(function(player)

		if MarketPlaceService:UserOwnsGamePassAsync(player.UserId, GamePassId) then

 			game.ServerStorage.Gravitycoil:Clone().Parent = player:WaitForChild("Backpack")
 			game.ServerStorage.Gravitycoil:Clone().Parent = player:WaitForChild("StarterGear")
 		end
 end)

‘RequiresHandle’ is on, and Gravity Coil has a handle.

Yeah one second, need to swap computers for ease of screenshotting.

Here are the screenshots and where they are located:

Screen Shot 2021-07-10 at 20.44.08 Screen Shot 2021-07-10 at 20.44.20 Screen Shot 2021-07-10 at 20.44.30

Game Pass buying button (Starter Gui, ‘Gravity’, Local Script.

Tools located in server storage (titled GravityCoil now, at the start of this thread was a lower c on Coil)

Game Pass Giver Script called ‘Script’ in ServerScriptService

I has to be

game:GetService(“ServerStorage”).GravityCoil:Clone().Parent = player:WaitForChild(“Backpack”)

Try changing the sections like that. Your code has Gravitycoil, but the actual name of it is GravityCoil

DID NOT READ YOUR POST CORRECTLY, Ignore this then lol

Try using this code. You place the tool you want to give inside the script, replace ‘gamepassid’ with your passid and replace script.tool with the tool name.

local MarketPlaceService = game:GetService("MarketplaceService")
 
local GamepassId = 1234567 -- REPLACE 1234567 WITH YOUR GAMEPASS ID
local Tool = script.GravityCoil -- REPLACE GravityCoil WITH THE NAME OF YOUR TOOL
 
game.Players.PlayerAdded:Connect(function(Player)
Player.CharacterAdded:Connect(function(Character)
    if MarketPlaceService:UserOwnsGamePassAsync(Player.UserId, GamepassId) then
        local ToolClone = Tool:Clone()
        ToolClone.Parent = Player.Backpack
    end
end)
end)
1 Like

Dukzae is right over here, the tool should be cloned into the backpack each time the player spawns.

Do not place your tools in ReplicatedStorage. The clients have access to this, and can clone it on their own to use.

I looked over your code, and noticed some spelling problems, case sensitivity problems, and efficiency problems that I can help with. One thing to note is “UserOwnsGamePassAsync” is an expensive function, and you really don’t want to be calling it each time the character spawns. That’s why you should only call it once when the player joins, and depending on whether they own it or not, clone the tool when they spawn.

-- Services and Variables
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
local ServerStorage = game:GetService("ServerStorage")
local GAMEPASS_ID = 19711172

-- A cache to index if a player owns the gamepass when they join
local cache = {}

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		-- Repeats until ownership is detected
		if cache[player] == nil then
			repeat 
				wait()
			until
			cache[player] ~= nil
		end
		
		-- If the player owns the gamepass, clone the gravity coil into their backpack
		local ownsGamepasses = cache[player]
		if ownsGamepasses then
			ServerStorage.GravityCoil:Clone().Parent = player:WaitForChild("Backpack")
		end
	end)
	
	-- This runs ONLY ONCE because UserOwnsGamePassAsync is an expensive function
	-- IT shouldn't be called every time the player spawns, so ownership will be indexed in the cache
	local success, hasPass = pcall(function()
		return MarketplaceService:UserOwnsGamePassAsync(player.UserId, GAMEPASS_ID)
	end)
	if success then
		cache[player] = hasPass
	else
		warn("Failed to retrieve ownership data: " .. player.Name) 
		cache[player] = false
	end
end)

I said this in the explanation below…thats not the problem Coil vs coil anymore.

No errors, yet tool is still not given after purchasing

It could be because the playerAdded function hasn’t been called on the current players, and the script is somehow delayed? I added some print statements into my code and initialized the players at the bottom. In the output, see if you can catch “YEET” or “NAH”, which determines whether the gamepass is owned.

-- Services and Variables
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
local ServerStorage = game:GetService("ServerStorage")
local GAMEPASS_ID = 19711172

-- A cache to index if a player owns the gamepass when they join
local cache = {}

local function playerAdded(player)
	player.CharacterAdded:Connect(function(character)
		-- Repeats until ownership is detected
		if cache[player] == nil then
			repeat 
				wait()
			until
			cache[player] ~= nil
		end

		-- If the player owns the gamepass, clone the gravity coil into their backpack
		local ownsGamepasses = cache[player]
		if ownsGamepasses then
			print("YEET")
			ServerStorage.GravityCoil:Clone().Parent = player:WaitForChild("Backpack")
		else
			print("NAH")
		end
	end)
	
	-- This runs ONLY ONCE because UserOwnsGamePassAsync is an expensive function
	-- IT shouldn't be called every time the player spawns, so ownership will be indexed in the cache
	local success, hasPass = pcall(function()
		return MarketplaceService:UserOwnsGamePassAsync(player.UserId, GAMEPASS_ID)
	end)
	if success then
		cache[player] = hasPass
	else
		warn("Failed to retrieve ownership data: " .. player.Name) 
		cache[player] = false
	end
end

-- Initialize Players
for _, player in ipairs(Players:GetPlayers()) do
	coroutine.wrap(playerAdded)(player)
end
Players.PlayerAdded:Connect(playerAdded)

If you see “YEET” and the tool still isn’t in the backpack, then something is wrong with the cloning. BUT, if you see “NAH”, then you don’t own the gamepass!

Let me know if you have any questions, I’m going to head off for now but I’ll check in

This is not an expensive function since the result is cached.

1 Like

Ahhhh thank you for that useful info! I should rewrite some of my code now considering I’ve had a custom caching system for it :skull: much appreciated!

I’ll rewrite the code above soon :slight_smile:

1 Like

The result is only cached on the server (at least what it seem, based on that other functions that have this behaviour) however therefore calls on the client are expensive.

3 Likes

You can read more into this in caching behavior here: MarketplaceService | Roblox Creator Documentation

1 Like

Ah yes, i was just on that page haha! I remember now why I had a custom caching system, it was because of the PromptGamePassPurchase and the Finished event, since the caching system of this async doesn’t handle that. But since this code doesn’t handle in-game purchases and receiving the tools in real-time, using the async on each spawn should be durable :joy:

1 Like

It’s interesting why the default Roblox system doesn’t account for this, it seems like something that should have been included especially the fact that PlayerOwnsAsset doesn’t have caching behaviour. Am I the only one that wants consistency in these functions?

1 Like

FOR REAL, I really hope they make this a feature soon. I might as well create an open-sourced module that handles the caching for easy use of other developers :face_with_head_bandage:

ANYWAYS @Hattasy

Here is your refined code:

-- Services and Variables
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
local ServerStorage = game:GetService("ServerStorage")

-- PassName and ID
local passes = {
	GravityCoil = 19711172,
        -- ADD MORE HERE IF YOU WANT
}

local function playerAdded(player)
	-- Function that returns true or false, depending on ownership of pass
	local function ownsPass(name)
		return MarketplaceService:UserOwnsGamePassAsync(player.UserId, passes[name])
	end
	
	player.CharacterAdded:Connect(function(character)
		-- Check if player owns "GravityCoil"
		if ownsPass("GravityCoil") then
			ServerStorage.GravityCoil:Clone().Parent = player:WaitForChild("Backpack")
		end
	end)
end

-- Initialize Players
for _, player in ipairs(Players:GetPlayers()) do
	coroutine.wrap(playerAdded)(player)
end
Players.PlayerAdded:Connect(playerAdded)

I created a passes table where you can index the pass name and the ID. There’s a function that checks whether or not the player owns the pass, “ownsPass.” You add add more gamepasses into the table if needed and create your own events :slight_smile: