Telling the difference between a Script and a LocalScript at runtime is easy, simply do the following;
if myItem:IsA("Script") then
print("Script")
elseif myItem:IsA("LocalScript") then
print("LocalScript")
end
However with the introduction of RunContext, a Script could be made to run both on client side like LocalScript or on the server side like legacy Script.
Telling the difference between scripts with different RunContext is not possible in the intuitive way;
if myItem:IsA("Script") then
if myItem.RunContext == Enum.RunContext.Client then
print("Client Script")
elseif myItem.RunContext == Enum.RunContext.Server then
print("Server Script")
elseif myItem.RunContext == Enum.RunContext.Legacy then
print("Legacy Script")
end
end
This code will fail at runtime with the following permissions error:
The current identity (2) cannot RunContext (lacking permission 1)
So what is the best way to distinguish scripts by their run context at runtime?
EDIT: I want to emphasize that I need to check this from OUTSIDE the script, not from the inside. As @metatablecatmaid correctly points out, we can easily check if we are running on server or client with RunService:IsServer and RunService:IsClient.
Those will only work from INSIDE the script in question. I want to know from the OUTSIDE, as in:
local myItem:Script = someInstance:FindFirstChild("RandomItem")
if myItem:IsA("Script") then
if myItem.RunContext == Enum.RunContext.Client then
-- put the script in RepliactedStorage
elseif myItem.RunContext == Enum.RunContext.Server then
-- put the script in ServerStorage
end
end
The benefits of using RunContext is not apparent before you actually get used to them, and even before that I would find Legacy scripts to be very counter intuitive as they will force you to use arbitrary locations in the datamodel. Creating anything but the most trivial of stand alone modules or packages based on Legacy scripts is very cumbersome. The module needs to bootstrap and put the relevant sub scripts in a bunch of locations. It complicates and slows down both the development as well as the runtime of the resulting solution.
Contrast this with using RunContext; the package has one main Client Script and one main Server Script. They run wherever you put them. If you don’t want them to run you simply set Enabled=false on them and enable them when you need them to run. If your module is larger in size you can split up the code in sub-scripts. There is no need to think about whire they are placed, just avoid the Starter folders.
The use case for knowing which is which from outside the script is of course if you want to make a plugin architecture. You can iterate over the instances in a folder and depending on their type you do different things. This is a common pattern I find to be very useful for many different situations.
You can use the GetDescendants method of the Workspace object to get a list of all descendant objects of the Workspace , and then iterate through the list and check the ClassName and Name properties of each object to identify the script you are interested in.
local workspace = game:GetService("Workspace")
local scripts = workspace:GetDescendants()
for _, obj in ipairs(scripts) do
if obj.ClassName == "Script" and obj.Name == "MyScript" then
-- Found the script, now check its run context
if obj.RunContext == Enum.RunContext.Client then
print("Client Script")
elseif obj.RunContext == Enum.RunContext.Server then
print("Server Script")
elseif obj.RunContext == Enum.RunContext.Legacy then
print("Legacy Script")
end
end
end
Or use the GetChildren method of the Workspace object to get a list of children of the Workspace , and then recursively search through the children of each object in the list until you find the script you are looking for.
local function findScript(parent, name)
for _, obj in ipairs(parent:GetChildren()) do
if obj.ClassName == "Script" and obj.Name == name then
-- Found the script, now check its run context
if obj.RunContext == Enum.RunContext.Client then
print("Client Script")
elseif obj.RunContext == Enum.RunContext.Server then
print("Server Script")
elseif obj.RunContext == Enum.RunContext.Legacy then
print("Legacy Script")
end
return obj
end
local script = findScript(obj, name)
if script then
return script
end
end
end
local workspace = game:GetService("Workspace")
findScript(workspace, "MyScript")
Thanks for your reply, however you are not adressing the question. The question is how can tell two scripts apart based on the RunContext property alone. I know how to iterate the scene for descendants, that is not related to the question. I also know that I can encode the script type in the name of the script which would be a convention that imposes unwanted restrictions on my workflow.
It almost feels like you asked GPT3 for an answer…
As @metatablecatmaid correctly points out, we can easily check if we are running on server or client with RunService:IsServer and RunService:IsClient.
But I was looking for a way to tell this from outside, so the best and only working way I found is to do the following: local function isServerScript(myItem)
local success, message = pcall(function()
return myItem.RunContext == Enum.RunContext.Client
end)
return not success
end
I didn’t read the entire word salad, but it looks like you’re trying to use this for a plugin
RunContext is plugin security, which means you can access it in a plugin, I honestly think it would still be nice to at least read the state of RunContext in a game since it makes sense why its write locked.