The error is above, printed from the client. No, I didn’t call ApplyDescription on the client.
Is that normal behavior? Even if the description is applied on the server, the backend cache to direct you to the actual instance isn’t replicated? That doesn’t seem very intuitive. If that’s true, I need to query the server just to know what accessory instance I’m manipulating. I’d understand if I had applied the description on the client and then did this on the server, but not the other way around.
Is there a way around this or am I missing something?
It appears that GetAppliedInstance only works in the same realm that the HumanoidDescription was applied to the player. Unless you write a custom character loader, or are creating clientsided humanoids, this will always be the server; the server loads player characters.
This means that GetAppliedInstance will only work on the server in the vast majority of usecases. I’m not sure why it’s like this - really annoying and awful. The Instance property is not used for that API - the property is only for applying in-game Accessory instances with ApplyDescription instead of being stuck to IDs only.
As an alternative to help, I’ve created some production ready code for adding an attribute to each accessory on the server to make this info easily available to the client. There’s some unknowns that I can’t test because, even in a live server in the player, all the accessories were already loaded by CharacterAdded, but that’s not the case for all games and servers.
In any case, this is tested and should work pretty broadly with the assumptions made.
--!strict
local players = game:GetService("Players")
local function description_applied(description: HumanoidDescription)
for _, subdescription in description:GetChildren() do
if not subdescription:IsA("AccessoryDescription") then
continue
end
local applied_instance = subdescription:GetAppliedInstance()
applied_instance:SetAttribute("AccessoryId", subdescription.AssetId)
end
end
local function appearance_change(character)
local humanoid = character:FindFirstChildOfClass("Humanoid")
local description = humanoid and humanoid:FindFirstChildOfClass("HumanoidDescription")
if not description then
return
end
description_applied(description)
end
players.PlayerAdded:Connect(function(player)
-- I'm unsure of the behavior of calling GetAppliedInstance mid character load
-- possibly before the appearance is fully loaded - it may error, so we won't risk it
player.CharacterAdded:Connect(function(character)
local humanoid = character:FindFirstChildOfClass("Humanoid")
if not humanoid then
return
end
humanoid.ApplyDescriptionFinished:Connect(description_applied)
end)
-- ApplyDescriptionFinished is not called by the Roblox character loader,
-- so we have to add this
player.CharacterAppearanceLoaded:Connect(appearance_change)
end)