Asset Dictionary | A fast and easy way to get Assets

Loops through an Instance and returns a dictionary containing arrays of every asset it found. By assets, I am referring to assets that the engine has to download, like Decals, Meshes, Sounds, etc.

It is compiled (hence the --!native flag). For reference, in my tests, when compiled it normally takes ~0.002 seconds to run. When it’s not compiled, it takes ~0.008 seconds.


Module link: Asset Dictionary
Documentation: Asset Dictionary Documentation
GitHub: bloodbonnieking/Roblox-Asset-Dictionary


Updates

V1.1:

  • Added different tables for every asset
  • Fixed the 2nd argument not doing anything
  • Changed the elseifs to cases (table)
  • Added some exceptions and errors

V1.2:

  • Fixed a coding oversight that would only sort AssetDictionary, DecalDictionary and TextureDictionary
  • Added a new optional boolean “Reverse”. Read more in the documentation.

V1.3:

  • Added pants and shirt support

V1.4:

  • Fixed supported assets getting skipped over for “not being supported”
  • Switched to numeric for loop to hopefully speed stuff up
  • Switched to using typeof() for exceptions

V1.5:

  • Added a few memory related functions (read more in the documentation)
  • Changed the AssetDictionary table to GeneralDictionary, and finally deprecated it. (REMOVED COMPLETELY IN V1.6)

V1.6:
- Removed GeneralDictionary (it was useless and it made the script unreadable)

  • Added type notations (tried to add type notations before in a few unreleased versions because I didn’t know that ? makes it optional)
  • Removed nil checks
  • Made errors tell you the argument name instead
  • Added a new argument, read more in the documentation

V1.7:

  • Moved documentation (and made it better)
  • Moved animation dictionary to above image labels, this does not affect anything and is merely tidying up the code a bit
  • Changed . to : for functions for slight consistency
  • Video support
  • ClearTable now needs the asset type name to avoid accidental clearing of non-related tables
  • Errors are now level 2 errors
  • Added a print argument for debugging

V1.8:

  • Cleaned up the code, got rid of useless things and improved it a bit
  • Moved dictionaries into ModuleDictionary.Dictionaries. This isn’t a big change, but possibly a tedious one.
  • Got rid of “temporary tables” which were the tables that weren’t share across scripts.
  • Created a new argument, ClearTables which will clear all of the tables before appending the ids (default is true)
  • Removed some useless exceptions
  • Script uses native Luau now (from my testing, it used to take a solid second without native and now it takes like less than half a second) (read more here: Luau Native Code Generation Preview [Studio Beta])
  • Made ClearTable WAY more compact
  • ClearTable now accepts strings with “Dictionary”, not just without
    V1.8.1 (not wasting a version number)
  • The GetDictionary scope no longer ends before the return (that wasn’t intentional)
  • Print will now print a benchmark (note: the time starts as soon as the function is called and the print happens at the end, which means other if statements may change the result.)

V1.9:

  • Added support for particle emitters
  • that’s it

V2:

  • Completely rewritten
  • Dictionaries now contain Instances instead of AssetIDs. There will be a new argument added soon to return to the old behavior if you want, but due to roblox messing mesh and texture properties up (cough)

    it will take a bit longer.
  • To reflect this, and for consistency, GetDictionary is now GetAssets
  • Removed a lot of the bloat and.. questionable practices
  • Dictionaries are now returned instead of being global, avoiding race conditions
  • Removed cases entirely and instead it finds the appropriate table based off the ClassName
  • Due to the prior change, VideoDictionary and ParticleDictionary are now VideoFrameDictionary and ParticleEmitterDictionary respectively.
  • Removed sorting and reversing, because although they did have use cases, unlike a lot of the other arguments, I did not want to bother with maintaining it and ultimately I doubt people are using this module to create Videos anymore.
  • Changed Print to DebugWarn and now instead of printing a bunch of useless debug data it will throw a warning only if it encounters an instance that is not an Asset. By default it is false so it doesn’t pointlessly spam the console with warnings.
    V2.1:
  • Fixed a bug where MeshParts were falsely flagged as unsupported due to MeshDictionary not using the correct name (MeshPartDictionary)
  • Added SpecialMesh support.

What’s supported:

  • Decals
  • Textures
  • Sounds
  • Meshes
  • Animations
  • Image labels
  • Image buttons
  • Video frames
  • Shirts
  • Pants
  • Particle emitters

Here is the code if you don’t want to open it in a place:

--!native

local ModuleDictionary = {}
local TemplateDictionaries = {
	GeneralDictionary = {};
	DecalDictionary = {};
	TextureDictionary = {};
	SoundDictionary = {};
	MeshPartDictionary = {};
	SpecialMeshDictionary = {};
	AnimationDictionary = {};
	VideoFrameDictionary = {};
	ImageLabelDictionary = {};
	ImageButtonDictionary = {};
	ShirtDictionary = {};
	PantsDictionary = {};
	ParticleEmitterDictionary = {}
}

local function AddToTable(Dictionary, TargetArray, Warn)
	for _, Asset: Instance in pairs(TargetArray) do
		local AssetDictionary = Dictionary[`{Asset.ClassName}Dictionary`]

		if not AssetDictionary then

			if Warn then
				warn(`Invalid Instance type. Expected Asset, got {Asset:GetFullName()}: {Asset.ClassName}`)
			end

			continue
		end
		
		table.insert(AssetDictionary, Asset)
		table.insert(Dictionary.GeneralDictionary, Asset)
	end
end


function ModuleDictionary:GetAssets(Target: Instance, Recursive: boolean?, DebugWarn: boolean?)
	local TargetArray

	assert(typeof(Target) == "Instance", `GetDictionary expects Target to be an instance, got {tostring(Target)}`)	
	if typeof(Recursive) ~= "boolean" then Recursive = false end
	if typeof(DebugWarn) ~= "boolean" then DebugWarn = false end
	
	local Dictionaries = table.clone(TemplateDictionaries)

	if not Recursive then
		TargetArray = Target:GetChildren()
	elseif Recursive then
		TargetArray = Target:GetDescendants()
	end

	AddToTable(Dictionaries, TargetArray, DebugWarn)
	return Dictionaries
end

return ModuleDictionary
12 Likes

why is it uploaded to the “Vulkano Studios” group?

1 Like

edit: the module is no longer uploaded to that group.

Why are you asking?
I’ve asked the people who i work with if i should upload it under the group and they said yes. We have agreed to upload it under the group.
that probably came off ruder than i wanted lol

6 Likes

I’m not rude (sorry if I sounded rude)

but I like your module!

2 Likes

I’ll add a change soon. I’ll add another argument for if you want to reverse the tables or not. Default is true.

Nah don’t worry i was a little confused by the tone. Glad you like it!

3 Likes

Alright patch is out:
fixed supported assets being skipped over even though they are supported
switched to numeric for loop for a slight performance increase (micro-optimizing i know)
switched to typeof() for exceptions
if you have any ideas please tell me. the code looks like a mess ngl

1 Like

Released a new update. I recommend you use this new version. sorry for weird post formatting lol

1 Like

Updated it again, you should use this version since it’s cleaner (i think) and i finally just deleted GeneralDictionary lol

1 Like

well not anymore lmao since i do not work with them anymore. that version is now deprecated since i can’t maintain it.

2 Likes

V1.7!!! Now there is video support, moved documentation, also changed groups due to the fact i do not work with Vulkano Studios anymore. That version is now deprecated since i cannot maintain it. Please use the new one from now on. check changelogs for other changes!

1 Like

You did a mistake here

anyways I joined the linked group but the game in the group is broken

2 Likes

whoops my bad, also dark forest? that’s a very old project i am not really working on at the moment lol. right now i’m working on another horror game since dark forest was very poorly written lmao.

2 Likes

If you find bugs (which you probably will since i suck at coding) please leave a detailed report here. Also, if you have feedback, suggestions or improvement feel free to leave them here!

1 Like

We’ve come a long way, (oh also V1.8 came out and you need to use this since it is way better) from 36 lines and like one table and nearly nothing, to the current 149 lines.

V1 code (release)

local ModuleDictionary = {}
local AssetDictionary = {}
local function AddToTable(Target)
    for _, instance in pairs(Target:GetChildren()) do
        if instance:IsA("Decal", "Texture") then
            table.insert(AssetDictionary, instance.Texture)
        elseif instance:IsA("Sound") then
            table.insert(AssetDictionary, instance.SoundId)
        elseif instance:IsA("Mesh") then
            table.insert(AssetDictionary, instance.MeshId)
        elseif instance:IsA("ImageLabel", "ImageButton") then
            table.insert(AssetDictionary, instance.ImageId)
        elseif instance:IsA("Animation") then
            table.insert(AssetDictionary, instance.AnimationId)
        end
    end
end
function ModuleDictionary.GetDictionary(Target, Descendants, DeleteTarget)
    if Descendants == false or Descendants == nil then
        AddToTable(Target)
    elseif Descendants == true then
        AddToTable(Target)
    elseif Descendants ~= true and Descendants ~= false and Descendants ~= nil then
        error("GetDictionary expects the second argument to be a boolean, got".. " "..tostring(Descendants))    
    end
    local function ReverseTable(ID1, ID2)
        return ID1 > ID2
    end
    table.sort(AssetDictionary, ReverseTable)
    ModuleDictionary.Dictionary = AssetDictionary
    print(AssetDictionary)
    if DeleteTarget == true then
        Target:Destroy()
    end
end
return ModuleDictionary

current code:

--!native

local ModuleDictionary = {}
ModuleDictionary.Dictionaries = {
	DecalDictionary = {};
	TextureDictionary = {};
	SoundDictionary = {};
	MeshDictionary = {};
	AnimationDictionary = {};
	VideoDictionary = {};
	ImageLabelDictionary = {};
	ImageButtonDictionary = {};
	ShirtDictionary = {};
	PantsDictionary = {};
}
local Dictionaries = ModuleDictionary.Dictionaries

--//functions

local function AddToTable(Target)
	for index = 1, #Target do
		local Cases = {
			["Decal"] = function ()
				table.insert(Dictionaries.DecalDictionary, Target[index].Texture)
			end,
			["Texture"] = function ()
				table.insert(Dictionaries.TextureDictionary, Target[index].Texture)
			end,
			["Sound"] = function ()
				table.insert(Dictionaries.TextureDictionary, Target[index].SoundId)
			end,
			["Mesh"] = function ()
				table.insert(Dictionaries.MeshDictionary, Target[index].MeshId)
			end,
			["Animation"] = function ()
				table.insert(Dictionaries.AnimationDictionary, Target[index].AnimationId)
			end,
			["VideoFrame"] = function ()
				table.insert(Dictionaries.VideoDictionary, Target[index].Video)
			end,
			["ImageLabel"] = function ()
				table.insert(Dictionaries.ImageLabelDictionary, Target[index].Image)
			end,
			["ImageButton"] = function ()
				table.insert(Dictionaries.ImageButtonDictionary, Target[index].Image)
			end,
			["Shirt"] = function ()
				table.insert(Dictionaries.ShirtDictionary, Target[index].ShirtTemplate)
			end,
			["Pants"] = function ()
				table.insert(Dictionaries.PantsDictionary, Target[index].PantsTemplate)
			end,
		}
		--print(Target[index], Target[index].ClassName)
		local success, err = pcall(function()
			Cases[Target[index].ClassName]()
		end)
		if not success then
			warn("Instance is not supported. Expected a downloadable or supported asset, got "..tostring(Target[index].ClassName))
		end
	end
end



function ModuleDictionary:GetDictionary(Target: Instance, Descendants: boolean?, ClearTables: boolean?, DeleteTarget: boolean?, Sort: boolean?, Reverse: boolean?, Print: boolean?)

	if Descendants == nil then Descendants = false end
	if ClearTables == nil then ClearTables = true end
	if DeleteTarget == nil then DeleteTarget = false end
	if Sort == nil then Sort = true end
	if Reverse == nil then Reverse = true end
	if Print == nil then Print = false end

	if ClearTables == true then
		for _, tables in pairs(Dictionaries) do
			table.clear(tables)
		end	
	end

	if Descendants == false then
		AddToTable(Target:GetChildren())
	elseif Descendants == true then
		AddToTable(Target:GetDescendants())
	end


	local function ReverseTable(ID1, ID2)
		pcall(function()
			--print(ID1, ID2)
			return ID1 > ID2
		end)
	end


	if Reverse == false and Sort == true then
		for _, tables in pairs(Dictionaries) do
			table.sort(tables)
		end
	elseif Reverse == true and Sort == true then
		for _, tables in pairs(Dictionaries) do
			table.sort(tables, ReverseTable)
		end
	end


	if DeleteTarget == true then
		Target:Destroy()
	end

	if Print == true then
		print("Decals:", Dictionaries.DecalDictionary)
		print("Textures:", Dictionaries.TextureDictionary)
		print("Sounds:", Dictionaries. SoundDictionary)
		print("Meshes:", Dictionaries.MeshDictionary)
		print("Animations:", Dictionaries.AnimationDictionary)
		print("Videos:", Dictionaries.VideoDictionary)
		print("Image Labels:", Dictionaries.ImageLabelDictionary)
		print("Image Buttons:", Dictionaries.ImageButtonDictionary)
		print("Shirts:", Dictionaries.ShirtDictionary)
		print("Pants:", Dictionaries.PantsDictionary)
	end

	--//memory freeing functions

	function ModuleDictionary:ClearTable(Table: string)
		if typeof(Table) == "string" then
			local success, err = pcall(function()
				print(Table)
				if Table:find("Dictionary") then
					table.clear(Dictionaries[Table])
				else
					table.clear(Dictionaries[Table.. "Dictionary"])
				end
			end)
			if not success then
				error("ClearTable expects the string to be a supported asset, got "..Table, 2)
			end
		end
	end

	function ModuleDictionary:ClearAllTables() --//no arguments needed, and will only clear asset dictionary tables.
		for _, tables in pairs (Dictionaries) do
			table.clear(tables)
		end
	end
end

return ModuleDictionary

I just think it’s a fun comparison. anyways use v1.8!!!11111

1 Like

small update: GetDictionary’s scope ends at the correct place now and print prints a benchmark. the time starts as soon as the function is called. should i change this?

  • Keep it this way
  • Start the time as soon as the AddToTable function is called and end it after it stops
  • Start the time as soon as the AddToTable function is called and end it before you print the benchmark

0 voters

1 Like

mr @VSCPlays what do you think (you’re the only invested person also totally choose the 2nd option/j)

Tidied up documentation. I’ll probably release an update later today moving the cases table to a nested function so it isn’t redeclared over and over for every iteration.

Also moving delete target to after Reverse because there’s like barely any uses for it and it’s annoying

i unfortunately could not do this since it looked messier.

unlogged update since it’s small:
this

and i added another benchmark for the for loop. yes, it does check for print.

Hippidy hoppidy your code is now my property :slightly_smiling_face:

Im using this for a studio plugin

1 Like