I want to block the use of UGC accessories in my experience

As a Roblox developer, it is currently too hard to make sure that when I allow visitors to bring their own avatars into my experience, they aren’t wearing assets that are game-breaking. I have to block either an entire category of accessories from wear (mostly Back accessories since that category is easily the most abused to make these), create a custom avatar appearance system (effectively disrespecting other creators’ IP but I have no choice in the matter anymore) or invest time and money into solutions to ban specific UGC accessory creators’ assets in my experiences. At this juncture, the only thing I can think of is simply blocking all non-Roblox-created assets from wear in my experience.

If Roblox is able to address this issue, it would improve my development experience because I could personally handle the scourge of the UGC accessory catalog by not allowing those assets altogether and not having to deal with the issues that are arising out of them such as assets that completely obscure the avatar (horrible for experiences requiring collision, hits against a character or the character to be visible for some gameplay-specific feature).


Below talks about my motivation, but it’s more to do with why I am dissatisfied with the current UGC accessory catalog which directly ties into why I don’t want non-Roblox-created assets to be worn.

It is abundantly clear that while Roblox’s vision forward is to put the catalog in the hands of the creator community, a simple glance at the catalog reveals that given lackadaisical and relaxed moderation criteria, creators will upload anything and everything to the maximum extent permitted. This is straining to me as a developer and I am getting utterly tired of having the problems of the openness of the avatar shop being offloaded to me as a developer instead of having tools readily available to deal with this.

It took us long enough to get access to check a MeshPart’s size and even then it’s not an adequate solution against some of the creations on the catalog. In general, none of these solutions work for me as a developer without in one way or another breaking a rule, violating a personal principle or having to bear costly expenses for solutions integrating third party solutions.

  • Blocking an entire attachment’s worth of accessories is unfortunate because it comes down to the situation of “a few spoiling the bunch”. There are great accessories, for example, that can attach to visitors’ backs, but as it’s also the most prominent category for silly avatar-covering assets, I also have to get rid of those as well.

  • Blocking an accessory by size is simply infeasible. It scales well but that’s all it is - it doesn’t let me fine tune for other problematic assets such as those with ample distance away from the avatar or making some room for genuine assets with a large size to fit onto the avatar.

  • All in-house solutions suck. It’s not hard to hit rate limits on certain APIs especially on larger servers and an arbitrary amount of assets per avatar and that’s with memoisation as well. Not only that but the only thing that I can get out of that is the details on the website. It doesn’t tell me anything about the asset, nevermind determing which creators I should ban.

  • Same goes with a third party solution. All it would do for me is allow me to apply a universal solution to all my experiences but I would be expected to shoulder the costs for hosting it and learning external development solely for this use case. It doesn’t help me better guard against UGC accessories that don’t belong in my experience.

  • At the very extremes, I have to completely remove visitors’ ability to bring their own avatar into my experiences and I need to either create my own characters completely from scratch (I have to finance mesh and clothing creation because I cannot do that myself) or I have to take others’ assets from the catalog and add them into my own experience with or without permission just to granularly control what kind of things visitors are allowed to wear.

Developers need better tooling to handle what’s to come of the openness of the UGC accessory catalog. This was already concerning when the program was closed to a few select creators and when they started uploading these assets, but it wasn’t bad enough to warrant a solution beyond manually writing the problem assets into a manual ban list. Now, UGC accessory creators want their share of the pie in the money to go around in creating accessories and simple bricks with pictures slapped onto them are being allowed to pass UGC accessory moderation into the catalog, and we as developers are strapped for tooling to handle this.

The catalog is still an application-based program and there’s still time to write new policy, improve moderation and help us developers with tooling. I am fully aware that there can’t be a one-size-fits-all solution and there should still be an expectation on developers to handle some cases on their own but I simply can’t stand for “no help at all” where the alternatives are breaking rules, expending capital that I do not have, long term maintenance that I may not be around for or dealing with inane rate limits. There should be no risk to me as a developer for taking steps to reign in what Roblox won’t.

32 Likes

Just add custom startercharacters players can use

7 Likes

I fully support what Colbert is saying here. As someone who didn’t really want to allow avatars in my morph-based RP but did after seeing how many users enjoy roleplaying as OCs, I was forced to create my own accessory system where users submit their back/waist accessories and our community helpers accept them before they can be used ingame.

This just barely works for a game of our size (few hundred CCU), and I know it won’t scale forever. It was also a huge waste of time to implement. All because of some bad apples who got in the program and abused it to make annoying accessories.

Roblox should be paying more attention to the state of UGC and encouraging genuine avatar creativity, instead of allowing and promoting Giant PNG accessories.

12 Likes

Then all the people who use UGC hats of any kind won’t play the game. Sure, you get rid of the obnixious hats that are too big, the low effort pictures slapped on a brick, inappropiate hats, etc. But you also get rid of, every UGC hat. Which can regard somebody’s entire avatar.

You should instead do what @BobygoWasTaken says, or making a size limit to hats. Blocking all UGC hats would be a huge mistake.

Also, you can make a script to do this?

6 Likes

I am someone who has told multiple developers in support categories that StarterCharacter should not be used for anything more than changing the initial construct of a character. Multiple StarterCharacters is using it incorrectly: that is one. Two, this is out of scope for my feature request - and for the use case of modifying characters, this has already been addressed in the post.


I do not care. I would rather hemorrhage potential audience over some manufactured outrage on being unable to bring their UGC accessory hats rather than sacrifice the integrity of my gameplay to be enjoyed by my actual target audience that can live without huge cargo crates obscuring avatars. It should be entirely my decision on what I get to allow in my experiences.

As for the rest of the post, this is entirely addressed in the thread. I have pointed out existing alternative solutions and why they are infeasible for my use case. If they were sufficient, I would not be making a thread asking for tooling or the ability to natively block all user-created accessories.

I don’t think you get to decide for me what is and isn’t a mistake for my experiences. Every experience will have different requirements or desires, and not having obnoxious UGC accessories - or any, in fact, to block potential issues from the outset - is my design decision.


For these replies, as well as any potential future ones I’m expecting, I ask: please actually read the thread. Several of these things are addressed and why they don’t work for my use case, do not scale well or are tough to implement from my end.

2 Likes

Yeah, no. Not going to happen, but this feature request is a good signal that the current state of affairs is not acceptable.

From requesting more granular categories so we can blacklist accessories from such categories, or simply having higher standards, nothing much has really changed since UGC started opening up more.

Everyone wants to know what Roblox is doing to improve developers’ trust in UGC.

7 Likes

I guess it’s fair to say that I’m shooting for air with this request - I am essentially asking Roblox to let me block a large part of their forward avatar vision with this request. It’s regrettable and the alternatives just do not exist to me as a developer, and I frankly am somewhat impatient in seeing some kind of improvement in the catalog. The troubles are being offloaded to me as a developer to resolve and I dislike that.

Ultimately in the short term I am still going to pursue the available options, but they are grossly inadequate. If how Roblox wants to interpret this is as another message in the pile about developer dissatisfaction with avatars, then so be it - that’s what this feature request will serve. Won’t seek to take it down just because I realise that the likelihood of this is slim, though I do want some signal about how things are going to improve, if at all.

One way or another, I’ll exhaust all available venues and give it some time before I begrudgingly implement my own solution to block all UGC accessories.

3 Likes

This would be a great feature for Pathos, we’ve had to do a few things to ensure that no obnoxious hats or other UGC can be worn in game - it doesn’t work well with hitboxes.

This feature would streamline this process so much! I would recommend make something similar to disabling layered clothing; a press of a button as apposed to an API.

1 Like

I don’t mind most UGC since I’m all in for self-expression.

Though there are some accessories that are RIDICULOUSLY large in size and cover a lot of space.
This, in a shooting or fighting game for example can really throw off other players or ruin the immersion.

The oversized accessories in some cases can be used to gain an advantage in the game.
Having very large items can make it harder or trickier to guess a player’s hitbox for example or can be used to hide certain visual clues that are necessary to be visible so you can see things like states/status effects or what items a player carries.

A way to disable overly large accessories would be a very welcome change.

2 Likes

If you are interested in a CustomCharacter creation interface I can provide you with an algorithm that does just that. It’s a reverse engineered version of the bundle importer plugin. I created a version that establishes server-client boundaries and automatically generates the gui. I have a barebones version of it I created you can have it :slight_smile:

you just essentially import all the assets you want to use or you could load each individual asset based on their ID and it will convert the catalogue item into the format. But I wouldn’t reccomend that due to catalogue api requests being slower than accessing server storage.

I got like 130 bundles and hundreds of particularly chosen assets for my experience. The character creation think has over 1300 indexed folders which contain either a bundle or an asset and this only amounts to 1.8gb in ram on server storage. Which you should consider given that maximum size of an experience. But I would reccomend this method over individually importing assets as they are needed. Although you could do it in chunks. or always important the next 5 assets in the index. but that would take a tiny bit of editing.
Here is an example code of the main function.

function importBundle(id)
local importedBundlesFolder = serverStorage:FindFirstChild(‘Imported Bundles’)
if not importedBundlesFolder then
importedBundlesFolder = Instance.new(‘Folder’)
importedBundlesFolder.Name = ‘Imported Bundles’
importedBundlesFolder.Parent = serverStorage
end
local folder = Instance.new(‘Model’)
folder.Name = tostring(id)

--[[local packageInfo = marketplaceService:GetProductInfo(id)
folder.Name = packageInfo.Name or tostring(id)
local assetIds = assetService:GetAssetIdsForPackage(id)]]
local assetIds = {}
local bundleDetails = assetService:GetBundleDetailsAsync(id)
if bundleDetails then
	if bundleDetails.Items then
		folder.Name = tostring(id) ..' - ' .. (bundleDetails.Name or '')
		for _,itemData in pairs(bundleDetails.Items) do
			if itemData.Type == "Asset" and itemData.Id then
				table.insert(assetIds, itemData.Id)
			end
		end
	end
end

for _, assetId in pairs(assetIds) do
	local assetModel = insertService:LoadAsset(assetId)
	if assetModel:IsA('Model') then
		local name = tostring(assetId)
		local success2, err = pcall(function()
			local assetInfo = marketplaceService:GetProductInfo(assetId)
			if assetInfo then
				
				local typeId = assetInfo.AssetTypeId
				if typeId then
					name = assetTypeDictionary[typeId] or name
				end
			end
		end)
		assetModel.Name = name
	end
	assetModel.Parent = folder
end
folder.Parent = importedBundlesFolder
return folder

end

function applyBundle(character, bundleFolder, rigTypeName,body, plr)
if CharV.Value==20 or CharV.Value==0 then
–Actor.Cycles.Value=Actor.Cycles.Value+1
local character=character
Doing=true

	character.Humanoid.Died:Connect(function()
		if Doing==true then
			character.Humanoid:BuildRigFromAttachments()
			character.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Dead, false)	
		end
	end)
	character.Humanoid:GetPropertyChangedSignal("Health"):Connect(function()
		if debounce==true then				
			--if game.Players:FindFirstChild(Actor.Name)~=nil then
			--	Player = game.Players:FindFirstChild(Actor.Name)
			--end
			--if Player.PlayerGui.Bars.Enabled == false then	
			--if HP> character.Humanoid.Health then
			--	character.Humanoid.Health = HP
			--elseif HP< character.Humanoid.Health then
			--	HP = character.Humanoid.Health		
		--	end
		 
		end	
	end)

	local humanoid = character.Humanoid
	--humanoid:UnequipTools()	
	humanoid:SetStateEnabled(Enum.HumanoidStateType.Dead, false)	
	if character:FindFirstChildOfClass("Tool")~=nil then
		CurrentTool= character:FindFirstChildOfClass("Tool")
			character.Humanoid:UnequipTools()	

	end
	humanoid.BreakJointsOnDeath=false
	humanoid.RequiresNeck=false


	if body=="Character" then
		humanoid:RemoveAccessories()
	end
	local Directory=game.ReplicatedStorage.Bundles
	for _,child in pairs(Directory:GetChildren()) do
		if child:FindFirstChild(body)~=nil then
			for _,childbody in pairs(child:GetChildren()) do
				if tostring(childbody.Name)==tostring(body) then
					for _,desc in pairs(childbody:GetChildren()) do
						repeat	
							if character:FindFirstChild(desc.Name)~=nil then
								character:FindFirstChild(desc.Name):Destroy()
							end
						until character:FindFirstChild(desc.Name)==nil
					end
				end
			end	
		end
	end

	if bundleFolder~=nil then	
		for _,asset in pairs(bundleFolder:GetChildren()) do
			if asset.Name==body or body=="Whole Body" or body=="Character" then
				for _,child in pairs(asset:GetChildren()) do
					if child:IsA("Folder") and child.Name == rigTypeName  then  --bodypart
						for _, bodyPart in pairs(child:GetChildren()) do
							local old = character:FindFirstChild(bodyPart.Name)
							if bodyPart:IsA("MeshPart") and old~=nil and old:IsA("MeshPart") then
								if old.MeshId~=bodyPart.MeshId then
									if lowertorso==false then
										if old then
											old:Destroy()
										end

										bodyPart:Clone().Parent = character
									elseif lowertorso==true then
										if bodyPart.Name=="LowerTorso" then
											if old then
												old:Destroy()
											end

											bodyPart:Clone().Parent = character
										end	
									end
									humanoid:BuildRigFromAttachments()
								end
								--SkinColor(game.Players:FindFirstChild(character.Name))


							end
						end
					elseif child:IsA("Accessory") and (body==child.Parent.Name or body=="Character") then --accessory
						local	Access=child:Clone()
						character.Humanoid:AddAccessory(Access)
					elseif child:IsA("Decal")  and (body=="Head" or body=="Face" or body=="Character")  then --head
						local head = character:FindFirstChild('Head')
						if head then
							for _,headChild in pairs(head:GetChildren()) do
								if headChild and headChild.ClassName=="Decal"  and (body=="Head" or body=="Face" or body=="Character")  then --head
									headChild:Destroy()
								end
							end
							child:Clone().Parent = head
						end	

					elseif child:IsA('SpecialMesh') and (body=="Head" or body=="Character")  then --head mesh
						local head = character:FindFirstChild('Head')
						if head then
							if head:IsA('MeshPart') then
								-- Replace meshPart with a head part

								local newHead = partHeadTemplate:clone()	

								newHead.Name = 'Head'
								newHead.Color = head.Color
								newHead.CFrame=head.CFrame
								for _,v in pairs(head:GetChildren()) do
									if v:IsA('Decal') then
										v:Clone().Parent = newHead
									end
								end

								head:Destroy()	
								newHead.Parent = character

								humanoid:BuildRigFromAttachments()

								head = newHead
							end
							for _,headChild in pairs(head:GetChildren()) do
								if headChild and headChild:IsA('SpecialMesh') then
									headChild:Destroy()
								end
							end
							if child:IsA("SpecialMesh") then
								local Mesh= child:Clone()
								if Mesh:FindFirstChild("OriginalSize")~=nil then
									Mesh.Scale=child:FindFirstChild("OriginalSize").Value*humanoid.HeadScale.Value
								else
									Mesh.Scale=child.Scale*humanoid.HeadScale.Value
								end
								Mesh.Parent = head
							else child:Clone().Parent=head	
							end
						end
					elseif child.ClassName=="Decal" then
						child:Clone().Parent=character.Head	
						--print('Not sure what to do with this class:', child.ClassName)
					end

				end	
				--todo: handle animations
			end
			if asset.Name=="Face" and (body=="Head" or body=="Face" or body=="Character")  then
				for _,FaceDecal in pairs(asset:GetChildren()) do
					if character.Head:FindFirstChild(FaceDecal.Name)==nil then
						FaceDecal:Clone().Parent=character.Head
					end	
				end		
			end 
		end
	end

	humanoid:BuildRigFromAttachments()

--	if character==Character then
		spawn(function()
			--		humanoid.BodyDepthScale.Value=Player.Inventory.BodyDepthScale.Value*.999
			--humanoid.BodyHeightScale.Value=Player.Inventory.BodyHeightScale.Value*.999
			--humanoid.BodyTypeScale.Value=Player.Inventory.BodyTypeScale.Value*.999
			--humanoid.BodyProportionScale.Value=Player.Inventory.BodyProportionScale.Value*.999
			--humanoid.BodyTypeScale.Value=Player.Inventory.BodyTypeScale.Value*.999
			--humanoid.BodyWidthScale.Value=Player.Inventory.BodyWidthScale.Value*.999
			--humanoid.HeadScale.Value=Player.Inventory.HeadScale.Value*.999
			--task.wait()
			--humanoid.BodyDepthScale.Value=Player.Inventory.BodyDepthScale.Value
			--humanoid.BodyHeightScale.Value=Player.Inventory.BodyHeightScale.Value
			--humanoid.BodyTypeScale.Value=Player.Inventory.BodyTypeScale.Value
			--humanoid.BodyProportionScale.Value=Player.Inventory.BodyProportionScale.Value
			--humanoid.BodyTypeScale.Value=Player.Inventory.BodyTypeScale.Value
			--humanoid.BodyWidthScale.Value=Player.Inventory.BodyWidthScale.Value
			--humanoid.HeadScale.Value=Player.Inventory.HeadScale.Value
		end)	
--	end

	SkinColor(character)
	debounce=false
end

end

1 Like

You can already do this with current APIs. In fact, I just wrote the script to do it. UGC Deleter - Roblox

3 Likes

It would probably be more apparent that I recognise existing methods and continue to use them but talk about their pitfalls if you actually read the thread over having chosen to respond to the title alone. It’s really important that you read the thread so you don’t end up making posts about things that have already been discussed either in the OP or in other replies.

I did read your thread and you sound like you have absolutely no idea what you’re talking about. The rate limit for MarketplaceService:GetProductInfo as of now is 351 requests per minute with one player in game. It’s supposed to scale with the amount of players in the server. That’s not an “inane” rate limit, that’s a rate limit you should NEVER hit if you handle your “memoization” (just say caching like the rest of us) properly. The pitfalls you describe in your thread do not apply whatsoever, so I don’t appreciate you condescendingly telling me to reread it. Also, even if I was responding to the title and not the content, (which would imply that your thread is titled incorrectly), I can still post my solution because it’s not just about you, it’s about the people who are looking for a solution and found this thread in the search results. That’s not to mention that my code does exactly what your title, your content, and your replies are in consensus that you want to do, anyway. Maybe you should read the code instead of just assuming it’s “sucky” like the in-house solutions you came up with.

2 Likes

Don’t confuse my disagreement with not knowing what I’m talking about.

I have a very real problem with performing an abundant amount of web calls for small systems like trying to manage the player’s avatar, seeing how there lacks a way to batch information requests from the information API. Nevermind it working to scale, I also need it to return usable information for me - working through the web call alone is not enough because I still require other details to make an informed decision about what I’m going to block or not if I don’t outright ban all UGC accessories, which while yes that is the title and request I’m making, is not my only option in the case this request goes nowhere. It’s also just not future-proof as internal rate limits (non-HttpService web calls) can change arbitrarily and without announcement or documentation.

Memoisation is a real technique that is related to caching - it literally is what the process of caching results from a function, particularly an expensive one, is called - so it was unnecessary to add that extra quip about my wording choice. Furthermore, as different experiences have different requirements, it is inaccurate and disingenuous to say that the pitfalls listed do not apply.

Feature requests are meant to be revolving around a problem, so my thread’s title is fine as it summarises and highlights the problem I have developing on the platform and the thread body has a more detailed description about the problem I face. Whether in retaliation or in bad faith, taking shots at my thread like this is just not appropriate.

Please stop replying to my thread if you’re going to argue for the sake of arguing or add moot conversation to this thread because this isn’t contributing useful discussion.

2 Likes

I’ll have to quote reply this since you’ve brought up a huge assortment of points that are starting to veer heavily off topic and out of scope for my feature request, and hyperfocusing on the wrong things.

This is wrong. If an experience is to account for any number of combinations a player may change between, then it’s not going to be up to 11 calls on entry (maximum number of accessories per player). You don’t want to architecture a system thinking of only the most minimum limits. Assuming players will never change their avatars during gameplay is a bold assumption.

It is more intuitive to only flag an asset once for the experience or a set of experiences’ lifetimes and never again, so this is generally unavoidable. It is possible to save a flag only for the current session’s lifetime but that forces me to reevaluate an asset for every other session which is just straight up unnecessary. I shouldn’t need to perform this flag again if I’ve done it before.

I’ve already addressed this.

A feature request is written to highlight a problem, not a potential solution. Please read How to post a feature request - a key point is that developers are to write requests focusing on problems and use cases, not potential solutions.

I’ve written a resource about the XY problem - yes, that’s about focusing on a proposed solution instead of the actual problem. The thread is written to avoid discussing potential solutions (they are intentionally listed in a “motivation” add-on for the feature request and not the main body). These posts here are hyperfocusing on your proposed solution and not my actual problem.

Flag it or message me privately.

The post is disingenuous and dismissive, but I have not attacked your character at all. I would love for this discussion to remain respectful if it actually contributes to the thread, but it currently doesn’t.


Once again, please don’t respond to my thread if you’re going to argue for the sake of arguing or make off-topic replies. I appreciate your contribution but it doesn’t belong here, you can write a resource thread for that. I’ve talked about existing methods in the OP and how I do use them but would appreciate native support for blocking UGC accessories via website configuration.

2 Likes

This is getting a bit out of control. Back when I first replied to this post, I implemented a whitelist system for waist and back items, and it worked pretty well; everything massively sized was blocked, and the more reasonable items were allowed in.

Well, things have become worse. As of now, items are blatantly being uploaded to the wrong category, quite possibly to circumvent systems like this.

This accessory was uploaded as a hair item. This is clearly not hair. It covers the entire body. I’m not going to blacklist every hair item as well.

Here’s the item link, along with a few more huge ones from a completely different creator that were also uploaded as “hair”:


In short, I think we’re past developers reasonably being able to filter out these items by ourselves, on our own terms, without some help from Roblox.

2 Likes

Accessories aren’t normally allowed to be this big. Roblox should probably do something about people abusing backwards Rthro accessory scaling.

The way these extra-large accessories work is because of the existence of a StringValue named “AvatarPartScaleType” in the accessory’s Handle. Its value controls how an accessory is scaled, depending on your R15 body type. This is usually meant for accessories included in Rthro bundles.

Body types include:

  • Classic (Body Type 0%, Proportions N/A)
  • Rthro Normal (Body Type 100%, Proportions 0%)
  • Rthro Slender (Body Type 100%, Proportions 100%)

The value of AvatarPartScaleType can be either Classic (the default scaling logic), ProportionsNormal, or ProportionsSlender respectively, and the accessory will be automatically scaled depending on your avatar’s current body type.

On an “Rthro Normal” avatar, an accessory set to Classic might be slightly distorted as it stretches with the taller avatar, but a ProportionsNormal accessory would look just fine.

If you were to do the inverse, that is, use a ProportionsNormal accessory on a “Classic” avatar, the accessory would be scaled down with the avatar. The Rthro-to-Classic scaling logic causes Rthro heads to become much bigger when they are scaled down, so if the accessory is attached to the head, it too would become much bigger.

So you can probably see how this can be abused with already-large head accessories, since making them an Rthro accessory and then scaling them down would make them oversized, essentially bypassing the maximum accessory sizes for UGC submissions.

1 Like