Using :GetAssetIdsForPackage() not working

local assetService = game:GetService('AssetService')

print(assetService:GetAssetIdsForPackage(24243319))

[HTTP 400 (HTTP/1.1 400 Bad Request)] this is the error I get. I’ve never used this before, but I’m basically trying to figure out how to morph players with R15 since CharacterMeshes aren’t a thing there. Eventually I will be using completely custom packages made in blender, but I’d like to be able to import the parts somehow, so I can use the below code to put the parts onto the character:

function classesManager:GiveClass(player)
	
	local class = dataManager:GetClass(player)
	if class then
		local character = player.Character
		if character then
			local humanoid = character.Humanoid
			if not humanoid then return end
			for _,v in ipairs(game:GetService('AssetService'):GetAssetIdsForPackage(24243319)) do
				if v:IsA('Part') then
					local part = v:Clone()
					character[v.Name]:Destroy()
					part.Parent = character
				end
			end
		
			humanoid:BuildRigFromAttachments()
		end
	end
end

Where did you get that ID? It’s not the assetId for a package which means that GetAssetIdsForPackage will fail.

You can see that it’s not the correct ID by going to the following link:

Went onto the catalog and it was under bundles:

https://www.roblox.com/bundles/240/Mystic-Vizier?rbxp=24243319

The last numer I figured was the ID. Tried the 240 number and got the same error tho, so where is the ID located??

That’s a Roblox promotional code(they get a % or everything you buy - probably from an extension), not an asset ID.

The last number is not added in URLs by Roblox, that’s a chrome extension you are using. This is probably the internal product id of the asset and is likely being used for referral revenue for the extension.

The assetId for this package is 81734174. See here:
https://www.roblox.com/catalog/81734174/redirect

There is no good way to find this AssetId for bundles, the GetBundleDetailsApi should be be used instead with the bundle ID which is 240 in this case.

Example just printing out the result returned by this API:

local HttpService = game:GetService("HttpService")
local AssetService = game:GetService("AssetService")

local bundleDetails = AssetService:GetBundleDetailsAsync(240)
print(HttpService:JSONEncode(bundleDetails))
1 Like

So wait what’s the difference between a bundle and a package?? I thought they were the exact same thing, just got renamed to bundles. Cant find packages anywhere on the catalog tab

Bundles are the replacement for packages, the functionality is a bit different from the users perspective. Bundles come with preset costumes instead of the old system of applying a package. From a developer perspective the main change is that the ID which you use to access details about a bundle is an ID that is unique to bundles, it is not just a regular asset ID.

local character = player.Character
if character then
	local humanoid = character.Humanoid
	if not humanoid then return end
	for _,v in ipairs(game:GetService('AssetService'):GetAssetIdsForPackage(81734174)) do
		if v:IsA('Part') then
			local part = v:Clone()
			character[v.Name]:Destroy()
			part.Parent = character
		end
	end
		
	humanoid:BuildRigFromAttachments()
end

Well I’m trying to put the package onto the player and had found this code. Error lies on if v:IsA('Part') then saying that v is a number or something??

Yes, the API GetAssetIdsForPackage returns a table of AssetIds which are numbers and not Instances.

Here is how you would do this:

local AssetService = game:GetService("AssetService")
local InsertService = game:GetService("InsertService")

local assetIdsCache = {}
local function getAssetIdsList(packageAssetId)
	if assetIdsCache[packageAssetId] then
		return assetIdsCache[packageAssetId]
	end
	
	local retries = 3
	while retries > 0 do
		local success, assetsInPackageOrError = pcall(function() 
			return AssetService:GetAssetIdsForPackage(packageAssetId)
		end)
		
		if success then
			assetIdsCache[packageAssetId] = assetsInPackageOrError
			return assetsInPackageOrError
		else
			warn("Failed to get assetIds for packageAssetId " ..packageAssetId..
			     "\nError: " ..assetsInPackageOrError)
		end
		
		retries = retries - 1
	end
	
	return {}
end

local function getAssetsInPackage(packageAssetId)
	local ASSET_LOAD_RETRIES = 2
	
	local resultModel = Instance.new("Model")
	local loadingFinished = Instance.new("BindableEvent")

	local assetsIdsInPackage = getAssetIdsList(packageAssetId)

	local assetsLoaded = 0
	for _, id in pairs(assetsIdsInPackage) do
		spawn(function()
			local retries = ASSET_LOAD_RETRIES
			
			while retries > 0 do
				local success, loadedModelOrError = pcall(function()
					return InsertService:LoadAsset(id)
				end)
				
				if success then
					for _, item in pairs(loadedModelOrError:GetChildren()) do
						item.Parent = resultModel
					end
					break
				else
					warn("Failed to get load assetId " ..id..
				 	     "\nError: " ..loadedModelOrError)
				end
				
				retries = retries - 1
			end
			
			assetsLoaded = assetsLoaded + 1
			
			if assetsLoaded == #assetsIdsInPackage then
				loadingFinished:Fire()
			end
		end)
	end

	if #assetsIdsInPackage > 0 then
		loadingFinished.Event:Wait()
	end
	return resultModel
end


local function loadPackageForPlayer(player, packageAssetId)
	local character = player.Character
	if not character then 
		return
	end
	
	local humanoid = character:FindFirstChildOfClass("Humanoid")
	if not humanoid then
		return
	end
	
	local function addR15PartsToCharacter(folder)
		for _, part in pairs(folder:GetChildren()) do
			local existingPart = character:FindFirstChild(part.Name)
			part.Parent = character
			
			if existingPart then
				existingPart:Destroy()
			end
		end
	end
	
	local packageItemsModel = getAssetsInPackage(packageAssetId)
	for _, item in pairs(packageItemsModel:GetChildren()) do
		if item:IsA("Folder") and item.Name == "R15ArtistIntent" then
			addR15PartsToCharacter(item)
		end
	end
	
	humanoid:BuildRigFromAttachments()
end

local mysticVizierAssetId = 81734174
loadPackageForPlayer(player, mysticVizierAssetId)

This code loads assets in parallel for performance and will cache the list of assetIds in the package.

Ok well that works perfectly! But now experimenting with trying to get it to work on a custom made character mesh set. So I can’t use the asset service.

local character = player.Character
		if character then
			local humanoid = character.Humanoid
			if not humanoid then return end
			for _,v in ipairs(classes:FindFirstChild(class).Armour:GetChildren()) do
				local part = v:Clone()
				character[v.Name]:Destroy()
				part.Parent = character
			end
		
			humanoid:BuildRigFromAttachments()
		end

Don’t get any errors, the player just dies and respawns back to normal. Not sure what’s missing. I tried to add the Head and HumanoidRootPart, but didn’t make a difference. I know the normal packages contain a lot of Attachments, but I got no clue how to go abouts adding those things in properly. Do these meshes need those attachments in them for this to work??

BuildRigFromAttachments will only rebuild the rig for the character if the necessary RigAttachments are in the character. Otherwise the character will have no joints and will immediately die.

You need to rig your character so that it will have joints. This can be done by positioning the attachments in Studio for each part or could also be done with a script to copy over the attachments from another R15 package and change the positioning for the new parts size. This won’t get you the best possible result but it can avoid a lot of manual joint positioning.

You can find a list of the attachments on this page:
https://developer.roblox.com/articles/r6-vs-r15-avatars

1 Like

Hmm ok. Ended up just copying over Attachments from another package, but not the characters just lay on the floor XD

Just thinking out loud tho, couldn’t I just change each body parts MeshId and TextureId?? Instead of trying to delete all their parts, then replace them with a different part, surely it’d make sense to just change the MeshId?

You can put this code into a module script and then call the module script and just send over the Bundle Id and character.

Roblox doesn’t allow you to just change the MeshId on characters, not sure as to why but because they don’t you have to replace them with new parts and rig it.

What this code does:
Get’s the package and accessories out of the bundle, imports them, takes the folders and applies the body parts in the folders to the character. Works with both R6 and R15. I’d recommend looking over the code so you may get some learning out of it and see what’s going on. If you have any questions just message me :slight_smile:

local as = game:GetService('AssetService')
local is = game:GetService('InsertService')
local marketplaceService = game:GetService('MarketplaceService')


local assetTypeDic = {
	[27] = Enum.AssetType.Torso,
	[28] = Enum.AssetType.RightArm,
	[29] = Enum.AssetType.LeftArm,
	[30] = Enum.AssetType.RightLeg,
	[31] = Enum.AssetType.LeftLeg,
	[17] = Enum.AssetType.Head,
	[41] = Enum.AssetType.HairAccessory,
	[42] = Enum.AssetType.FaceAccessory,
	[43] = Enum.AssetType.NeckAccessory,
	[44] = Enum.AssetType.ShoulderAccessory,
	[45] = Enum.AssetType.FrontAccessory,
	[46] = Enum.AssetType.BackAccessory,
	[47] = Enum.AssetType.WaistAccessory,
	[57] = Enum.AssetType.EarAccessory,
	[58] = Enum.AssetType.EyeAccessory
}

local function buildJoint(parentAttachment, partForJointAttachment)
    local jointname = parentAttachment.Name:gsub("RigAttachment", "")
    local motor = partForJointAttachment.Parent:FindFirstChild(jointname)
    if not motor then
        motor = Instance.new("Motor6D")
    end
	motor.Name = jointname
 
    motor.Part0 = parentAttachment.Parent
    motor.Part1 = partForJointAttachment.Parent
 
	motor.C0 = parentAttachment.CFrame
    motor.C1 = partForJointAttachment.CFrame
 
    motor.Parent = partForJointAttachment.Parent
end
 
local function buildRigFromAttachments(last, part)
	for _, attachment in pairs(part:GetChildren()) do
		if attachment:IsA("Attachment") and string.find(attachment.Name, "RigAttachment") then
			for _, sibling in pairs(part.Parent:GetChildren()) do
				if sibling ~= part and sibling ~= last then
					local matchingAttachment = sibling:FindFirstChild(attachment.Name)
					if matchingAttachment then
						buildJoint(attachment, matchingAttachment)
						buildRigFromAttachments(part, matchingAttachment.Parent)
					end
				end
			end
		end
	end
end
 
local function getOldCharacterMesh(character, newCharacterMesh)
    for _, obj in pairs(character:GetChildren()) do
        if obj:IsA("CharacterMesh") and obj.BodyPart == newCharacterMesh.BodyPart then
            return obj
        end 
    end
    return nil
end
local function applyBodyPart(character, r15Folder, r6Folder)
    local humanoid = character:FindFirstChild("Humanoid")
    if humanoid.RigType == Enum.HumanoidRigType.R15 then
        for _, part in pairs(r15Folder:GetChildren()) do
            local oldPart = character:FindFirstChild(part.Name)
            part:Clone().Parent = character
            oldPart.Name = ""
            oldPart:Destroy()
        end
        buildRigFromAttachments(nil, character.HumanoidRootPart)
    else
        for _, characterMesh in pairs(r6Folder:GetChildren()) do
            local oldCharacterMesh = getOldCharacterMesh(character, characterMesh)
            if oldCharacterMesh then
                oldCharacterMesh:Destroy()
            end
            characterMesh.Parent = character
        end
    end
end

local function getAssetIds(bundleId)
	local details = as:GetBundleDetailsAsync(bundleId)
	
	local ids = {}

	for i, detail in pairs(details) do
		if type(detail) == "table" then
			for _, d in pairs(detail) do
				if type(d) == "table" then
					for _, a in pairs(d) do
						if type(a) == "number" then
							local info = marketplaceService:GetProductInfo(a)
							if assetTypeDic[info.AssetTypeId] then
								table.insert(ids, a)
							end
						end
					end
				end
			end
		end
	end
	
	return ids
end
local import = {}
function import:ImportPackage(packageId, char)
	local humanoid = char:FindFirstChild("Humanoid")
    local rigType = Humanoid.RigType
	if packageId ~= 1 then
		local ids = getAssetIds(packageId)
		for _, assetId in pairs(ids) do
			local asset = is:LoadAsset(assetId)
			if rigType == Enum.HumanoidRigType.R6 then
				if asset:FindFirstChild("R6") then
					applyBodyPart(selectedModel, nil, asset.R6)
				else
					if asset:FindFirstChildOfClass("Accessory") then
						local accessory = asset:FindFirstChildOfClass("Accessory")
						accessory.Parent = char
						humanoid:AddAccessory(accessory)
					elseif asset:FindFirstChildOfClass("Tool") then
						asset.Parent = selectedModel
					end
				end
			else
				if asset:FindFirstChild("R15Fixed") then
					local folder = asset:FindFirstChild("R15Fixed")
					applyBodyPart(char, folder, nil)
				elseif asset:FindFirstChild("R15Anim") then
					asset:FindFirstChild("R15Anim").Parent = char
				elseif asset:FindFirstChild("Mesh") then
					selectedModel.Head:FindFirstChildOfClass("SpecialMesh"):Destroy()
					asset:FindFirstChild("Mesh").Parent = char:WaitForChild("Head")
				elseif asset:FindFirstChild("face") then
					selectedModel.Head:FindFirstChildOfClass("Decal"):Destroy()
					asset:FindFirstChild("face").Parent = char:WaitForChild("Head")
				else
					if asset:FindFirstChildOfClass("Accessory") then
						local a = asset:FindFirstChildOfClass("Accessory")
						a.Parent = char
						humanoid:AddAccessory(a)
					elseif asset:FindFirstChildOfClass("Tool") then
						asset:FindFirstChildOfClass("Tool").Parent = char
					elseif asset:FindFirstChildOfClass("Decal") then
						selectedModel.Head:FindFirstChildOfClass("Decal"):Destroy()
						asset:FindFirstChildOfClass("Decal").Parent = char.Head
					end
				end
			end
		end
	end
	humanoid.HipHeight = 1.35
end
1 Like

Problem with thise though is eventually I’m gonna be moving to custom character meshes, so i can’t use the assetservice, etc

Then you can send in folders inside a model that have corresponding names with this.
So like this:

Model --> Folder --> LeftLowerLeg, LeftUpperLeg, LeftFoot
Model --> (other) Folder --> RightLowerLeg, LeftUpperLeg, LeftFoot

and so on.

So then you’d do:

for _, folder in pairs(model:GetChildren()) do
     applyBodyPart(char, nil, folder)
end

Adjust the module code to just send over the model instead of Bundle Id