Release Notes for 369


Wasn’t this already the case? What changed?

2 Likes

Looks like the note wasn’t elaborated upon. This is referring to a bug with the Lua Toolbox where inserting a Sky wouldn’t parent it inside of the Lighting service.

12 Likes


Just in time for Nexus Admin’s refactoring! Glad to see this was added considering how much I use it in Python.

12 Likes

Does string.split use string patterns or does it use plain text for splitting?

ez ragdolls?

8 Likes

HumanoidDescription is a new object that represents components of a Humanoid’s appearance, such as accessories, animations, packages, etc.

I believe the goal of this system is to make it easier for developers to apply existing/custom costumes to R15 rigs, without needing to cover all existing/future edge-cases with the system.

There are lots of components to Roblox’s avatar system, each with their quirks and rules, so it helps to have it all centralized in a system that’s easy for developers to use, as it interfaces with the C++ code that assembles avatars in the first place!

For example, if you wanted to apply a bundle to a character using just the id of the bundle, you could have a function that looks something like this:

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

local function applyBundle(humanoid, bundleId)
	local bundleInfo = AssetService:GetBundleDetailsAsync(bundleId)
	local outfitId = 0
		
	-- Find the outfit that corresponds with this bundle.
	for _,item in pairs(bundleInfo.Items) do
		if item.Type == "UserOutfit" then
			outfitId = item.Id
			break
		end
	end
		
	if outfitId > 0 then
		local bundleDesc = Players:GetHumanoidDescriptionFromOutfitId(outfitId)
		humanoid:ApplyDescription(bundleDesc)
	end
end

The object itself has a lot of properties available at your disposal to define a costume for an avatar.

16 Likes

Here’s an example of what it generates for my avatar.

Interestingly, the accessories are strings that function as a comma separated list of asset ids.

5 Likes

Plain text. The separator is also optional and defaults to ,.

6 Likes

Local file importing in Studio as temporary assets.

This will be a valuable asset.

4 Likes

I completely overlooked string.split, time to make more libraries redundant!

My VMF Importer plugin will support this right out of the box :)!

3 Likes

Any possibility on whether or not StartDecalDrag() will be allowed for Developer use?

Its possible that you might not need to.

According to the Lua code that controls asset insertion from the toolbox, there’s a fallback case for when StartDecalDrag fails.

2 Likes

Certainly should be easier, but you’ll still have to manually break the existing joints.

1 Like

Goodness gracious, it seems that a lot of new features are coming to help facilitate development. I am quite excited. I’ll be noting this for my Monthly Recap rants.

I actually didn’t notice the Humanoid.BreakJointsOnDeath property - this means hooray for corpse-creation, ragdolls and extended after-death effects without hitting edge cases or producing hacky workarounds for the old behaviour (I for one used custom health systems with ValueObjects or clamped health to 0.01 and used custom states if health reaches below 1).

This pack of updates is amazing. But I am still keeping my eye on MessagingService.

7 Likes

This is endlessly cool and I can’t wait to see what others do with LocalAssets.

2 Likes

This doesn’t seem to do what I need it to do.

Any other way to workaround this?

I still feel StartDecalDrag() should be allowed for plugin use.

Would StartDrag also work?

EDIT: A bunch of drag/drop events and functions were added in one of the previous releases: Release Notes for 366

Nope - the datatype is incorrect, so this function is unusable - I tested it with my plugin with no good results.

Not even sure what the drag function would be used for.

The toolbox uses StartDrag when you drag and drop models from the catalog onto workspace:

function InsertAsset.dragInsertAsset(options)
	local assetId = options.assetId
	local assetName = options.assetName

	if DebugFlags.shouldDebugWarnings() then
		print(("Inserting asset %s %s"):format(tostring(assetId), tostring(assetName)))
	end

	ChangeHistoryService:SetWaypoint(("Before insert asset %d"):format(assetId))

	local success, errorMessage = pcall(function()
		-- Mark the toolbox as using the C++ drag handler implementation
		-- That will insert the given asset and drag it in the 3d view
		options.plugin.UsesAssetInsertionDrag = true

		-- TODO CLIDEVSRVS-1246: This should use uri list or something
		local url = Urls.constructAssetGameAssetIdUrl(
			assetId,
			options.assetTypeId,
			Category.categoryIsPackage(options.categoryIndex)
		)
		if DebugFlags.shouldDebugUrls() then
			print(("Dragging asset url %s"):format(url))
		end
		options.plugin:StartDrag({
			Sender = "LuaToolbox",
			MimeType = "text/plain",
			Data = url,
		})
	end)

	if success then
		ChangeHistoryService:SetWaypoint(("After insert asset %d"):format(assetId))
		sendInsertionAnalytics(options)

		-- TODO CLIDEVSRVS-1689: For AssetInsertionTracker.trackInsert with dragged
		-- asset, need to listen for dropped event on 3d view which
		-- depends on viewports api

		-- TODO CLIDEVSRVS-1246: If they cancel the drag, this probably shouldn't be called?
		options.onSuccess(assetId)
	else
		warn(("Toolbox failed to drag asset %d %s: %s"):format(assetId, assetName, errorMessage or ""))
	end
	return success

end

StartDrag accepts a dictionary, so you should be able to do a whole lot with it.