Problem
As a Roblox developer, it is currently impossible to read various properties of SurfaceAppearance and MaterialVariant from a script because they are set to Plugin Security
. The classes have the properties ColorMap
, MetalnessMap
, NormalMap
, and RoughnessMap
which are set to plugin security so reading these properties from scripts results in a run time error.
Use Case
My specific use case deals with the functionality of ContentProvider:PreloadAsync(). I have developed a system which dynamically generates a list of sound and image assets that are to be preloaded by the client. There’s a set amount of common assets that are loaded first which are common to all game maps. In addition to the common assets, there are additional assets that are map specific which are loaded after the common assets. Some items in both common assets and map specific assets have SurfaceAppearence
classes associated with them. MaterialVariant
is exclusively a common asset.
The code that generates the asset list for this system is below:
-- Builds the preload asset list.
function buildPreloadedAssetList()
local sourceList = {
game.ReplicatedFirst;
game.ReplicatedStorage;
game.StarterGui;
game.ServerStorage:FindFirstChild("MapParts");
game.ServerStorage:FindFirstChild("Tools");
game.ServerStorage:FindFirstChild("ToolModels");
game.ServerStorage:FindFirstChild("Weapons");
--game.ServerStorage:FindFirstChild("");
}
-- Makes sure that all assets in the list are unique.
local function unique(list)
local newlist = {}
table.sort(list)
local prev = nil
for _, item in pairs(list) do
if item ~= prev then
table.insert(newlist, item)
end
prev = item
end
return newlist
end
-- Generates the asset list for the given path.
local function generateAssetList(root, list)
-- Processes the asset URL.
local function processURL(list, str)
local flag = false
if str ~= nil and str ~= "" then
-- If the string starts with http, then we extract the
-- asset Id and replace the prefix with rbxassetid.
if string.find(str, "http://", 1, true) ~= nil then
local txb = string.split(str, "=")
local aId = txb[#txb]
str = "rbxassetid://" .. aId
flag = true
-- If the string starts with rbxassetid, then we do
-- nothing.
elseif string.find(str, "rbxassetid", 1, true) ~= nil then
flag = true
end
-- If one of the two branches above was taken, then this
-- is a valid asset Id.
if flag == true then
table.insert(list, str)
end
end
end
-- Currently, it seems that only sounds and images
-- are supported.
local objectList = root:GetDescendants()
local data
for _, item in pairs(objectList) do
if item:IsA("Decal") then
processURL(list, item.Texture)
elseif item:IsA("Sound") then
processURL(list, item.SoundId)
elseif item:IsA("MeshPart") then
processURL(list, item.MeshId)
processURL(list, item.TextureID)
elseif item:IsA("SpecialMesh") then
processURL(list, item.MeshId)
processURL(list, item.TextureId)
elseif item:IsA("MaterialVariant") then -- Generates Runtime Error
--processURL(list, item.ColorMap)
--processURL(list, item.MetalnessMap)
--processURL(list, item.NormalMap)
--processURL(list, item.RoughnessMap)
elseif item:IsA("SurfaceAppearance") then -- Generates Runtime Error
--processURL(list, item.ColorMap)
--processURL(list, item.MetalnessMap)
--processURL(list, item.NormalMap)
--processURL(list, item.RoughnessMap)
elseif item:IsA("Sky") then
processURL(list, item.MoonTextureId)
processURL(list, item.SunTextureId)
processURL(list, item.SkyboxBk)
processURL(list, item.SkyboxDn)
processURL(list, item.SkyboxFt)
processURL(list, item.SkyboxLf)
processURL(list, item.SkyboxRt)
processURL(list, item.SkyboxUp)
end
end
end
-- Generate asset list for common items.
for _, item in pairs(sourceList) do
generateAssetList(item, preloadAssetList)
preloadAssetList = unique(preloadAssetList)
end
preloadSkipCount = #preloadAssetList
preloadCheck = true
end
A similar process is used to generate the map specific asset list. Within game.ReplicatedFirst
, there is a ScreenGui
and a LocalScript
. The local script first clones the ScreenGui
to PlayerGui
and then sends an event to the server requesting the asset list. The server responds with the list of assets that the client should load. When the client is done loading assets, it tells the server that it’s done and waits for a response. Once the server responds, the client removes the ScreenGui
from PlayerGui
and exits while the server spawns the player’s character in the game.
The assets in question can be manually specified, but then what’s the point of having an automatic system? All that’s being asked for is read access to the afore mentioned properties by scripts.
Possible Solution
One possible solution is to remove the Plugin Security
flag and set the properties to read only. However, if it’s by design that plugins are supposed to have full access, then an alternate solution would be to create new properties that are mirrors of the original properties with a security context that allows scripts to read those and set them to read only.
Conclusion
If Roblox is able to address this issue, it would improve my development experience because then I can include more items for the preload phase so the client doesn’t have to download them on the fly which will prevent visual artifacts in the game while those assets are downloaded.