Loading massive amounts of badges in seconds

Hello Developers :slight_smile:.
I’ve come to a situation in which I need to load 120 badges to a GUI in seconds when players join.
I keep getting the TooManyRequests error.
When I get that error, I wait around 15 seconds to let the service refresh and then continue.

It all works well but sums up to about a minute of loading time… Which is a lot.

I’ve seen games that load dozens of badges and data in seconds, how do they do that?

Does anyone know how to decrease the loading time? Load so many badges quickly?

Thanks in advance.

2 Likes

The script that you are looking for should be statically written.
Which means the code for the badges is hard coded.

I like the idea of dynamic loading etc, however the Roblox API itself has its limitations to prevent people from flooding the servers.

1 Like

You could possibly get around this request limit by using HttpService and a proxy. The requests need to be proxyed to the badge api (api documentation: badges.roblox.com/docs). Note this has a limit of 500 requests a minute and requires http service to be enabled.

1 Like

Thanks! I’ll try that.
I am worried though that when multiple players join it might break.

Of course, but 120 badges although seems like a lot, there are many games that load massive amounts of items/badges very quickly.

What do you mean by that?

you should just save all the badges into a table that saves and only update when needed.

2 Likes

Thank you all for answering :slightly_smiling_face:.

I will try all these when I get home, and I’ll mark the solution/ask more if I don’t understand.

What do you mean by that?

What @AC_Starmarine said :slight_smile:

local badge1 = 65465416 -- some ID, I just smashed my keyboard
local badge2 = 56465456 -- some ID, I just smashed my keyboard

Is what I mean with static, hard coded.
On addition, AC Starmarine means something like this (which is my preferable way too)

local badges = {
    badge1 = 65465416,
    badge2 = 56465456
}

If your approach is the table way you can iterate over each key value pair. This way you could write a function which do stuff for your badges. (I dunno, i havent worked with badges yet) But an example would be like

local BadgeService = game:GetService("BadgeService");
local badges = {
    badge1 = 65465416,
    badge2 = 56465456
}

local funtion doSomethingWithBadge(badgeName, badgeId)
    local success, badgeInfo = pcall(function()
        return BadgeService:GetBadgeInfoAsync(badgeId);
    end)
    if success then return badgeInfo; end;
    return nil;
end)
-- Make iteration happen
for badgeName, badgeId in pairs(badges) do
    local badge = doSomethingWithBade(badgeName, badgeId);
    if badge ~= nil then
        -- do stuff with the badge info
        print(badge);
    end
end

Anyway, this is code which is not tested, further the less this should work

3 Likes

That’s pretty much what I am doing. And it works, bit only with
:GetBadgeInfoAsync()

When I try to use :UserHasBadgeAsync() then after some badges the service just fails and atleast 15 seconds are needed until you can use it again.

I will try to use the HTTPService solution, but I am afraid it’ll fail with many people.

1 Like

You can do that, otherwise check out this part of the documentation.

In the example provided in the documentation you should do it like this

local BadgeService = game:GetService("BadgeService")
local Players = game:GetService("Players")
local badgeID = 00000000 -- Change this to your badge ID
local function onPlayerAdded(player)
    -- Check if the player has the badge
    local success, hasBadge = pcall(function()
        return BadgeService:UserHasBadgeAsync(player.UserId, badgeID)
    end)
    -- If there's an error, issue a warning and exit the function
    if not success then
        warn("Error while checking if player has badge!")
        return
    end
    if hasBadge then
        -- Handle player's badge ownership as needed
    end
end
-- Connect "PlayerAdded" events to the "onPlayerAdded()" function
Players.PlayerAdded:Connect(onPlayerAdded)

If this doesn’t work for you, you probably didn’t explain the whole picture, show some code, show some of your own research. Are there any other scripts running which you might have forgot about? Have you imported some kind of model with a nasty script in it?, at this point its a wild guess for me, I hope you figure it out. Cheers :beers:

Sorry for the long response time.

Nope.

I do not think that is the issue, the service is failing because of too many :UserHasBadgeAsync Requests.

Yes, I did use a pcall, and I used the service from the client to check the local player’s badges.

I think here’s my new plan:

Use the service like I am currently doing, and if it fails, I’ll start using HTTPService, and replace them every time each one fails.

What you can also do is check for all badges when they join the first time, save all badges that they own in a datastore and when rejoining, only fetch the badgedata of the badges they doesn’t own yet. Then you will have much less calls to the badgeservice

1 Like

Are you sure that’s an ideal solution?

DataStoreService is also a service that can fail from time to time, especially with lots of data stored, and players can delete their own badges, which will cause inaccurate results.

This will also require server intervention. Right now I only use the client for this.

Isn’t there a risk for a 120 slots/indexes long dictionary to eventually exceed the datastore size limit?

You will never exceed the datastore limit, you can just make a row with key userid and value table of badgeid’s (for example {1,2,7,9}). Even with 1000 badges with length 8 you will never pass the limit.
Badge deleting is a thing, but since the player did achieve it, it doesn’t really matter if he owns it now or not.
What you are claiming about datastoreservive failing with lots of data stored is false. The amount of data stored doesn’t matter, and if datastoreservice is down all games are affected by it.
Next to that, badgeservice can also fail.
Question: why do you need to check 120 badges in the first place? Are those badges from different places, or are they from the game you are checking from?

To answer your questions:


Badges can be deleted from the inventory. Results should be accurate.
You will never exceed the datastore limit

Since I also need the badge description it could be problematic, also, the larger the data the more time it takes to load it. Although that might not be a big difference, not sure…

A GUI that lists all badges that the player has in the game. Badge hunting game.


Thanks for the help :slight_smile:.

1 Like

Don’t worry lol

I was thinking about this and reading through the other solutions people provided while working on my own game. You see. My new game is going to have a lot of generated stuff going on. The server needs to build my world for every new server. Sometimes this might take some time, but I don’t want the player able to play because the world is not loaded in yet.

Kinda similair story with your badges, you need your badges in order to play your game.
I know this sounds crazy, but a few things popped up in mind.

  1. loading screen
    * Yes, I know,… This doesnt cut down your loading time,
  2. play with coroutines

The first one is obvious, the second one however, (No clue if this is going to work, aka not tested)
Look at the orignal code and the code that i added:
ORIGINAL

NEW

local BadgeService = game:GetService("BadgeService");
local badges = {
	badge1 = 65465416,
	badge2 = 56465456
}

local funtion doSomethingWithBadge(badgeName, badgeId)
	local success, badgeInfo = pcall(function()
		return BadgeService:GetBadgeInfoAsync(badgeId);
	end)
	if success then return badgeInfo; end;
	return nil;
end)
-- Make iteration happen
for badgeName, badgeId in pairs(badges) do
	coroutine.resume(coroutine.create(
		function()
			local badge = doSomethingWithBade(badgeName, badgeId);
			if badge ~= nil then
				-- do stuff with the badge info
				print(badge);
			end
		end
	))

end

I dunno if this solves the API limitation or if it makes it worse. Curious tho.
This might cutdown your loading time

2 Likes

I thought of dividing it into threads, but all this time I thought it fails because requests are sent too quickly.

It’ll work if the service is based on Request Count and not Request Speed. So it might reduce the time. That’s actually brilliant, I will try it :slight_smile:, Thanks!

There’s already a 20 second loading screen :stuck_out_tongue:, so that ain’t a very good solution.

I actually do this from the client, the service works with LocalPlayer.
It’s also brilliant to try it from the server. Perhaps there’s a limit on the Client to prevent Exploiter from Slowing down the service.

Thanks for the help! I’ll try that and let you know if it worked.

1 Like

I felt like posting an update, as this is something that many games might need.
Two solutions that were ideal and worked:

  1. Offered by @OofUndefined :
    He offered to efficiently split the badges into many threads. It speeds up the process as UserHasBadgeAsync yields .
    It worked very well :slightly_smiling_face:, took around 40 seconds,but it was ideal and a good solution :slight_smile:.

  2. Partially suggested by @Abcreator, To use HttpService, with the Roblox Badges API:
    This solution was actually brilliant. I’ve managed to get 70 Badges from1 Http request from this part of the API:


    I got all badges from the game that were awarded to the player, and in no more than 20 seconds, I’ve received all badges that the player has, with only 2 Http Requests Per player.

Thank you so much, Abcreator and OofUndefined.

Both of your solutions work well :slightly_smiling_face:
image
(120 badges in the Badge GUI, completely loaded, sometimes even before the loading screen finishes).

3 Likes

Thank you for your well-worded explanation. It is very clear and helpful. I hope that other users may find it of value.

1 Like