SafeBadges - An open-source, reliable, safe, and precise way to award badges

SafeBadges

An open-source, reliable, safe, and precise way to award badges

Introduction

SafeBadges is an open-source Roblox module designed to simplify and enhance the process of awarding badges within your game (… or experience). With SafeBadges, you can award badges to players with reliability, safety, and precision. :herb:

Key Features

“Well, what do you mean by reliable, safe and precise?”

  • Reliable: SafeBadges handles the process of awarding badges efficiently and retries automatically if something goes wrong, so you don’t have to worry about any potential failed results.
  • Safe: It includes checks to prevent issues like awarding the same badge multiple times unnecessarily and manages retries to avoid infinite loops.
  • Precise: With clear error handling and retry logic, SafeBadges ensures that badges are awarded correctly and provides useful logs to help with troubleshooting.

Why use SafeBadges?

SafeBadges is perfect for developers who want an easy way to manage badges. It offers:

  • Automatic Retries: If awarding a badge fails, SafeBadges will retry automatically, using a backoff strategy to handle temporary problems smoothly.
  • Detailed Errors: When something goes wrong, SafeBadges gives detailed error logs, so you can quickly figure out what happened and fix it.
  • Easy Integration: Adding SafeBadges to your game is straightforward, letting you focus on creating other features instead of having to worry about badges.

Whether you’re rewarding players for achievements, in-game events, or any other reason—which I can’t possibly list here, as that list would go on forever—SafeBadges ensures they receive their badges reliably and with minimal effort on your end.

Installation

To start using SafeBadges, just add the module to your game, and require it from a server script!

There are 4 settings you can edit in the module to your liking: ATTEMPT_LIMIT: number, RETRY_DELAY: number, BACKOFF_FACTOR: number, and DEBUG_PRINTS: boolean.

Get the module here 👇

Example Usage 👇

-- Variables
local Players = game:GetService("Players")
local ServerScriptService = game:GetService("ServerScriptService")
local ServerModules: Folder = ServerScriptService.ServerModules

-- Require the module
local SafeBadges: ModuleScript = require(ServerModules.SafeBadges)

-- A little list of badge IDs so we have a variety
local badgeIds = {1234567890, 0987654321, 1048249946058447} :: {number}
-- Welcome badge:
Players.PlayerAdded:Connect(function(player: Player)
	SafeBadges.AwardBadges(player, badgeIds)
end)
-- Check badge ownership:
Players.PlayerAdded:Connect(function(player: Player)
	local OwnedBadges = SafeBadges.HasBadges(player, badgeIds)
	return print(OwnedBadges)
	-- returns (and prints) an array of owned badges
end)
-- Change a fast flag:
SafeBadges.SetFastFlag("DEBUG_PRINTS", false)

Have any suggestions/bugs/issues?

You can reach out to me via the DevForum, and I’ll try my best to respond as fast as I can!

40 Likes

very cool, plus the troubleshooting logic will be very good for a project I am making

1 Like

Overall great module but I’m just a little concerned that you added a type to each of those 4 variables. :fearful:

2 Likes

What’s wrong with type checking?

4 Likes

Nothing wrong with it but I’m just saying there wasn’t really a need to add types to those 4 variables.

1 Like

Crap ton of untoggable useless prints, no retrying and caching for checking if user owns badge, not very necessary type checking and instead of returning a table with the function just return the function. Retrying also should be default behaviour

Consider doing this as its somewhat more efficient and readable

return SafeAwardBadge
local awardBadge = require(path.to.module)
awardBadge(player, badge)
4 Likes

Hi, thank you for the feedback! I’ll release a update later that covers these points.

1 Like

Honestly you can just do

return function(plr: Player, badgeId: number) -- SafeAwardBadges but not a named function

if you really care about cutting down on the length

2 Likes

August 18th, 2024

  • Added new setting for console logs: DEBUG_PRINTS: boolean
  • Added retries and caching for checking badge ownership (also made retrying in general automatic)
  • Reduced usage of types
  • Changed up Example Usage code

Please re-add SafeBadges to your experience for most up-to-date code.

1 Like

yippe code review time

local LOG_LEVELS = {
	INFO = "INFO",
	WARN = "WARN",
	ERROR = "ERROR",
}

local function log(level: string, message: string)
	if DEBUG_PRINTS then
		if level == LOG_LEVELS.INFO then
			print(message)
		elseif level == LOG_LEVELS.WARN then
			warn(message)
		elseif level == LOG_LEVELS.ERROR then
			error(message, 2)
		end
	end
end

thats alot of work innit?

local function log(f:(string)->nil, message:string)
if debug_enabled then f(message) end
end

log(print, "this is a print")
log(warn, "yellowww")
log(error, "AAAAAAAAAAAAAAAA")

I’d also by default put debug prints as game:GetService("RunService"):IsStudio()

You could also put a if game:GetService("RunService"):IsClient() then return error("r yuu stupi m8, this is a server sided module") end

Coudl also just return the SafeAwardBadge function without asigning it a variable

Okay thats it!!!

1 Like

Good documentation includes types even when they’re obvious. Kudos to the OP for doing things right, defo makes me want to check out the module more

2 Likes

Hey, I regularly use roblox-ts, and would love to use this script in my TS project. To do so, I need to create a .d.ts for use with your code, which I can totally do. However, I would also love to share it with people so they can to, but to do that I would need your permission to upload your code to NPM. I’m totally cool if you don’t want me to, but please, let me know.

TLDR; are you ok with me uploading your code to NPM?

Suggestion: add functions like setAttemptLimit, setRetryDelay, etc. for changing of the config values at runtime.

1 Like

August 20th, 2024

  • Added support for awarding and checking multiple badges at once
    • Includes support for CheckUserBadgesAsync()
    • Currently restricted to a maximum of 10 badges per request due to limitations with CheckUserBadgesAsync()
  • Added new function for changing fast flags in runtime: SetFastFlag(FastFlag: string, value: any)
    • This function also comes with the change of fast flags now being stored in 1 table rather than as seperate variables
-- Example usage of the SetFastFlag() function
SetFastFlag("DEBUG_PRINTS", false)
  • Added ability to check for badge ownership without awarding badges
-- Example usage of the HasBadges() function
local SafeBadges: ModuleScript = require(path_to_module)
local badgeIds = {1234567890, 0987654321, 1048249946058447} :: {number}

local BadgesOwned = SafeBadges.HasBadges(player, badgeIds)
return print(BadgesOwned)
  • Simplified the log() function
-- Example usage of the log() function
log(print, "Hello world!")
  • SafeBadges module is now only able to be called by the server
  • Properly changed up Example Usage code

Please re-add SafeBadges to your experience for most up-to-date code.

2 Likes

genuinely don’t understand the use case of this, using the default API or making your own simple wrapper works fine for your own use case. I don’t understand why it is a “precise” or “reliable” way either.

4 Likes

Hint: it’s because I’m probably too lazy to make my own wrapper :astonished:

(dont take this too seriously)

What’s the point in this and what if you need to check badges a user owns on the client? (Unless im stupid and it’s not possible, but i’m quite sure you can use the userid of the localplayer to check badges on the client)

Probably better off to make it easier to check badges on the client (if you haven’t already) by automatically using localplayer’s id and having you just need to send the badge ids

If it should only be required on the server why not just tell users that so they store it in serverscriptservice instead of replicatedstorage?

I disagree, including the types improves the compiler speed, helps with auto complete, and improves assembly efficiency.

Saying there’s “no need” is absolutely pointless, and harmful. Do not promote this behavior.

3 Likes

Err not to be that dude but is there any proof of this? For my own code purposes)

theres currently no benefits of adding types in the luau compiler
most types are inferred anyway, and not sure what “assembly efficiency” is

1 Like

the point is you don’t need it either, its completely optional

1 Like