OnionSuport [MODULE LOADER] – Simplifying Module & Remote Access

!!! BE AWARE: THIS METHOD CAUSES FUNCTION HINTS TO BREAK DUE TO DYNAMICALLY FETCHING MODULE PATHS AT RUNTIME !!!

When you move a module to another folder, you have to change its path everywhere.
How annoying…

If your code looks like this:

local MyModule = require(ServerStorage.MyModulesFolder_1.MyModulesFolder_2.MyModule)

And you find it annoying, as I mentioned before, you can shorten all of it to this:

local MyModule = modules.MyModule

By using a simple ModuleLoader.

All you have to do is create a ModuleScript in ReplicatedStorage called “LoaderService”.

local LoaderService = {}

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local FOLDERS_ORDER = {
	"DataBaseModules",
	"ServicesModules",
	"ControllersServerModules",
}

function LoaderService.LoadModules(ModulesFolder: Folder): { [string]: any }
	local modules = {}
	for _, folderName in ipairs(FOLDERS_ORDER) do
		local folder = ModulesFolder:WaitForChild(folderName)
		for _, module in pairs(folder:GetDescendants()) do
			if module:IsA("ModuleScript") then
				local success, result = pcall(require, module)
				if success then
					modules[module.Name] = result
					print(("[MODULE_LOADER] %s loaded successfully"):format(module.Name))
				else
					warn("Error requiring module:", module.Name, "-", result)
				end
			end
		end
	end
	return modules
end

function LoaderService.LoadRemotes(): ({ [string]: RemoteEvent }, { [string]: RemoteFunction })
	local remoteEvents = {}
	local remoteFunctions = {}
	
	for _, remote in pairs(ReplicatedStorage:GetDescendants()) do
		if remote:IsA("RemoteEvent") then
			remoteEvents[remote.Name] = remote
		elseif remote:IsA("RemoteFunction") then
			remoteFunctions[remote.Name] = remote
		end
	end
	return remoteEvents, remoteFunctions
end

return LoaderService

You need to create 3 folders in ServerStorage called:

  • DataBaseModules – modules that store data for your game, no functions
  • ServicesModules – modules that do the work for controllers
  • ControllersServerModules – modules that control game flow

Then in ServerStorage, create a ModuleScript called “ModuleLoader”. It will store all paths for your game.

local ModuleLoader = {}

ModuleLoader.Modules = {}
ModuleLoader.RemoteEvents = {}
ModuleLoader.RemoteFunctions = {}

return ModuleLoader

In your main script in ServerScriptService, you need to initialize it:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")

-----------------------------
-- [LOADER]
-----------------------------
local LoaderService = require(ReplicatedStorage:WaitForChild("LoaderService"))

local ModulesServer = ServerStorage.ModulesServer
local ModuleLoader = require(ServerStorage.ModuleLoader)

ModuleLoader.RemoteEvents, ModuleLoader.RemoteFunctions = LoaderService.LoadRemotes()
ModuleLoader.Modules = LoaderService.LoadModules(ModulesServer)

-----------------------------
-- [MODULES]
-----------------------------
local Modules = ModuleLoader.Modules

for moduleName, module in pairs(Modules) do
	if type(module) == "table" and module.Init then
		module.Init(Modules)
		print(("[MODULE_INIT] %s initialized"):format(moduleName))
	else
		warn(("[MODULE_INIT] %s does not have an Init function"):format(moduleName))
	end
end
--Here you can use all modules this way
local MyModule_1 = Modules.MyModule_1
local MyModule_2 = Modules.MyModule_2
...
local MyModule_n = Modules.MyModule_n

Now, when you need to use it in a ModuleScript, you can simply do this:

local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local MyModule_1 = {}
-----------------------------
-- [MODULES]
-----------------------------
local MyModule_2
local MyModule_3
local MyModule_4
local MyModule_5

function MyModule_1.Init(modules)
	MyModule_2 = modules.MyModule_2
	MyModule_3 = modules.MyModule_3
	MyModule_4 = modules.MyModule_4
	MyModule_5 = modules.MyModule_5
end

----------------------------------------
-- [REMOTE_EVENTS & REMOTE_FUNCTIONS]
----------------------------------------
local ModuleLoader = require(ServerStorage.ModuleLoader)
local RemoteEvents = ModuleLoader.RemoteEvents
local RemoteFunctions = ModuleLoader.RemoteFunctions

local MyRemote_Event_1 = RemoteEvents.MyRemote_Event_1
local MyRemote_Event_2 = RemoteEvents.MyRemote_Event_2

local MyRemote_Function_1 = RemoteFunctions.MyRemote_Function_1
local MyRemote_Function_2 = RemoteFunctions_MyRemote_Function_2

This method helps me avoid tracking module paths and lets me change them freely.

EDIT_1

It helped me a lot while refactoring the code
It will not be too good in any other scenario :saluting_face:

1 Like