Need Help: Getting Services that are not children of DataModel

So, I have this plugin that I’m making that get’s the Services by looking at the Children of the game DataModel. I just came across ConfigService and tried to require it using my plugin. And as it’s not a children of the game DataModel I cannot quick Require it. I cannot check if some other payed plugins like Require Autocomplete can do it as I cannot pay for them. So:

  1. What do you want to achieve?
    Get a list of all Services containing also Services that are not Children of the game DataModel

  2. What solutions have you tried so far?
    Looked up on the Api, Docs, Tried serching in the code of some other free plugins and Messing up with some stuff in Studio.

This is the code I use rn to Get the Services:

local function GetServices()
	
	local Services: {Instance} = {}
	
	for _, Child in game:GetChildren() do
		
		-- Avoid having services with spaces like ["Run Service"] as variable names
		local ServiceName = string.gsub(Child.Name, " ", "")
		
		-- Check if it's actually a service
		local Success, Error = pcall(function()
			local Service = game:GetService(ServiceName)
			
			if Service == nil then
				error("[Autocomplete] " ..ServiceName .." is not a service")
			end
		end)
		
		if Success then
			table.insert(Services, Child)
		end
	end
	
	return table.freeze(Services)
end

Just tried with some other Services and Like also Services like MessagingService and MemoryStoreService that both happen to have the same behaviour as ConfigService. All not being children of the DataModel Initially until created with :GetService(). You can see it by just going to StudioSetting >> Hide Hidden Objects in Explorer or smth like that.

You can use the recently-implemented ReflectionService to inspect the engine and find services regardless of whether they have been created or not.

The following code sample requests class information on all Instance-derived classes and fetches the corresponding service if the class name ends in “Service.” Granted, the success of this method relies on ROBLOX maintaining a naming convention, but it seems like it should work in the majority of circumstances.

local ReflectionService = game:GetService("ReflectionService")

local function GetServices(): {Instance}
	
	local Classes = ReflectionService:GetClasses({
		IsA = "Instance";
		ExcludeDisplay = true;
	})
	
	local Services: {Instance} = {}
	
	for _, Class in Classes do
		
		if string.sub(Class.Name, -7, -1) == "Service" then
			
			Services[#Services + 1] = game:GetService(Class.Name)
		end
	end
	
	return Services
end
1 Like

The problem here would be of Services that don’t end with the Service Like Debris or the ContentProviders. I know there are more and I wandered if there was just a list of all of those.

Ah, yeah, and it appears there are more exceptions than I had thought. There isn’t any special “service-ness” superclass or property that I can really see that would allow you to differentiate a service from a standard Instance, at least not one that is accessible through the engine directly.

I guess your best option at that point (besides hard-coding them) is to just pass in every class name into GetServce through a pcall like you were doing before, although that won’t suppress some internal warnings from being emitted.

local ReflectionService = game:GetService("ReflectionService")

local function GetServices(): {Instance}
	
	local Classes = ReflectionService:GetClasses({
		IsA = "Instance";
		ExcludeDisplay = true;
	})
	
	local Services: {Instance} = {}
	
	for _, Class in Classes do
		
		local Success, Service = pcall(game.GetService, game, Class.Name)
		
		if Success and Service then
			Services[#Services + 1] = Service
		end
	end
	
	return Services
end
1 Like

That’s probably the best method (Beside Hard coding as you said). I really wonder how plugins like Require Autocomplete do it. If anyone could help.

Also I just checked and there are 852 Total classes and basically 99% of those inherite the Instance Class. So this causes quite a lot of lag at start but I think it’s reasonable.

I think you can slightly optimize this by checking the Permits of a ReflectedClass dictionary while also avoiding the pcall:

local function getServices(): {ReflectedClass}
	local list = {}
	for _, class: ReflectedClass in ReflectionService:GetClasses() do
		if not (class.Permits and class.Permits.GetService) then
			continue
		end
		table.insert(list, class)
	end
	return list
end

By my benchmark this looks to be ~15% faster. The returned table also has slightly different contents; My function returns more than 20 services which are not returned in @MysteriousVagabond’s function, however, mine does not include services with spaces in their names: “Run Service”, “Script Context”, “Teleport Service” (it includes the no space versions of these 3 though), and 11 counts of “Instance” (for some reason?).

Hugely optimizing this doesn’t seem like an issue to me, it’s only practical to call it once per studio session/game start.

Basically the data it returns is mostly static and calling it many times seems redundant.

Oh yeah oops. I was thinking GetClasses returned something other than a ReflectedClass. No need for the redundant GetClass call and another slight optimization.

No I mean that the entire call of getServices need to only be called once per game session, it will always return the same results because it is a hugely static and deterministic thing. They just need to cache the response in their code.

I did some testing and There are some Services that you can require with GetService and are indeed Services. Though those are Roblox private and GetService Will return nil. I don’t think I will keep them so the max for now is 139 Service Instances.

For now ended up this way as Storing all ClassNames would be useless anyway as you cannot use those Services.

local function GetServices(): {Instance}
	
	local Services: {Instance} = {}
	
	for _, Class in ReflectionService:GetClasses() do
		
		if not (Class.Permits and Class.Permits.GetService) then
			continue
		end
		
		local Success, Result = pcall(function()
			local Service = game:GetService(Class.Name)
			
			if Service == nil then
				error(`[Autocomplete] {Class.Name} is not a service`)
			end
			
			return Service
		end)
		
		if Success and Result then
			table.insert(Services, Result)
		end
	end
	
	return table.freeze(Services)
end