Feedback on an Accessory Banning System

This is a script that I have been working on that can delete any accessory on a character that is from a group/user on a “Ban List”, a module script that has a table of their respective ID’s. While I am happy that it works, I feel that there are some things that I wish could be better about it.

  1. I’ve noticed that sometimes Roblox’s Avatar Service/Editor can go down, and that Player:HasAppearanceLoaded() will not work when something like this occurs. In order to fix this, I’ve wrapped it in a pcall to help combat if something like this occurs. However, I ask: Is this something I should realistically be worried about?

  2. The code I have in place to get the names of the accessories on the character itself uses the InsertService to get their names. Is there a better way to get their names that doesn’t involve using InsertService to create a duplicate?

HatBanDemo.rbxl (55.9 KB)

– Main Script

local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
local ServerScriptService = game:GetService("ServerScriptService")
local InsertService = game:GetService("InsertService")

local BanList = require(ServerScriptService.BanList)
Players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:Connect(function(char)
		
		-- // Sometimes (rarely), Avatars can fail to load, or the avatar system can break. To ensure the script does not break when this occurs, wrap :HasAppearanceLoaded in a pcall.
		local iteration = 0
		local s
		while task.wait(0.1) do
			s = pcall(function()
				return plr:HasAppearanceLoaded()
			end)
			if s then
				break
			else if iteration > 60 and s == nil then
					-- // If pcall iterates 60 times (approx 6 seconds) with no result, then the avatar service must be broken and the script does not have to be ran.
					break
				else
					-- // Add one to the i variable. If it overflows 61, the loop is broken.
					iteration += 1
				end
			end
		end
		
		-- // If the pcall succeeds, this code is ran.
		if s then
			
			local desc = char:WaitForChild("Humanoid"):WaitForChild("HumanoidDescription")
			local hats = desc:GetAccessories(true)
			
			local hatsToDelete = {}
			
			for i, hat in pairs(hats) do
				local assetId = hat.AssetId
				
					local success, result = pcall(function()
						return MarketplaceService:GetProductInfo(assetId)
					end)
				
				if success then
					if result then
						
						-- // Sort if it is made by a Group or User
						-- // Users
						if result.Creator.CreatorType == "User" then
							local UserId = result.Creator.CreatorTargetId
							local IsBanned = BanList["BannedCreators"][tonumber(string.sub(tostring(UserId),1,1))][UserId]
							
							if IsBanned then
								
								-- This code demonstrates how there can be a reason demonstrated to the player why their hat is banned, but I'm too lazy rn to make a notification system for this :))))))))
								print("Hat " .. result.Name .. " is from a banned user! " .. result.Creator.Name .. " Reason: " .. IsBanned)
								
								local s1, bannedHat = pcall(function()
									return InsertService:LoadAsset(assetId)
								end)

								if s1 then
									table.insert(hatsToDelete, bannedHat:FindFirstChildOfClass("Accessory").Name)
									bannedHat:Destroy()
								end
							end
						end
						
						-- // Groups
						if result.Creator.CreatorType == "Group" then
							local GroupId = result.Creator.CreatorTargetId
							local IsBanned = BanList["BannedGroups"][tonumber(string.sub(tostring(GroupId),1,1))][GroupId]
							
							if IsBanned then
								
								-- This code demonstrates how there can be a reason demonstrated to the player why their hat is banned, but I'm too lazy rn to make a notification system for this :))))))))
								print("Hat " .. result.Name .. " is from a banned group! " .. result.Creator.Name .. " Reason: " .. IsBanned)
								
								local s1, bannedHat = pcall(function()
									return InsertService:LoadAsset(assetId)
								end)
								if s1 then
									table.insert(hatsToDelete, bannedHat:FindFirstChildOfClass("Accessory").Name)
									bannedHat:Destroy()
								end
							end
						end
					end
				end
			end
			
			-- // The hats that will be removed from the character have been inserted into the hatsToDeleteTable. Now we run through each entry and delete them off the character. 
			
			for i, hat in pairs(hatsToDelete) do
				local hatOnChar = char:FindFirstChild(hat)
				if hatOnChar then
					hatOnChar:Destroy()
				end
			end
		end
	end)
end)

– Module Script serving as a Ban List

-- local module = {
	
	["BannedCreators"] = {
		[1] = {
			
		},
		[2] = {
			
		},
		[3] = {
			
		},
		[4] = {
			
		},
		[5] = {
			
		},
		[6] = {
			
		},
		[7] = {
			
		},
		[8] = {
			[8115493] = "User creates accessories that cover up the player."
		},
		[9] = {
			
		}
	},
	
	["BannedGroups"] = {
		[1] = {
			[16142068] = "Group creates accessories that cover up the player.",
			[16006645] = "Group creates accessories that cover up the player."
		},
		[2] = {

		},
		[3] = {
			[32939069] = "Group creates accessories that cover up the player.",
			[32836034] = "Group creates accessories that cover up the player."
		},
		[4] = {

		},
		[5] = {

		},
		[6] = {

		},
		[7] = {

		},
		[8] = {

		},
		[9] = {

		}
	}
}

return module

To answer this, I truly believe it is always important to protect your code in some sort of protected call(pcall) to prevent against, as unlikely as it is, a downtime. As unlikely as it is of course, it’s something to take into account in the long run and can affect user experience.

To answer this question, from past works I have done, the easiest way I have found to get the name of an item is by doing game:GetService("MarketplaceService"):GetProductInfo(ACCESSORY).Name

If you have any questions or concerns to follow up with my help, feel free to do so!!

1 Like

If I explained my concerns incorrectly, I am sorry. :GetProductInfo().Name returns the name of the Accessory that it has on the Creator Marketplace, not when the Accessory itself is on the character.


Screenshot demonstrating naming differences.

I am using InsertService:LoadAsset() to create a duplicate of the accessory, and then grab the name the accessory has when it is attached to the player.

-- Code for demonstration/explanation purposes

local asset = InsertService:LoadAsset(assetId) -- Create a duplicate of the accessory
local aName = asset:FindFirstChildOfClass("Accessory").Name -- Get name of asset
local hatOnChar = character:FindFirstChild(aName) -- Find Accessory on avatar.
if hatOnChar then hatOnChar:Destroy() end -- Destroy Accessory

While this works fine, I was wondering if there was more streamlined way of doing this, as I feel that using InsertService would not be an optimal way of grabbing the Accessories name.

I appreciate your answer to my question on the pcall, and I am very thankful for your assistance.