This is part of my script inside a sword, this will run every time a player dies. I was just wondering if calling the UserOwnsGamepassAsync every time a player dies will cause performance issues and if theres a better way to do it?
humanoid.Died:Connect(function()
deaths.Value += 1
local killer = humanoid:FindFirstChild("creator")
if killer and killer.Value then
if game:GetService("MarketplaceService"):UserOwnsGamePassAsync(killer.Value.UserId, 98681915) then
killer.Value.leaderstats.Kills.Value += 2
else
killer.Value.leaderstats.Kills.Value += 1
end
end
end)
There’s definitely a better way.
When the player joins, use UserOwnsGamepassAsync (in a pcall). If it fails, kick the player.
If they own the pass, make a BoolValue in their player object, or set an attribute, or something like that. Then, you should check for that attribute/value instead of calling UserOwnsGamepassAsync each time.
Thank you very much, that all makes sense, the only thing is i’m not very familiar with pcalls and I don’t know how I would script that. Could I not just use an if statement?
Yeah, but I’m pretty sure UserOwnsGamePassAsync can fail (nearly anything ending in Async can fail, it’s an indicator you need a pcall).
It can look something like this:
local mps = game:GetService("MarketplaceService")
local success, owned = pcall(mps.UserOwnsGamePassAsync, mps, player.UserId, PASS_ID_HERE)
if success and owned then
--they own the pass
player:SetAttribute("GamepassOwned", true)
elseif not success then
player:Kick("Failed to retrieve gamepass data. Please rejoin!")
else
--they don't own the pass
player:SetAttribute("GamepassOwned", false)
end
or:
local owned
local success, err = pcall(function()
owned = mps:UserOwnsGamePassAsync(player.UserId, PASS_ID_HERE)
end)
Since UserOwnsGamePassAsync caches the result it returns and the script is a child of the sword tool, I think storing the value returned by the method in a constant variable at the top of the script, then checking the value in the if statement, should be enough to get the desired result without having to call it more than once
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local gamepassOwned do
local success, result = pcall(
MarketplaceService.UserOwnsGamePassAsync,
MarketplaceService,
Players.LocalPlayer.UserId, -- or: killer.Value.UserId
98681915
)
if success then
gamepassOwned = result
end
end
Server script example:
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local tool = script.Parent
local gamepassOwned = false
tool.Equipped:Once(function()
local success, result = pcall(
MarketplaceService.UserOwnsGamePassAsync,
MarketplaceService,
Players:GetPlayerFromCharacter(tool.Parent).UserId,
98681915
)
if success then
gamepassOwned = result
end
end)
Store the info on the gamepass as soon as they join, and only update that info after a gamepasspurchase happens. You don’t need to check every time they die because its not changing unless a gamepasspurchase happens, which you can code as an event.
The problem with that is the script is not running for the killer, it is running for the victim. That would be less efficient, because you would have to check for every single player when they join whether they own the gamepass, and store it as a variable and compare to it on humanoid death.
Why is the script responsible for awarding the killer points is running on the victim’s side, wouldn’t it be better to have it work on the killer’s sword?
I just use the basic roblox sword script. When a player dies to a sword it applies a tag to the dead player, which gives the player that killed them. I then just search for this tag when a player dies, and add leaderstats to the killer
You can keep the tag if you prefer, so that you can use it as a way to be able to inform the victim which player killed them in a Gui
@rafferty05 Something like this should work to achieve what you’re looking for (It needs to be a server Script that’s inside the sword tool):
Original code
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")
local tool = script.Parent
local animation = -- The sword animation
local handle = tool.Handle
local alreadyHit = {}
local debounce = false
local overlapParams
local player
local track
tool.Equipped:Once(function()
local character = tool.Parent
overlapParams = OverlapParams.new() -- Filters out the player themselves
overlapParams.FilterDescendantsInstances = {character}
overlapParams.FilterType = Enum.RaycastFilterType.Exclude
player = Players:GetPlayerFromCharacter(character)
track = character.Humanoid.Animator:LoadAnimation(animation) -- Loads the sword animation
end)
tool.Activated:Connect(function()
if debounce then return end
debounce = true
track:Play()
while true do
if track.IsPlaying then
for _, basePart in Workspace:GetPartsInPart(handle, overlapParams) do
local model = basePart:FindFirstAncestorOfClass("Model")
if model and not alreadyHit[model] then
local humanoid = model:FindFirstChildWhichIsA("Humanoid")
if humanoid then
alreadyHit[model] = true -- This is a way to limit damage taken by other players until the swords' debounce is over
humanoid:TakeDamage(20)
if humanoid.Health <= 0 then
humanoid:AddTag(player.UserId)
end
end
end
end
else
break
end
task.wait()
end
-- task.wait(2) (Optional extra wait time)
table.clear(alreadyHit)
debounce = false
end)
(RunService could be safely left out since I didn’t end up using it in this case)
@rafferty05 I modified the script to include the gamepass, changed the tag into an attribute, and added how to update the leaderstats:
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local tool = script.Parent
local animation = -- The sword animation
local handle = tool.Handle
local alreadyHit = {}
local debounce = false
local gamepassOwned = false
local overlapParams
local player
local track
tool.Equipped:Once(function()
local character = tool.Parent
overlapParams = OverlapParams.new() -- Filters out the player themselves
overlapParams.FilterDescendantsInstances = {character}
overlapParams.FilterType = Enum.RaycastFilterType.Exclude
player = Players:GetPlayerFromCharacter(character)
track = character.Humanoid.Animator:LoadAnimation(animation) -- Loads the sword animation
local success, result = pcall(
MarketplaceService.UserOwnsGamePassAsync,
MarketplaceService,
player.UserId,
98681915
)
if success then
gamepassOwned = result
end
end)
tool.Activated:Connect(function()
if debounce then return end
debounce = true
track:Play()
while true do
if track.IsPlaying then
for _, basePart in Workspace:GetPartsInPart(handle, overlapParams) do
local model = basePart:FindFirstAncestorOfClass("Model")
if model and not alreadyHit[model] then
local humanoid = model:FindFirstChildWhichIsA("Humanoid")
if humanoid then
alreadyHit[model] = true -- This is a way to limit damage taken by other players until the swords' debounce is over
humanoid:TakeDamage(20)
if humanoid.Health <= 0 then
Players:GetPlayerFromCharacter(model):SetAttribute("Killer", player)
if gamepassOwned then
player.leaderstats.Kills.Value += 2
else
player.leaderstats.Kills.Value += 1
end
end
end
end
end
else
break
end
task.wait()
end
-- task.wait(2) (Optional extra wait time)
table.clear(alreadyHit)
debounce = false
end)
You should also add marketplaceService.PromptGamePassPurchaseFinished:Connect() and update the player’s Owned attribute, because if they’ve bought it they’ll have to rejoin to claim it.
You can take advantage of MarketplaceService events to avoid making multiple calls. Here’s an example generalized for all your game gamepasses:
--Gamepass handler inside ServerScriptService
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
--your game gamepasses
local gamepasses = {}
--retry logic
local function gamepassOwned(userId: number, gamepassId: number): boolean
local success, response = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(userId, gamepassId)
end)
if success then return response end
warn(response)
task.wait(1)
return gamepassOwned(userId, gamepassId)
end
local function addGamepass(player: Player, gamepass: number): ()
local current = player:GetAttribute("gamepasses")
if not current then
player:SetAttribute("gamepasses", tostring(gamepass))
return
else
if current:find(tostring(gamepass)) then return end
player:SetAttribute("gamepasses", current..","..gamepass)
end
end
Players.PlayerAdded:Connect(function(player)
for _, gamepass in pairs(gamepasses) do
if not gamepassOwned(player.UserId, gamepass) then continue end
addGamepass(player, gamepass)
end
if not player:GetAttribute("gamepasses") then
player:SetAttribute("gamepasses", "")
end
player:SetAttribute("gpLoaded", true)
end)
MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(plr, gamepass, success)
if success then addGamepass(plr, gamepass) end
end)
Your new optimized code:
local function hasPass(plr: Player, gamepass: number): boolean
--Waiting for gamepasses to finish loading, if they haven't
while not plr:GetAttribute("gpLoaded") do
task.wait(0.5)
end
return not not plr:GetAttribute("gamepasses"):find(tostring(gamepass))
end
humanoid.Died:Connect(function()
deaths.Value += 1
local killer = humanoid:FindFirstChild("creator")
if not killer or not killer.Value then return end
local owned = hasPass(killer.Value, 98681915)
killer.Value.leaderstats.Kills.Value += owned and 2 or 1
end)
Just set it on joining, or like @Sweetbunny12475 said, after PromptGamePassPurchaseFinished fires.
An example:
local mps = game:GetService("MarketplaceService")
local players = game:GetService("Players")
local pass = 00000000 --pass id here
local function addPassBenefits(player:Player, owns:boolean)
player:SetAttribute("OwnsGamepass", owns)
end
local function checkOnPrompt(player, product, purchased)
if purchased and product == pass then
addPassBenefits(player, true)
end
end
local function checkForPass(player:Player)
local success, owned = pcall(mps.UserOwnsGamePassAsync, mps, player.UserId, pass)
if success and owned then
addPassBenefits(player, true)
elseif success and not owned then
addPassBenefits(player, false)
else
player:Kick("Failed to retrieve gamepass data. Please rejoin!")
end
end
players.PlayerAdded:Connect(checkForPass)
mps.PromptGamePassPurchaseFinished:Connect(checkOnPrompt)
Since we are assigning the attribute to the player, we can just use GetAttribute.
--Can't use "if player:GetAttribute('OwnsGamepass')" on its own because that would just check if the attribute exists.
if player:GetAttribute("OwnsGamepass") == true then
--they own it :D
end