Quick question - how to fire remote event only when badge is awarded not when player already owns it?

Issue: Title says it all. I want the SE event to fire only when players get badge. Any help is appreciated!

local function awardBadge(player,badgeId,Text)
	local success1, badgeInfo = pcall(function()
		return BS:GetBadgeInfoAsync(badgeId)
	end)
	if success1 then
		if badgeInfo.IsEnabled then
			local awarded, errorMessage = pcall(function()
				BS:AwardBadge(player.UserId, badgeId)
			end)
			if not awarded then
				warn("Error while awarding badge:", errorMessage)
			else
				if Text then
					SE:FireAllClients(Text)
				end	
			end
		end
	else
		warn("Error while fetching badge info!")
	end
end
1 Like

You’ll need to use this.

UserHasBadge

Important note: You can only use this on the server

This is my code and it gives too many requests which result in server lagging. Just realized I am using hasbadgeasync. What difference does it make?

local function awardBadge(player,badgeId,Text)
	local hasBadge
	local success, err = pcall(function()
		hasBadge = BS:UserHasBadgeAsync(player.UserId, badgeId)
	end)

	if not success then
		warn("Error while checking if player has badge:",err)
		return
	end

	if hasBadge then 
		return
	end
	local success1, badgeInfo = pcall(function()
		return BS:GetBadgeInfoAsync(badgeId)
	end)
	if success1 then
		if badgeInfo.IsEnabled then
			local awarded, errorMessage = pcall(function()
				BS:AwardBadge(player.UserId, badgeId)
			end)
			if not awarded then
				warn("Error while awarding badge:", errorMessage)
			else
				if Text then
					SE:FireAllClients(Text)
				end	
			end
		end
	else
		warn("Error while fetching badge info!")
	end
end

You can try storing the people who owns badges in a cache array, so you can either check if the player is registered in the cache or the player has the pass using the roblox function.

1 Like

alright, that seems a good way but which way is better:

  • checking for badges using hasbadgeasync then saving it in a dict (not saving the dict to datastore)
  • using datastore to save the table

Don’t save the table in a datastore, it will just cause more troubles, also remove players from the cache table when they leave and lastly, UserHasBadgeAsync yields, that’s why you’re probably having apparent lag issues.
EDIT: The cache may not be needed if you manage to yield at the right moment.

Sorry, I was in a rush. Looks like the difference between the two is that UserHasBadge is depreciated… Sorry.

You had it right by using UserHasBadgeAsync.

You might be able to wrap the function a spawn function to remove the yeild aspect. Of course, this won’t decrease processing power needed to complete the task, so if the lag isn’t coming from the fact that it is a yeild, the spawn function won’t do a whole lot.

If the lag is coming from the processing power required, the cache will come in handy.

Here is my best estimate at what will help you:

local BS = game:GetService("BadgeService")
local PlayerHasPassCache = {}

local function awardBadge(player,badgeId,Text)
	spawn(function()
		local hasBadge
		if PlayerHasPassCache[player.UserId] then -- // If the cache says that the player has the badge
			hasBadge = PlayerHasPassCache[player.UserId]
		else -- // If we don't have data on the player, or the player doesn't have the badge and we know that
			local success, err = pcall(function()
				hasBadge = BS:UserHasBadgeAsync(player.UserId, badgeId)	
			end)
			if not success then
				warn("Error while checking if player has badge:",err)
				return
			end
			PlayerHasPassCache[player.UserId] = hasBadge -- // Add the player to the cache, even if they don't have the badge
		end
		if hasBadge then 
			return
		end
		local success1, badgeInfo = pcall(function()
			return BS:GetBadgeInfoAsync(badgeId)
		end)
		if success1 then
			if badgeInfo.IsEnabled then
				local awarded, errorMessage = pcall(function()
					BS:AwardBadge(player.UserId, badgeId)
				end)
				if not awarded then
					warn("Error while awarding badge:", errorMessage)
				else
					if Text then
						SE:FireAllClients(Text)
					end	
				end
			end
		else
			warn("Error while fetching badge info!")
		end
	end)
end

I’m curious as to why @jcnruad900 mentioned that you should remove the data from the array when the player leaves. Wouldn’t that just make us have to call the function if the player rejoined to the same server?

I think that if you keep the data in the array and the player has the pass, it’ll save you some processing power later on if that player rejoins. Please correct me if I am wrong.

It actually doesnt need to be as complicated as it seems.

Simply use UserHasBadgeAsync and fire the event when the player initially gets the badge rewarded to them.


Example Logic:

1.) Check if the player owns the badge

a.) if they dont own it, proceed to step 2
b.) if they do own it, stop here.

2.) Award the badge and fire the event

I’m actually gonna check it through a table second time. So user has badge async runs only once for one badge per player.

1 Like

Let’s say a player rejoins the same server, if they’re already registered in the cache with the non-ownership of the badge and in the meanwhile the player got the badge the script would not work for him even if he has got the badge.

Ah, I think I understand. My code would have worked in that case because I continued to use UserHasBadgeAsync if the value in the cached value false, but not if the cached value was true. I had not thought to add the false value in and use that per server instead of relying on UserHasBadgeAsync more than once.

Now, with a better understanding, here is the optimal code you are looking for @lagnis7859

local SE = game:GetService("ReplicatedStorage").RemoteEvent -- // Your remote event

local BS = game:GetService("BadgeService") -- // BadgeService
local PS = game:GetService("Players")
local PlayerHasPassCache = {} -- // Cache

local function awardBadge(player,badgeId,Text)
	spawn(function()
		local hasBadge
		if PlayerHasPassCache[player.UserId] ~= nil then -- // If we have previously used UserHasBadgeAsync for this player in this server, we take that value
			hasBadge = PlayerHasPassCache[player.UserId]
		else -- // Only if we don't have data on the player
			local success, err = pcall(function()
				hasBadge = BS:UserHasBadgeAsync(player.UserId, badgeId)	
			end)
			if not success then
				warn("Error while checking if player has badge:",err)
				return
			end
			PlayerHasPassCache[player.UserId] = hasBadge -- // Add the player to the cache, even if they don't have the badge
		end
		if hasBadge then 
			return
		end
		--[[ I was reading this over, and it looks like this is all just for another check if the player has the badge?
          We already do that above, so we don't need this. Please correct me if I am wrong]]
		
		--local success1, badgeInfo = pcall(function()
		--	return BS:GetBadgeInfoAsync(badgeId)
		--end)
		--if success1 then
		--	if badgeInfo.IsEnabled then
		--		local awarded, errorMessage = pcall(function()
		--			BS:AwardBadge(player.UserId, badgeId)
		--		end)
		--		if not awarded then
		--			warn("Error while awarding badge:", errorMessage)
		--		else
		--			if Text then
		--				SE:FireAllClients(Text)
		--			end	
		--		end
		--	end
		--else
		--	warn("Error while fetching badge info!")
		--end
		local awarded, errorMessage = pcall(function()
			BS:AwardBadge(player.UserId, badgeId)
		end)
		if not awarded then
			warn("Error while awarding badge:", errorMessage)
		elseif Text then
			SE:FireAllClients(Text)
		end
	end)
end

PS.PlayerRemoving:Connect(function(player)
	-- // Sets the value to nil so if the player joins another server and earns the badge, they can still function properly in this server
	PlayerHasPassCache[player.UserId] = nil
end)
2 Likes

Great idea for reducing lag, caching is really important when you’re doing something like this;

Hey, thanks for helping me out. I’m going to test a bit then mark it as solution. It did work but I modified it a bit to my needs.

1 Like

Awesome, I’m glad it worked :happy2:

1 Like