As a Roblox developer, it is currently too hard to figure out what kind of accessory(Hat, Hair, etc.) a certain accessory is.
If Roblox is able to address this issue, it would improve my game / my development experience because it’d allow Developers to offer the player more freedom over how their character looks while still limiting some options.
For example, if I was working on a character creation UI, as of now, I would have to offer a limited selection of hairs for the player to choose from, which may not include the player’s desired hear. With some kind of identifier on accessories telling us what each one is, I would be able to allow the player to wear whatever hair they want so long as they have it equipped on their avatar.
What is the specific API context in which you looking to retrieve an accessory’s type?
As far as I am aware, there are already solutions in at least two contexts:
(1) You have a reference to the Instance:
local function getAccessoryType(accessoryInstance)
local attachment = accessoryInstance.Handle:FindFirstChildOfClass("Attachment")
local accessoryType = attachment.Name:match("(.+)Attachment")
return accessoryType
end
(2) You have the AssetId:
-- https://developer.roblox.com/en-us/api-reference/enum/AssetType
local assetTypeLookup = {
[41] = "Hair";
[42] = "Face";
[43] = "Neck";
[44] = "Shoulder";
[45] = "Front";
[46] = "Back";
[47] = "Waist";
}
local marketplaceService = game:GetService("MarketplaceService")
local function getAccessoryType(accessoryAssetId)
local assetInfo = marketplaceService:GetProductInfo(accessoryAssetId, Enum.InfoType.Asset)
local accessoryType = assetTypeLookup[assetInfo.AssetTypeId]
return accessoryType
end
I realise you might be aware of both of these techniques - perhaps you’re looking for something less reliant on parsing or hard-coding.
I’ve considered these solutions, but I was hoping for something more reliable. Web calls are prone to fail/throttle sometimes, which can be somewhat impractical for larger games. I was thinking there could be property that is set whenever the hat is loaded in, that way it’s just one request done whenever the player’s character is loaded. The value could probably be cached this way too, saving even more requests.
This method isn’t reliable because some accessory creators don’t use the correct attachment. Shaggy 2.0 is a good example. It uses the hat attachment instead of the hair attachment despite clearly being a hair accessory.
I don’t think there’s a way of solving this consistently without using a web API at the moment.
Sorry for the bump, but I thought I would just share my current workaround for anybody who stumbles across this looking for answers like I was.
Place the following code in a script in game.ServerScriptService or somewhere similar.
local InsertService = game:GetService("InsertService")
game.Players.PlayerAdded:Connect(function(Player : Player)
Player.CharacterAdded:Connect(function(Character : Model)
-- this ENTIRE mess is literally just for getting the assetId and AssetType of each accessory the player is wearing.
-- get all accessories the player is currently wearing and their assetIds
local appearanceInfo = game.Players:GetCharacterAppearanceInfoAsync(Player.UserId)
-- itterate through each asset the player is wearing
for i, assetInfo : AssetInfo in ipairs(appearanceInfo.assets) do
-- make sure its an accessory and not a body part or a face or something.
if assetInfo.assetType.name ~= "Hat" and not string.find(assetInfo.assetType.name, "Accessory") then continue end
-- load in the accessory Roblox claims the player is wearing
local success, loadedModel = pcall(InsertService.LoadAsset, InsertService, assetInfo.id)
if not success then
warn("Something went wrong while trying to load accessory", assetInfo, loadedModel)
debug.traceback(loadedModel)
continue
end
--print("-------", assetInfo.name, "----------")
local referenceAccesory : Accessory = loadedModel:FindFirstChildOfClass("Accessory") -- async api calls can fail.
local referenceHandle : MeshPart? = referenceAccesory:FindFirstChild("Handle")
local referenceSpecialMesh : SpecialMesh? = nil
if referenceHandle then referenceSpecialMesh = referenceHandle:FindFirstChildOfClass("SpecialMesh") end
-- painstakingly compare the imported accessory to every accessory the player is wearing
for i, wornAccessory in ipairs(Character:GetChildren()) do
if wornAccessory:IsA("Accessory") then
local wornHandle : MeshPart? = wornAccessory:FindFirstChild("Handle")
local wornSpecialMesh : SpecialMesh? = nil
if wornHandle then wornSpecialMesh = wornHandle:FindFirstChildOfClass("SpecialMesh") end
local function applyAssetIdToAccessory()
wornAccessory:SetAttribute("AssetId", assetInfo.id)
wornAccessory:SetAttribute("AssetType", assetInfo.assetType.name) -- name is lowercase for some reason.
wornAccessory:SetAttribute("WebName", assetInfo.name)
end
--print(wornAccessory, referenceAccesory, wornHandle, referenceHandle, wornSpecialMesh, referenceSpecialMesh)
if wornAccessory.Name == referenceAccesory.Name and wornHandle and referenceHandle then
local wornTextureId : number? = (wornHandle and wornHandle:IsA("MeshPart") and wornHandle.TextureID) or (wornSpecialMesh and wornSpecialMesh.TextureId)
local wornMeshId : number? = (wornHandle and wornHandle:IsA("MeshPart") and wornHandle.MeshId) or (wornSpecialMesh and wornSpecialMesh.MeshId)
local referenceTextureId : number? = (referenceHandle and referenceHandle:IsA("MeshPart") and referenceHandle.TextureID) or (referenceSpecialMesh and referenceSpecialMesh.TextureId)
local referenceMeshId : number? = (referenceHandle and referenceHandle:IsA("MeshPart") and referenceHandle.MeshId) or (referenceSpecialMesh and referenceSpecialMesh.MeshId)
--print("textureIds: ", wornTextureId, referenceTextureId)
--print("meshIds: ", wornMeshId, referenceTextureId)
if wornTextureId and referenceTextureId
and wornTextureId == referenceTextureId
and wornMeshId and referenceMeshId
and wornMeshId == referenceMeshId then
-- these two match. Give the worn accessory attributes for its assetId, assetType, and website name.
applyAssetIdToAccessory()
end
end
end
end
end
end)
end)